COMPASS  5.4.4
End-to-end AO simulation tool using GPU acceleration
geom_init.py
1 
37 
38 import shesha.config as conf
39 import shesha.constants as scons
40 from shesha.constants import CONST
41 
42 import shesha.util.make_pupil as mkP
43 import shesha.util.utilities as util
44 from shesha.sutra_wrap import carmaWrap_context, Telescope
45 from shesha.constants import ApertureType
46 import numpy as np
47 
48 
49 def tel_init(context: carmaWrap_context, p_geom: conf.Param_geom, p_tel: conf.Param_tel,
50  r0=None, ittime=None, p_wfss=None, dm=None):
51  """
52  Initialize the overall geometry of the AO system, including pupil and WFS
53 
54  Args:
55  context: (carmaWrap_context) : context
56 
57  p_geom: (Param_geom) : geom settings
58 
59  p_tel: (Param_tel) : telescope settings
60 
61  r0: (float) : atmos r0 @ 0.5 microns
62 
63  ittime: (float) : 1/loop frequency [s]
64 
65  p_wfss: (list of Param_wfs) : wfs settings
66 
67  Kwargs:
68  dm: (list of Param_dm) : dms settings [=None]
69 
70  :return:
71  telescope: (Telescope): Telescope object
72 
73  """
74  if p_wfss is not None:
75  # WFS geometry
76  nsensors = len(p_wfss)
77 
78  any_sh = [o.type for o in p_wfss].count(scons.WFSType.SH) > 0
79  # dm = None
80  if (p_wfss[0].dms_seen is None and dm is not None):
81  for i in range(nsensors):
82  if (not p_wfss[i].open_loop):
83  p_wfss[i].set_dms_seen(np.arange(len(dm), dtype=np.int32))
84 
85  # first get the wfs with max # of subaps
86  # we'll derive the geometry from the requirements in terms of sampling
87  if (any_sh):
88  indmax = np.argsort([o.nxsub for o in p_wfss
89  if o.type == scons.WFSType.SH])[-1]
90  else:
91  indmax = np.argsort([o.nxsub for o in p_wfss])[-1]
92 
93  print("*-----------------------")
94  print("Computing geometry of WFS", indmax)
95 
96  init_wfs_geom(p_wfss[indmax], r0, p_tel, p_geom, ittime, verbose=1)
97  # #do the same for other wfs
98  for i in range(nsensors):
99  if (i != indmax):
100  print("*-----------------------")
101  print("Computing geometry of WFS", i)
102  init_wfs_geom(p_wfss[i], r0, p_tel, p_geom, ittime, verbose=1)
103 
104  else:
105  geom_init(p_geom, p_tel)
106 
107  telescope = Telescope(context, p_geom._spupil.shape[0],
108  np.where(p_geom._spupil > 0)[0].size,
109  (p_geom._spupil * p_geom._apodizer).astype(np.float32),
110  p_geom._mpupil.shape[0], p_geom._mpupil)
111 
112  if (p_geom._phase_ab_M1 is not None):
113  telescope.set_phase_ab_M1(p_geom._phase_ab_M1)
114  telescope.set_phase_ab_M1_m(p_geom._phase_ab_M1_m)
115 
116  return telescope
117 
118 
119 def init_wfs_geom(p_wfs: conf.Param_wfs, r0: float, p_tel: conf.Param_tel,
120  p_geom: conf.Param_geom, ittime: float, verbose=1):
121  """Compute the geometry of WFSs: valid subaps, positions of the subaps,
122  flux per subap, etc...
123 
124  Args:
125  p_wfs: (Param_wfs) : wfs settings
126 
127  r0: (float) : atmos r0 @ 0.5 microns
128 
129  p_tel: (Param_tel) : telescope settings
130 
131  geom: (Param_geom) : geom settings
132 
133  ittime: (float) : 1/loop frequency [s]
134 
135  verbose: (int) : (optional) display informations if 0
136 
137 
138  """
139 
140  if p_geom.pupdiam:
141  if p_wfs.type == scons.WFSType.SH or p_wfs.type == scons.WFSType.PYRHR or p_wfs.type == scons.WFSType.PYRLR:
142  pdiam = p_geom.pupdiam // p_wfs.nxsub
143  if ((pdiam * p_wfs.nxsub) % 2):
144  pdiam += 1
145  else:
146  pdiam = -1
147 
148  p_wfs._pdiam = pdiam
149 
150  init_wfs_size(p_wfs, r0, p_tel, verbose)
151 
152  if not p_geom.is_init:
153  # this is the wfs with largest # of subaps
154  # the overall geometry is deduced from it
155  #if not p_geom.pupdiam:
156  if p_geom.pupdiam != 0 and p_geom.pupdiam != p_wfs._pdiam * p_wfs.nxsub:
157  print("WARNING: pupdiam set value not correct")
158  p_geom.pupdiam = p_wfs._pdiam * p_wfs.nxsub
159  print("pupdiam used: ", p_geom.pupdiam)
160  if p_wfs.type == scons.WFSType.PYRHR or p_wfs.type == scons.WFSType.PYRLR:
161  geom_init(p_geom, p_tel, padding=p_wfs._nrebin)
162  elif (p_wfs.type == scons.WFSType.SH):
163  geom_init(p_geom, p_tel)
164  else:
165  raise RuntimeError("This WFS can not be used")
166 
167  if (p_wfs.type == scons.WFSType.PYRHR or p_wfs.type == scons.WFSType.PYRLR):
168  init_pyrhr_geom(p_wfs, r0, p_tel, p_geom, ittime, verbose=1)
169  elif (p_wfs.type == scons.WFSType.SH):
170  init_sh_geom(p_wfs, r0, p_tel, p_geom, ittime, verbose=1)
171  else:
172  raise RuntimeError("This WFS can not be used")
173 
174 
175 def init_wfs_size(p_wfs: conf.Param_wfs, r0: float, p_tel: conf.Param_tel, verbose=1):
176  """Compute all the parameters usefull for further WFS image computation (array sizes)
177 
178  Args:
179  p_wfs: (Param_wfs) : wfs settings
180 
181  r0: (float) : atmos r0 @ 0.5 microns
182 
183  p_tel: (Param_tel) : telescope settings
184 
185  verbose: (int) : (optional) display informations if 0
186 
187  Scheme to determine arrays sizes
188  sh :
189  k = 6
190  p = k * d/r0 # size of seeing blob
191  n = int(2*d*v/lambda/CONST.RAD2ARCSEC)+1
192  N = fft_goodsize(k*n/v*lambda/r0*CONST.RAD2ARCSEC)
193  u = k * lambda / r0 * CONST.RAD2ARCSEC / N
194  n = v/u - int(v/u) > 0.5 ? int(v/u)+1 : int(v/u)
195  v = n * u
196  Nt = v * Npix
197 
198  PYRAMID CASE :
199  Fs = field stop radius in arcsec
200  N size of big array for FFT in pixels
201  P pupil diameter in pixels
202  D diameter of telescope in m
203  Nssp : number of pyr measurement points in the pupil
204 
205  # Rf is the radius of field stop in pixels
206  Rf = Fs . N . D / lambda / P
207  ideally we choose : Fs = lambda / D . Nssp / 2
208 
209  If we want a good sampling of r0 (to avoid aliasing of speckles that may
210  roll over the FoV), we have to specify a FoV of the FFT support that is
211  larger than seeing by a factor <m> at least equal to 2 (at a very minimum)
212  or 3 (for a better comfort..). This writes as:
213  P > D / r0 . m
214  with m = 2 or 3. This condition is equivalent to put <m> pixels per r0.
215 
216  To get reasonable space between pupil images : N > P.(2 + 3S)
217  with S close to 1
218  N must be a power of 2
219 
220  to ease computation lets assume that Nssp is a divider of P
221  scaling factor between high res pupil images in pyramid model
222  and actual pupil size on camera images would be P / Nssp
223 
224  """
225  r0 = r0 * (p_wfs.Lambda * 2)**(6. / 5)
226 
227  if (r0 != 0):
228  if (verbose):
229  print("r0 for WFS :", "%4.3f" % r0, " m")
230  # seeing = CONST.RAD2ARCSEC * (p_wfs.lambda * 1.e-6) / r0
231  if (verbose):
232  print("seeing for WFS : ",
233  "%4.3f" % (CONST.RAD2ARCSEC * (p_wfs.Lambda * 1.e-6) / r0), "\"")
234 
235  if (p_wfs._pdiam <= 0):
236  # this case is usualy for the wfs with max # of subaps
237  # we look for the best compromise between pixsize and fov
238  if (p_wfs.type == scons.WFSType.SH):
239 
240  subapdiam = p_tel.diam / float(p_wfs.nxsub) # diam of subap
241  k = 6
242  pdiam = int(k * subapdiam / r0) # number of phase points per subap
243  if (pdiam < 16):
244  pdiam = 16
245 
246  # Must be even to keep ssp and actuators grids aligned in the pupil
247  if ((pdiam * p_wfs.nxsub) % 2):
248  pdiam += 1
249 
250  nrebin = int(2 * subapdiam * p_wfs.pixsize /
251  (p_wfs.Lambda * 1.e-6) / CONST.RAD2ARCSEC) + 1
252  nrebin = max(2, nrebin)
253  # first atempt on a rebin factor
254 
255  # since we clipped pdiam we have to be careful in nfft computation
256  Nfft = util.fft_goodsize(
257  int(pdiam / subapdiam * nrebin / p_wfs.pixsize * CONST.RAD2ARCSEC *
258  (p_wfs.Lambda * 1.e-6)))
259 
260  elif (p_wfs.type == scons.WFSType.PYRHR or p_wfs.type == scons.WFSType.PYRLR):
261  # while (pdiam % p_wfs.npix != 0) pdiam+=1;
262  k = 3 if p_wfs.type == scons.WFSType.PYRHR else 2
263  pdiam = int(p_tel.diam / r0 * k)
264  while (pdiam % p_wfs.nxsub != 0):
265  pdiam += 1 # we choose to have a multiple of p_wfs.nxsub
266  pdiam = pdiam // p_wfs.nxsub
267  if (pdiam < 8):
268  pdiam = 8
269 
270  # quantum pixel size
271  else:
272  pdiam = p_wfs._pdiam
273  # this case is for a wfs with fixed # of phase points
274  Nfft = util.fft_goodsize(2 * pdiam)
275  # size of the support in fourier domain
276 
277  # qpixsize = pdiam * \
278  # (p_wfs.Lambda * 1.e-6) / subapdiam * CONST.RAD2ARCSEC / Nfft
279  # # quantum pixel size
280 
281  if (p_wfs.type == scons.WFSType.SH):
282  subapdiam = p_tel.diam / float(p_wfs.nxsub) # diam of subap
283 
284  # size of the support in fourier domain
285 
286  # qpixsize = k * (p_wfs.Lambda*1.e-6) / r0 * CONST.RAD2ARCSEC / Nfft
287  qpixsize = (pdiam * (p_wfs.Lambda * 1.e-6) / subapdiam * CONST.RAD2ARCSEC) / Nfft
288 
289  # # actual rebin factor
290  if (p_wfs.pixsize / qpixsize - int(p_wfs.pixsize / qpixsize) > 0.5):
291  nrebin = int(p_wfs.pixsize / qpixsize) + 1
292  else:
293  nrebin = int(p_wfs.pixsize / qpixsize)
294 
295  # actual pixel size
296  pixsize = nrebin * qpixsize
297 
298  if (pixsize * p_wfs.npix > qpixsize * Nfft):
299  Ntot = util.fft_goodsize(int(pixsize * p_wfs.npix / qpixsize) + 1)
300  else:
301  Ntot = Nfft
302 
303  if (Ntot % 2 != Nfft % 2):
304  Ntot += 1
305 
306  elif (p_wfs.type == scons.WFSType.PYRHR or p_wfs.type == scons.WFSType.PYRLR):
307  pdiam = pdiam * p_wfs.nxsub
308  m = 3
309  # Nfft = util.fft_goodsize( m * pdiam)
310  Nfft = int(2**np.ceil(np.log2(m * pdiam)))
311 
312  nrebin = pdiam // p_wfs.nxsub
313  while (Nfft % nrebin != 0):
314  nrebin += 1 # we choose to have a divisor of Nfft
315  pdiam = nrebin * p_wfs.nxsub
316  Nfft = int(2**np.ceil(np.log2(m * pdiam)))
317  # Nfft = util.fft_goodsize( m * pdiam)
318 
319  qpixsize = (pdiam *
320  (p_wfs.Lambda * 1.e-6) / p_tel.diam * CONST.RAD2ARCSEC) / Nfft
321 
322  Ntot = Nfft // pdiam * p_wfs.nxsub
323  pixsize = qpixsize * nrebin
324  pdiam = pdiam // p_wfs.nxsub
325 
326  p_wfs._pdiam = pdiam
327  p_wfs.pixsize = pixsize
328  p_wfs._qpixsize = qpixsize
329  p_wfs._Nfft = Nfft
330  p_wfs._Ntot = Ntot
331  p_wfs._nrebin = nrebin
332  p_wfs._subapd = p_tel.diam / p_wfs.nxsub
333 
334  if (verbose):
335  if (p_wfs.type == scons.WFSType.SH):
336  print("quantum pixsize : ", "%5.4f" % qpixsize, "\"")
337  print("simulated FoV : ", "%3.2f" % (Ntot * qpixsize), "\" x ",
338  "%3.2f" % (Ntot * qpixsize), "\"")
339  print("actual pixsize : ", "%5.4f" % pixsize)
340  print("actual FoV : ", "%3.2f" % (pixsize * p_wfs.npix), "\" x ",
341  "%3.2f" % (pixsize * p_wfs.npix), "\"")
342  print("number of phase points : ", p_wfs._pdiam)
343  print("size of fft support : ", Nfft)
344  print("size of HR spot support : ", Ntot)
345 
346  elif (p_wfs.type == scons.WFSType.PYRHR or p_wfs.type == scons.WFSType.PYRLR):
347  print("quantum pixsize in pyr image : ", "%5.4f" % qpixsize, "\"")
348  print("simulated FoV : ", "%3.2f" % (Nfft * qpixsize), "\" x ",
349  "%3.2f" % (Nfft * qpixsize), "\"")
350  print("number of phase points : ", p_wfs._pdiam * p_wfs.nxsub)
351  print("size of fft support : ", Nfft)
352 
353 
354 def compute_nphotons(wfs_type, ittime, optthroughput, diam, cobs=0, nxsub=0, zerop=0,
355  gsmag=0, lgsreturnperwatt=0, laserpower=0, verbose=1):
356  ''' Determines the number of photons TBC
357 
358  Args:
359  wfs_type: (scons.WFSType) : wfs type: SH or PYRHR.
360 
361  ittime: (float) : 1/loop frequency [s].
362 
363  optthroughput: (float) : wfs global throughput.
364 
365  diam: (float) : telescope diameter.
366 
367  cobs: (float) : (optional for SH) telescope central obstruction.
368 
369  nxsub: (int) : (optional for PYRHR) linear number of subaps.
370 
371  zerop: (float) : (optional for LGS) detector zero point expressed in ph/m**2/s in the bandwidth of the WFS.
372 
373  gsmag: (float) : (optional for LGS) magnitude of guide star.
374 
375  lgsreturnperwatt: (float) : (optional for NGS) return per watt factor (high season : 10 ph/cm2/s/W).
376 
377  laserpower: (float) : (optional for NGS) laser power in W.
378 
379  verbose: (bool) : (optional) display informations if True.
380 
381  for PYRHR WFS: nphotons = compute_nphotons(scons.WFSType.PYRHR, ittime,
382  optthroughput, diam, cobs=?, zerop=?, gsmag=?)
383  for NGS SH WFS: nphotons = compute_nphotons(scons.WFSType.SH, ittime,
384  optthroughput, diam, nxsub=?, zerop=?, gsmag=?)
385  for LGS SH WFS: nphotons = compute_nphotons(scons.WFSType.SH, ittime,
386  optthroughput, diam, nxsub=?,
387  lgsreturnperwatt=?, laserpower=?)
388  '''
389  surface = 0
390  nphotons = 0
391  if (wfs_type == scons.WFSType.PYRHR or wfs_type == scons.WFSType.PYRLR):
392  surface = np.pi / 4. * (1 - cobs**2.) * diam**2.
393  elif (wfs_type == scons.WFSType.SH):
394  # from the guide star
395  if (laserpower == 0):
396  if (zerop == 0):
397  zerop = 1e11
398  surface = (diam / nxsub)**2.
399  # include throughput to WFS for unobstructed
400  # subaperture per iteration
401  else: # we are dealing with a LGS
402  nphotons = lgsreturnperwatt * laserpower * \
403  optthroughput * (diam / nxsub) ** 2. * 1e4 * ittime
404  # detected by WFS
405  # ... for given power include throughput to WFS
406  # for unobstructed subaperture per iteration
407  if (verbose):
408  print("nphotons : ", nphotons)
409  return nphotons
410  else:
411  raise RuntimeError("WFS unknown")
412 
413  nphotons = zerop * 10. ** (-0.4 * gsmag) * ittime * \
414  optthroughput * surface
415 
416  if (verbose):
417  print("nphotons : ", nphotons)
418  return nphotons
419 
420 
421 def init_pyrhr_geom(p_wfs: conf.Param_wfs, r0: float, p_tel: conf.Param_tel,
422  p_geom: conf.Param_geom, ittime: float, verbose: bool = True):
423  """Compute the geometry of PYRHR WFSs: valid subaps, positions of the subaps,
424  flux per subap, etc...
425 
426  Args:
427  p_wfs: (Param_wfs) : wfs settings
428 
429  r0: (float) : atmos r0 @ 0.5 microns
430 
431  p_tel: (Param_tel) : telescope settings
432 
433  geom: (Param_geom) : geom settings
434 
435  ittime: (float) : 1/loop frequency [s]
436 
437  verbose: (bool) : (optional) display informations if True
438 
439  """
440 
441  p_wfs.npix = p_wfs._pdiam
442  p_wfs._Ntot = p_geom._n
443 
444  # Creating field stop mask
445  fsradius_pixels = int(p_wfs.fssize / p_wfs._qpixsize / 2.)
446  if (p_wfs.fstop == scons.FieldStopType.ROUND):
447  focmask = util.dist(p_wfs._Nfft, xc=p_wfs._Nfft / 2. - 0.5,
448  yc=p_wfs._Nfft / 2. - 0.5) < (fsradius_pixels)
449  elif (p_wfs.fstop == scons.FieldStopType.SQUARE):
450  X = np.indices((p_wfs._Nfft, p_wfs._Nfft)) + 1 # TODO: +1 ??
451  x = X[1] - (p_wfs._Nfft + 1.) / 2.
452  y = X[0] - (p_wfs._Nfft + 1.) / 2.
453  focmask = (np.abs(x) <= (fsradius_pixels)) * \
454  (np.abs(y) <= (fsradius_pixels))
455  else:
456  msg = "PYRHR wfs fstop must be FieldStopType.[ROUND|SQUARE]"
457  raise ValueError(msg)
458 
459  pyr_focmask = focmask * 1.0 # np.fft.fftshift(focmask*1.0)
460  p_wfs._submask = np.fft.fftshift(pyr_focmask)
461 
462  # Creating pyramid mask
463  pyrsize = p_wfs._Nfft
464  cobs = p_tel.cobs
465  rpup = p_geom.pupdiam / 2.0
466  dpup = p_geom.pupdiam
467  nrebin = p_wfs._nrebin
468  fracsub = p_wfs.fracsub
469  if p_wfs.pyr_pup_sep == -1:
470  pup_sep = int(p_wfs.nxsub)
471  else:
472  pup_sep = p_wfs.pyr_pup_sep
473  # number of pix between two centers two pupil images
474 
475  y = np.tile(np.arange(pyrsize) - pyrsize / 2, (pyrsize, 1))
476  x = y.T
477 
478  Pangle = pup_sep * nrebin # Pyramid angle in HR pixels
479  if p_wfs.nPupils == 0:
480  p_wfs.nPupils = 4
481  # Centers is a nPupils x 2 array describing the position of the quadrant centers
482  centers = Pangle / np.sin(
483  np.pi / p_wfs.nPupils) * np.c_[np.cos(
484  (2 * np.arange(p_wfs.nPupils) + 1) * np.pi / p_wfs.nPupils),
485  np.sin((2 * np.arange(p_wfs.nPupils) + 1) *
486  np.pi / p_wfs.nPupils)]
487  # In case nPupils == 4, we put the centers back in the normal A-B-C-D ordering scheme, for misalignment processing.
488  if p_wfs.nPupils == 4:
489  centers = np.round(centers[[1, 0, 2, 3], :]).astype(np.int32)
490  # Add in the misalignments of quadrant centers, in LR px units
491  if p_wfs.pyr_misalignments is not None:
492  mis = np.asarray(p_wfs.pyr_misalignments) * nrebin
493  else:
494  mis = np.zeros((p_wfs.nPupils, 2), dtype=np.float32)
495  # Pyramid as minimal intersect of 4 tilting planes
496  pyr = 2 * np.pi / pyrsize * np.min(
497  np.asarray([(c[0] + m[0]) * x + (c[1] + m[1]) * y
498  for c, m in zip(centers, mis)]), axis=0)
499  p_wfs._halfxy = np.fft.fftshift(pyr.T)
500 
501  # Valid pixels identification
502  # Generate buffer with pupil at center
503  pup = np.zeros((pyrsize, pyrsize))#, dtype=np.int32)
504  pup[pyrsize // 2 - p_geom._n // 2:pyrsize // 2 + p_geom._n // 2,
505  pyrsize // 2 - p_geom._n // 2:pyrsize // 2 + p_geom._n // 2] = \
506  p_geom._mpupil
507 
508  # Shift the mask to obtain the geometrically valid pixels
509  for qIdx in range(p_wfs.nPupils):
510  quadOnCenter = np.roll(pup, tuple((centers[qIdx] + mis[qIdx]).astype(np.int32)),
511  (0, 1))
512  mskRebin = util.rebin(quadOnCenter.copy(),
513  [pyrsize // nrebin, pyrsize // nrebin])
514  if qIdx == 0:
515  stackedSubap = np.roll(mskRebin >= fracsub,
516  tuple((-centers[qIdx] / nrebin).astype(np.int32)),
517  (0, 1))
518  mskRebTot = (mskRebin >= fracsub).astype(np.int32)
519  else:
520  stackedSubap += np.roll(mskRebin >= fracsub,
521  tuple((-centers[qIdx] / nrebin).astype(np.int32)),
522  (0, 1))
523  mskRebTot += (mskRebin >= fracsub).astype(np.int32) * (qIdx + 1)
524 
525  validRow, validCol = [], []
526  # If n == 4, we need to tweak -again- the order to feed
527  # compass XY controllers in the appropriate order
528  if p_wfs.nPupils == 4:
529  centers = centers[[2, 1, 3, 0], :]
530  # mis = mis[[2, 1, 3, 0], :] # We don't need mis anymore - but if so keep the order of centers
531  for qIdx in range(p_wfs.nPupils):
532  tmpWh = np.where(
533  np.roll(stackedSubap, tuple((centers[qIdx] / nrebin).astype(np.int32)),
534  (0, 1)))
535  validRow += [tmpWh[0].astype(np.int32)]
536  validCol += [tmpWh[1].astype(np.int32)]
537  nvalid = validRow[0].size
538  validRow = np.asarray(validRow).flatten()
539  validCol = np.asarray(validCol).flatten()
540  stack, index = np.unique(np.c_[validRow, validCol], axis=0, return_index=True)
541 
542  p_wfs._nvalid = nvalid
543  p_wfs._validsubsx = validRow[np.sort(index)]
544  p_wfs._validsubsy = validCol[np.sort(index)]
545  p_wfs._hrmap = mskRebTot.astype(np.int32)
546 
547  if (p_wfs.pyr_pos is None):
548  pixsize = (np.pi * p_wfs._qpixsize) / (3600 * 180)
549  # scale_fact = 2 * np.pi / npup * \
550  # (p_wfs.Lambda / p_tel.diam / 4.848) / pixsize * p_wfs.pyr_ampl
551  # Proposition de Flo
552  scale_fact = 2 * np.pi / pyrsize * \
553  (p_wfs.Lambda * 1e-6 / p_tel.diam) / pixsize * p_wfs.pyr_ampl
554  cx = scale_fact * \
555  np.sin((np.arange(p_wfs.pyr_npts)) * 2. * np.pi / p_wfs.pyr_npts)
556  cy = scale_fact * \
557  np.cos((np.arange(p_wfs.pyr_npts)) * 2. * np.pi / p_wfs.pyr_npts)
558  p_wfs.set_pyr_scale_pos(scale_fact)
559  # mod_npts = p_wfs.pyr_npts #UNUSED
560  else:
561  if (verbose):
562  print("Using user-defined positions [arcsec] for the pyramid modulation")
563  cx = p_wfs.pyr_pos[:, 0] / p_wfs._qpixsize
564  cy = p_wfs.pyr_pos[:, 1] / p_wfs._qpixsize
565  # mod_npts=cx.shape[0] #UNUSED
566 
567  p_wfs._pyr_cx = cx.copy()
568  p_wfs._pyr_cy = cy.copy()
569 
570  # telSurf = np.pi / 4. * (1 - p_tel.cobs**2.) * p_tel.diam**2.
571  # p_wfs._nphotons = p_wfs.zerop * \
572  # 10. ** (-0.4 * p_wfs.gsmag) * ittime * \
573  # p_wfs.optthroughput * telSurf
574  p_wfs._nphotons = compute_nphotons(scons.WFSType.PYRHR, ittime, p_wfs.optthroughput,
575  p_tel.diam, cobs=p_tel.cobs, zerop=p_wfs.zerop,
576  gsmag=p_wfs.gsmag, verbose=verbose)
577 
578  # spatial filtering by the pixel extent:
579  # *2/2 intended. min should be 0.40 = sinc(0.5)^2.
580  y = np.tile(np.arange(pyrsize) - pyrsize // 2, (pyrsize, 1))
581  x = y.T
582  x = x * 1. / pyrsize
583  y = y * 1. / pyrsize
584  sincar = np.fft.fftshift(np.sinc(x) * np.sinc(y))
585 
586  # sincar = np.roll(np.pi*x*np.pi*y,x.shape[1],axis=1)
587  p_wfs._sincar = sincar.astype(np.float32)
588 
589  #pup = p_geom._mpupil
590  a = pyrsize // nrebin
591  b = p_geom._n // nrebin
592  pupvalid = stackedSubap[a // 2 - b // 2:a // 2 + b // 2, a // 2 - b // 2:a // 2 +
593  b // 2]
594  p_wfs._isvalid = pupvalid.T.astype(np.int32)
595 
596  validsubsx = np.where(pupvalid)[0].astype(np.int32)
597  validsubsy = np.where(pupvalid)[1].astype(np.int32)
598 
599  istart = np.arange(p_wfs.nxsub + 2) * p_wfs.npix
600  jstart = np.copy(istart)
601 
602  # sorting out valid subaps
603  fluxPerSub = np.zeros((p_wfs.nxsub + 2, p_wfs.nxsub + 2), dtype=np.float32)
604 
605  for i in range(p_wfs.nxsub + 2):
606  indi = istart[i]
607  for j in range(p_wfs.nxsub + 2):
608  indj = jstart[j]
609  fluxPerSub[i, j] = np.sum(
610  p_geom._mpupil[indi:indi + p_wfs.npix, indj:indj + p_wfs.npix])
611 
612  fluxPerSub = fluxPerSub / p_wfs.nxsub**2.
613 
614  p_wfs._fluxPerSub = fluxPerSub.copy()
615 
616  phasemap = np.zeros((p_wfs.npix * p_wfs.npix, p_wfs._nvalid), dtype=np.int32)
617  X = np.indices((p_geom._n, p_geom._n)) # we need c-like indice
618  tmp = X[1] + X[0] * p_geom._n
619 
620  pyrtmp = np.zeros((p_geom._n, p_geom._n), dtype=np.int32)
621 
622  ttprojmat = np.zeros([p_wfs.npix * p_wfs.npix,p_wfs._nvalid*2], dtype=np.float32)
623  for i in range(len(validsubsx)):
624  indi = istart[validsubsy[i]] # +2-1 (yorick->python
625  indj = jstart[validsubsx[i]]
626  phasemap[:, i] = tmp[indi:indi + p_wfs.npix, indj:indj + p_wfs.npix].flatten("C")
627  pyrtmp[indi:indi + p_wfs.npix, indj:indj + p_wfs.npix] = i
628 
629  Sx = np.zeros([p_wfs.npix,p_wfs.npix],dtype=np.float32)
630  Sy = np.zeros([p_wfs.npix,p_wfs.npix],dtype=np.float32)
631 
632  subapmask = p_geom._mpupil.T[indi:indi + p_wfs._pdiam,
633  indj:indj + p_wfs._pdiam]
634  for ii in range(p_wfs.npix):
635  for jj in range(p_wfs.npix):
636  Sx[ii,jj] += subapmask[ii,jj-1] if jj>0 else 0
637  Sx[ii,jj] -= subapmask[ii,jj+1] if jj+1 < p_wfs.npix else 0
638  Sx[ii,jj] *= subapmask[ii,jj]
639  Sy[ii,jj] += subapmask[ii-1,jj] if ii>0 else 0
640  Sy[ii,jj] -= subapmask[ii+1,jj] if ii+1 < p_wfs.npix else 0
641  Sy[ii,jj] *= subapmask[ii,jj]
642  Sx_den = -Sx.cumsum(axis=1).sum()
643  Sy_den = -Sy.cumsum(axis=0).sum()
644  if Sx_den == 0 or Sy_den == 0:
645  continue
646  Sx /= Sx_den
647  Sy /= Sy_den
648  ttprojmat[:,i] = Sx.flatten()
649  ttprojmat[:,i+p_wfs._nvalid] = Sy.flatten()
650 
651  p_wfs._phasemap = phasemap
652  p_wfs._ttprojmat = ttprojmat * p_geom.get_pupdiam() / p_tel.get_diam() \
653  * CONST.RAD2ARCSEC * 1e-6
654 
655  p_wfs._pyr_offsets = pyrtmp # pshift
656 
657 
658 def init_sh_geom(p_wfs: conf.Param_wfs, r0: float, p_tel: conf.Param_tel,
659  p_geom: conf.Param_geom, ittime: float, verbose: bool = True):
660  """Compute the geometry of SH WFSs: valid subaps, positions of the subaps,
661  flux per subap, etc...
662 
663  Args:
664  p_wfs: (Param_wfs) : wfs settings
665 
666  r0: (float) : atmos r0 @ 0.5 microns
667 
668  p_tel: (Param_tel) : telescope settings
669 
670  geom: (Param_geom) : geom settings
671 
672  ittime: (float) : 1/loop frequency [s]
673 
674  verbose: (bool) : (optional) display informations if True
675 
676  """
677  p_wfs.nPupils = 1
678  # this is the i,j index of lower left pixel of subap in _spupil
679  istart = ((np.linspace(0, p_geom.pupdiam, p_wfs.nxsub + 1))[:-1]).astype(np.int64)
680  # Translation in _mupil useful for raytracing
681  istart += 2
682  jstart = np.copy(istart)
683 
684  # sorting out valid subaps
685  fluxPerSub = np.zeros((p_wfs.nxsub, p_wfs.nxsub), dtype=np.float32)
686 
687  for i in range(p_wfs.nxsub):
688  indi = istart[i] # +2-1 (yorick->python)
689  for j in range(p_wfs.nxsub):
690  indj = jstart[j] # +2-1 (yorick->python)
691  fluxPerSub[i, j] = np.sum(
692  p_geom._mpupil[indi:indi + p_wfs._pdiam, indj:indj + p_wfs._pdiam])
693  # fluxPerSub[i,j] = np.where(p_geom._mpupil[indi:indi+pdiam,indj:indj+pdiam] > 0)[0].size
694 
695  fluxPerSub = fluxPerSub / p_wfs._pdiam**2.
696 
697  pupvalid = (fluxPerSub >= p_wfs.fracsub) * 1
698  p_wfs._isvalid = pupvalid.astype(np.int32)
699  p_wfs._nvalid = int(np.sum(p_wfs._isvalid))
700  p_wfs._fluxPerSub = fluxPerSub.copy()
701  validx = np.where(p_wfs._isvalid.T)[1].astype(np.int32)
702  validy = np.where(p_wfs._isvalid.T)[0].astype(np.int32)
703  p_wfs._validsubsx = validx
704  p_wfs._validsubsy = validy
705  p_wfs._validpuppixx = validx * p_wfs._pdiam + 2
706  p_wfs._validpuppixy = validy * p_wfs._pdiam + 2
707 
708  # this defines how we cut the phase into subaps
709  phasemap = np.zeros((p_wfs._pdiam * p_wfs._pdiam, p_wfs._nvalid), dtype=np.int32)
710 
711  X = np.indices((p_geom._n, p_geom._n))
712  tmp = X[1] + X[0] * p_geom._n
713 
714  n = p_wfs._nvalid
715  # for i in range(p_wfs._nvalid):
716  ttprojmat = np.zeros([p_wfs._pdiam**2,p_wfs._nvalid*2], dtype=np.float32)
717  for i in range(n):
718  indi = istart[p_wfs._validsubsy[i]] # +2-1 (yorick->python)
719  indj = jstart[p_wfs._validsubsx[i]]
720  phasemap[:, i] = tmp[indi:indi + p_wfs._pdiam, indj:indj +
721  p_wfs._pdiam].flatten()
722 
723  Sx = np.zeros([p_wfs._pdiam,p_wfs._pdiam],dtype=np.float32)
724  Sy = np.zeros([p_wfs._pdiam,p_wfs._pdiam],dtype=np.float32)
725 
726  subapmask = p_geom._mpupil.T[indi:indi + p_wfs._pdiam,
727  indj:indj + p_wfs._pdiam]
728  for ii in range(p_wfs._pdiam):
729  for jj in range(p_wfs._pdiam):
730  Sx[ii,jj] += subapmask[ii,jj-1] if jj>0 else 0
731  Sx[ii,jj] -= subapmask[ii,jj+1] if jj+1 < p_wfs._pdiam else 0
732  Sx[ii,jj] *= subapmask[ii,jj]
733  Sy[ii,jj] += subapmask[ii-1,jj] if ii>0 else 0
734  Sy[ii,jj] -= subapmask[ii+1,jj] if ii+1 < p_wfs._pdiam else 0
735  Sy[ii,jj] *= subapmask[ii,jj]
736  Sx_den = -Sx.cumsum(axis=1).sum()
737  Sy_den = -Sy.cumsum(axis=0).sum()
738  if Sx_den == 0 or Sy_den == 0:
739  continue
740  Sx /= Sx_den
741  Sy /= Sy_den
742  ttprojmat[:,i] = Sx.flatten()
743  ttprojmat[:,i+p_wfs._nvalid] = Sy.flatten()
744 
745  p_wfs._phasemap = phasemap
746  p_wfs._validsubsx *= p_wfs.npix
747  p_wfs._validsubsy *= p_wfs.npix
748  p_wfs._ttprojmat = ttprojmat * p_geom.get_pupdiam() / p_tel.get_diam() \
749  * CONST.RAD2ARCSEC * 1e-6
750 
751  # this is a phase shift of 1/2 pix in x and y
752  halfxy = np.linspace(0, 2 * np.pi, p_wfs._Nfft + 1)[0:p_wfs._pdiam] / 2.
753  halfxy = np.tile(halfxy, (p_wfs._pdiam, 1))
754  halfxy += halfxy.T
755  # p_wfs._halfxy = <float*>(halfxy*0.).data #dont work: half*0 is temp
756  # python obj
757 
758  if (p_wfs.npix % 2 == 1 and p_wfs._nrebin % 2 == 1):
759  # p_wfs._halfxy = <float*>(halfxy*0.)
760  halfxy = np.zeros((p_wfs._pdiam, p_wfs._pdiam), dtype=np.float32)
761  p_wfs._halfxy = halfxy.astype(np.float32)
762  else:
763  p_wfs._halfxy = halfxy.astype(np.float32)
764 
765  # this defines how we create a larger fov if required
766  if (p_wfs._Ntot != p_wfs._Nfft):
767  indi = int((p_wfs._Ntot - p_wfs._Nfft) / 2.) # +1 -1 (yorick>python)
768  indj = int(indi + p_wfs._Nfft)
769  X = np.indices((p_wfs._Nfft, p_wfs._Nfft)) + 1 # TODO: +1 ??
770  x = X[1]
771  y = X[0]
772  # hrpix
773  tmp = np.zeros((p_wfs._Ntot, p_wfs._Ntot))
774  tmp[indi:indj, indi:indj] = np.roll(x + (y - 1) * p_wfs._Nfft, p_wfs._Nfft // 2,
775  axis=0)
776  tmp[indi:indj, indi:indj] = np.roll(tmp[indi:indj, indi:indj], p_wfs._Nfft // 2,
777  axis=1)
778  # hrmap=roll(hrpix)
779  tmp = np.roll(tmp, p_wfs._Ntot // 2, axis=0)
780  tmp = np.roll(tmp, p_wfs._Ntot // 2, axis=1)
781 
782  tmp = np.where(tmp.flatten())[0]
783 
784  p_wfs._hrmap = np.copy(tmp.astype(np.int32))
785  p_wfs._hrmap = np.reshape(p_wfs._hrmap, (p_wfs._hrmap.shape[0], 1))
786  # must be set even if unused
787 
788  else:
789  tmp = np.zeros((2, 2))
790  p_wfs._hrmap = np.copy(tmp.astype(np.int32))
791  # must be set even if unused
792 
793  if (p_wfs._nrebin * p_wfs.npix % 2 != p_wfs._Ntot % 2):
794  # +2-1 yorick>python
795  indi = int((p_wfs._Ntot - p_wfs._nrebin * p_wfs.npix) / 2.) + 1
796  else:
797  indi = int((p_wfs._Ntot - p_wfs._nrebin * p_wfs.npix) / 2.) + 0
798  indj = int(indi + p_wfs._nrebin * p_wfs.npix)
799 
800  X = np.indices((p_wfs._nrebin * p_wfs.npix, p_wfs._nrebin * p_wfs.npix))
801  x = (X[1] / p_wfs._nrebin).astype(np.int64)
802  y = (X[0] / p_wfs._nrebin).astype(np.int64)
803 
804  # binindices
805  binindices = np.zeros((p_wfs._Ntot, p_wfs._Ntot))
806  binindices[indi:indj, indi:indj] = x + y * p_wfs.npix + 1
807 
808  binmap = np.zeros((p_wfs._nrebin * p_wfs._nrebin, p_wfs.npix * p_wfs.npix))
809 
810  X = np.indices((p_wfs._Ntot, p_wfs._Ntot))
811  tmp = X[1] + X[0] * p_wfs._Ntot
812 
813  if (p_wfs.gsalt <= 0):
814  binindices = np.roll(binindices, binindices.shape[0] // 2, axis=0)
815  binindices = np.roll(binindices, binindices.shape[1] // 2, axis=1)
816 
817  for i in range(p_wfs.npix * p_wfs.npix):
818  binmap[:, i] = tmp[np.where(binindices == i + 1)]
819  # binmap=np.reshape(binmap.flatten("F"),(binmap.shape[0],binmap.shape[1]))
820  p_wfs._binmap = np.copy(binmap.astype(np.int32))
821 
822  dr0 = p_tel.diam / r0 * \
823  (0.5 / p_wfs.Lambda) ** 1.2 / \
824  np.cos(p_geom.zenithangle * CONST.DEG2RAD) ** 0.6
825  fwhmseeing = p_wfs.Lambda / \
826  (p_tel.diam / np.sqrt(p_wfs.nxsub ** 2. + (dr0 / 1.5) ** 2.)) / 4.848
827  fwhmseeing = min(fwhmseeing, 2 * p_wfs.pixsize)
828  kernelfwhm = np.sqrt(fwhmseeing**2. + p_wfs.kernel**2.)
829 
830  tmp = util.makegaussian(p_wfs._Ntot, kernelfwhm / p_wfs._qpixsize, p_wfs._Ntot // 2,
831  p_wfs._Ntot // 2).astype(np.float32)
832 
833  tmp = np.roll(tmp, tmp.shape[0] // 2, axis=0)
834  tmp = np.roll(tmp, tmp.shape[1] // 2, axis=1)
835 
836  tmp[0, 0] = 1. # this insures that even with fwhm=0, the kernel is a dirac
837  tmp = tmp / np.sum(tmp)
838  tmp = np.fft.fft2(tmp).astype(np.complex64) / (p_wfs._Ntot * p_wfs._Ntot)
839  p_wfs._ftkernel = np.copy(tmp).astype(np.complex64)
840 
841  # dealing with photometry
842  # telSurf = np.pi / 4. * (1 - p_tel.cobs**2.) * p_tel.diam**2.
843 
844  # from the guide star
845  if (p_wfs.gsalt == 0):
846  if (p_wfs.zerop == 0):
847  p_wfs.zerop = 1e11
848  # p_wfs._nphotons = p_wfs.zerop * 10 ** (-0.4 * p_wfs.gsmag) * \
849  # p_wfs.optthroughput * \
850  # (p_tel.diam / p_wfs.nxsub) ** 2. * ittime
851  # include throughput to WFS
852  # for unobstructed subaperture
853  # per iteration
854  p_wfs._nphotons = compute_nphotons(scons.WFSType.SH, ittime, p_wfs.optthroughput,
855  p_tel.diam, nxsub=p_wfs.nxsub,
856  zerop=p_wfs.zerop, gsmag=p_wfs.gsmag,
857  verbose=verbose)
858 
859  else: # we are dealing with a LGS
860  # p_wfs._nphotons = p_wfs.lgsreturnperwatt * \
861  # p_wfs.laserpower * \
862  # p_wfs.optthroughput * \
863  # (p_tel.diam / p_wfs.nxsub) ** 2. * 1e4 * \
864  # ittime
865  # detected by WFS
866  # ... for given power
867  # include throughput to WFS
868  # for unobstructed subaperture
869  # per iteration
870  p_wfs._nphotons = compute_nphotons(scons.WFSType.SH, ittime, p_wfs.optthroughput,
871  p_tel.diam, nxsub=p_wfs.nxsub,
872  lgsreturnperwatt=p_wfs.lgsreturnperwatt,
873  laserpower=p_wfs.laserpower, verbose=verbose)
874  # Creating field stop mask
875  if(p_wfs.fssize != 0):
876  fftsize = util.fft_goodsize(p_geom._mpupil.shape[0])
877  fspixsize = (p_geom.pupdiam *
878  (p_wfs.Lambda * 1.e-6) / p_tel.diam * CONST.RAD2ARCSEC) / fftsize
879 
880  fsradius_pixels = int(p_wfs.fssize / fspixsize / 2.)
881  if (p_wfs.fstop == scons.FieldStopType.ROUND):
882  focmask = util.dist(fftsize, xc=fftsize / 2. - 0.5,
883  yc=fftsize / 2. - 0.5) < (fsradius_pixels)
884  elif (p_wfs.fstop == scons.FieldStopType.SQUARE):
885  X = np.indices((fftsize, fftsize)) + 1 # TODO: +1 ??
886  x = X[1] - (fftsize + 1.) / 2.
887  y = X[0] - (fftsize + 1.) / 2.
888  focmask = (np.abs(x) <= (fsradius_pixels)) * \
889  (np.abs(y) <= (fsradius_pixels))
890  else:
891  msg = "wfs fstop must be FieldStopType.[ROUND|SQUARE]"
892  raise ValueError(msg)
893 
894  pyr_focmask = focmask * 1.0 # np.fft.fftshift(focmask*1.0)
895  p_wfs._submask = np.fft.fftshift(pyr_focmask)
896 
897 def geom_init(p_geom: conf.Param_geom, p_tel: conf.Param_tel, padding=2):
898  """
899  Initialize the system geometry
900 
901  Args:
902  p_geom: (Param_geom) : geometry settings
903  p_tel: (Param_tel) : telescope settings
904  padding: (optional) : padding factor for PYRHR geometry
905  """
906 
907  # First power of 2 greater than pupdiam
908  p_geom.ssize = int(2**np.ceil(np.log2(p_geom.pupdiam) + 1))
909  # Using images centered on 1/2 pixels
910  p_geom.cent = p_geom.ssize / 2 - 0.5
911 
912  p_geom._p1 = int(np.ceil(p_geom.cent - p_geom.pupdiam / 2.))
913  p_geom._p2 = int(np.floor(p_geom.cent + p_geom.pupdiam / 2.))
914 
915  p_geom.pupdiam = p_geom._p2 - p_geom._p1 + 1
916 
917  p_geom._n = p_geom.pupdiam + 2 * padding
918  p_geom._n1 = p_geom._p1 - padding
919  p_geom._n2 = p_geom._p2 + padding
920 
921  cent = p_geom.pupdiam / 2. - 0.5
922 
923  # Useful pupil
924  p_geom._spupil = mkP.make_pupil(p_geom.pupdiam, p_geom.pupdiam, p_tel, cent,
925  cent).astype(np.float32)
926 
927  # large pupil (used for image formation)
928  p_geom._ipupil = util.pad_array(p_geom._spupil, p_geom.ssize).astype(np.float32)
929 
930  # useful pupil + 4 pixels
931  p_geom._mpupil = util.pad_array(p_geom._spupil, p_geom._n).astype(np.float32)
932 
933  if (p_tel.std_piston or p_tel.std_tt):
934  p_geom._phase_ab_M1 = mkP.make_phase_ab(p_geom.pupdiam, p_geom.pupdiam, p_tel,
935  p_geom._spupil, cent,
936  cent).astype(np.float32)
937  p_geom._phase_ab_M1_m = util.pad_array(p_geom._phase_ab_M1,
938  p_geom._n).astype(np.float32)
939  #TODO: apodizer
940  """
941  if p_geom.apod:
942  if p_geom.apodFile is None or p_geom.apodFile == '':
943  apod_filename = shesha_savepath + \
944  "apodizer/SP_HARMONI_I4_C6_N1024.npy"
945  p_geom._apodizer = makeA.make_apodizer(
946  p_geom.pupdiam, p_geom.pupdiam,
947  apod_filename.encode(), 180. / 12.).astype(np.float32)
948  else:
949  """
950  p_geom._apodizer = np.ones(p_geom._spupil.shape, dtype=np.int32)
951  p_geom._pixsize = p_tel.diam / p_geom.pupdiam
952  p_geom.is_init = True
953 
954 
955 def geom_init_generic(p_geom, pupdiam, t_spiders=0.01, spiders_type="six", xc=0, yc=0,
956  real=0, cobs=0):
957  """Initialize the system geometry
958 
959  Args:
960  pupdiam: (long) : linear size of total pupil
961 
962  t_spiders: (float) : secondary supports ratio.
963 
964  spiders_type: (str) : secondary supports type: "four" or "six".
965 
966  xc: (int)
967 
968  yc: (int)
969 
970  real: (int)
971 
972  cobs: (float) : central obstruction ratio.
973  """
974  # Initialize the system pupil
975  # first poxer of 2 greater than pupdiam
976  p_geom.ssize = int(2**np.ceil(np.log2(pupdiam) + 1))
977  # using images centered on 1/2 pixels
978  p_geom.cent = p_geom.ssize / 2 + 0.5
979  # valid pupil geometry
980  pupdiam = int(pupdiam)
981  p_geom._p1 = int(np.ceil(p_geom.cent - pupdiam / 2.))
982  p_geom._p2 = int(np.floor(p_geom.cent + pupdiam / 2.))
983  p_geom.pupdiam = p_geom._p2 - p_geom._p1 + 1
984  p_geom._n = p_geom.pupdiam + 4
985  p_geom._n1 = p_geom._p1 - 2
986  p_geom._n2 = p_geom._p2 + 2
987 
988  # useful pupil
989  p_geom._spupil = mkP.make_pupil_generic(pupdiam, pupdiam, t_spiders, spiders_type,
990  xc, yc, real, cobs)
991 
992  # large pupil (used for image formation)
993  p_geom._ipupil = pad_array(p_geom._spupil, p_geom.ssize).astype(np.float32)
994 
995  # useful pupil + 4 pixels
996  p_geom._mpupil = pad_array(p_geom._spupil, p_geom._n).astype(np.float32)
997 
998 
999 def pad_array(A, N):
1000  S = A.shape
1001  D1 = (N - S[0]) // 2
1002  D2 = (N - S[1]) // 2
1003  padded = np.zeros((N, N))
1004  padded[D1:D1 + S[0], D2:D2 + S[1]] = A
1005  return padded
Parameter classes for COMPASS.
Numerical constants for shesha and config enumerations for safe-typing.
Definition: constants.py:1
def init_wfs_size(conf.Param_wfs p_wfs, float r0, conf.Param_tel p_tel, verbose=1)
Compute all the parameters usefull for further WFS image computation (array sizes)
Definition: geom_init.py:232
def geom_init(conf.Param_geom p_geom, conf.Param_tel p_tel, padding=2)
Initialize the system geometry.
Definition: geom_init.py:913
def tel_init(carmaWrap_context context, conf.Param_geom p_geom, conf.Param_tel p_tel, r0=None, ittime=None, p_wfss=None, dm=None)
Initialize the overall geometry of the AO system, including pupil and WFS.
Definition: geom_init.py:73
def geom_init_generic(p_geom, pupdiam, t_spiders=0.01, spiders_type="six", xc=0, yc=0, real=0, cobs=0)
Initialize the system geometry.
Definition: geom_init.py:981
def init_pyrhr_geom(conf.Param_wfs p_wfs, float r0, conf.Param_tel p_tel, conf.Param_geom p_geom, float ittime, bool verbose=True)
Compute the geometry of PYRHR WFSs: valid subaps, positions of the subaps, flux per subap,...
Definition: geom_init.py:447
def init_wfs_geom(conf.Param_wfs p_wfs, float r0, conf.Param_tel p_tel, conf.Param_geom p_geom, float ittime, verbose=1)
Compute the geometry of WFSs: valid subaps, positions of the subaps, flux per subap,...
Definition: geom_init.py:138
def compute_nphotons(wfs_type, ittime, optthroughput, diam, cobs=0, nxsub=0, zerop=0, gsmag=0, lgsreturnperwatt=0, laserpower=0, verbose=1)
Determines the number of photons TBC.
Definition: geom_init.py:396
def init_sh_geom(conf.Param_wfs p_wfs, float r0, conf.Param_tel p_tel, conf.Param_geom p_geom, float ittime, bool verbose=True)
Compute the geometry of SH WFSs: valid subaps, positions of the subaps, flux per subap,...
Definition: geom_init.py:684
Pupil creation functions.
Definition: make_pupil.py:1
Basic utilities function.
Definition: utilities.py:1