COMPASS  5.0.0
End-to-end AO simulation tool using GPU acceleration
shesha.init.lgs_init Namespace Reference

Initialization of a LGS in a Wfs object. More...

Functions

def make_lgs_prof1d (conf.Param_wfs p_wfs, conf.Param_tel p_tel, np.ndarray prof, np.ndarray h, float beam, center="")
 same as prep_lgs_prof but cpu only. More...
 
def prep_lgs_prof (conf.Param_wfs p_wfs, int nsensors, conf.Param_tel p_tel, Sensors sensors, center="", imat=0)
 The function returns an image array(double,n,n) of a laser beacon elongated by perpective effect. More...
 

Variables

 shesha_db = os.environ['SHESHA_DB_ROOT']
 
 shesha_savepath = shesha_db
 

Detailed Description

Initialization of a LGS in a Wfs object.

Author
COMPASS Team https://github.com/ANR-COMPASS
Version
5.0.0
Date
2020/05/18

This file is part of COMPASS https://anr-compass.github.io/compass/

Copyright (C) 2011-2019 COMPASS Team https://github.com/ANR-COMPASS All rights reserved. Distributed under GNU - LGPL

COMPASS is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.

COMPASS: End-to-end AO simulation tool using GPU acceleration The COMPASS platform was designed to meet the need of high-performance for the simulation of AO systems.

The final product includes a software package for simulating all the critical subcomponents of AO, particularly in the context of the ELT and a real-time core based on several control approaches, with performances consistent with its integration into an instrument. Taking advantage of the specific hardware architecture of the GPU, the COMPASS tool allows to achieve adequate execution speeds to conduct large simulation campaigns called to the ELT.

The COMPASS platform can be used to carry a wide variety of simulations to both testspecific components of AO of the E-ELT (such as wavefront analysis device with a pyramid or elongated Laser star), and various systems configurations such as multi-conjugate AO.

COMPASS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with COMPASS. If not, see https://www.gnu.org/licenses/lgpl-3.0.txt.

Function Documentation

◆ make_lgs_prof1d()

def shesha.init.lgs_init.make_lgs_prof1d ( conf.Param_wfs  p_wfs,
conf.Param_tel  p_tel,
np.ndarray  prof,
np.ndarray  h,
float  beam,
  center = "" 
)

same as prep_lgs_prof but cpu only.

original routine from rico

:parameters: p_tel (Param_tel) : telescope settings

prof (np.ndarray[dtype=np.float32]) : Na profile intensity, in arbitrary units

h (np.ndarray[dtype=np.float32]) : altitude, in meters. h MUST be an array with EQUALLY spaced elements.

beam (float) : size in arcsec of the laser beam

center (string) : either "image" or "fourier" depending on where the centre should be.

Definition at line 71 of file lgs_init.py.

71  center: (string) : either "image" or "fourier" depending on where the centre should be.
72  """
73 
74  p_wfs._prof1d = prof
75  p_wfs._profcum = np.zeros(h.shape[0] + 1, dtype=np.float32)
76  p_wfs._profcum[1:] = prof.cumsum()
77 
78  subapdiam = p_tel.diam / p_wfs.nxsub # diam of subap
79  if (p_wfs.nxsub > 1):
80  xsubs = np.linspace((subapdiam - p_tel.diam) / 2, (p_tel.diam - subapdiam) / 2,
81  p_wfs.nxsub).astype(np.float32)
82  else:
83  xsubs = np.zeros(1, dtype=np.float32)
84  ysubs = xsubs.copy().astype(np.float32)
85 
86  # cdef int nP=prof.shape[0] #UNUSED
87  hG = np.sum(h * prof) / np.sum(prof)
88  x = np.arange(p_wfs._Ntot).astype(np.float32) - p_wfs._Ntot / 2
89  # x expressed in pixels. (0,0) is in the fourier-center.
90  x = x * p_wfs._qpixsize # x expressed in arcseconds
91  # cdef float dx=x[1]-x[0] #UNUSED
92  # cdef float dh=h[1]-h[0] #UNUSED
93 
94  if (p_wfs.nxsub > 1):
95  dOffAxis = np.sqrt((xsubs[p_wfs._validsubsy // p_wfs.npix] - p_wfs.lltx)**2 +
96  (ysubs[p_wfs._validsubsx // p_wfs.npix] - p_wfs.llty)**2)
97  else:
98  dOffAxis = np.sqrt((xsubs - p_wfs.lltx)**2 + (ysubs - p_wfs.llty)**2)
99 
100  profi = np.zeros((p_wfs._Ntot, p_wfs._nvalid), dtype=np.float32)
101 
102  subsdone = np.ones(p_wfs._nvalid, dtype=np.int32)
103  dif2do = np.zeros(p_wfs._nvalid, dtype=np.int32)
104 
105  while (np.any(subsdone)):
106  tmp = dOffAxis[np.where(subsdone)][0]
107  inds = np.where(dOffAxis == tmp)[0]
108  # height, translated in arcsec due to perspective effect
109  zhc = (h - hG) * (206265. * tmp / hG**2)
110  dzhc = zhc[1] - zhc[0]
111 
112  if (p_wfs._qpixsize > dzhc):
113  avg_zhc = np.zeros(zhc.size + 1, dtype=np.float32)
114  avg_zhc[0] = zhc[0]
115  avg_zhc[avg_zhc.size - 1] = zhc[zhc.size - 1]
116  avg_zhc[1:-1] = 0.5 * (zhc[1:] + zhc[:-1])
117  avg_x = np.zeros(x.size + 1, dtype=np.float32)
118  avg_x[0] = x[0]
119  avg_x[avg_x.size - 1] = x[x.size - 1]
120  avg_x[1:-1] = 0.5 * (x[1:] + x[:-1])
121 
122  for i in range(inds.size):
123  profi[:, inds[i]] = np.diff(np.interp(avg_x, avg_zhc,
124  p_wfs._profcum)).astype(np.float32)
125 
126  else:
127  for i in range(inds.size):
128  profi[:, inds[i]] = np.interp(x, zhc, prof)
129  subsdone[inds] = 0
130 
131  w = beam / 2.35482005
132  if (w == 0):
133  # TODO what is n
134  n = 1
135  g = np.zeros(n, dtype=np.float32)
136  if (center == "image"):
137  g[n / 2 - 1] = 0.5
138  g[n / 2] = 0.5
139  else:
140  g[n / 2] = 1
141 
142  else:
143  if (center == "image"):
144  if ((p_wfs.npix * p_wfs._nrebin) % 2 != p_wfs._Nfft % 2):
145  g = np.exp(-(x + p_wfs._qpixsize)**2 / (2 * w**2.))
146  else:
147  g = np.exp(-(x + p_wfs._qpixsize / 2)**2 / (2 * w**2.))
148 
149  else:
150  g = np.exp(-x**2 / (2 * w**2.))
151 
152  p_wfs._ftbeam = np.fft.fft(g).astype(np.complex64)
153  p_wfs._beam = g.astype(np.float32)
154  # convolved profile in 1D.
155 
156  g_extended = np.tile(g, (p_wfs._nvalid, 1)).T
157 
158  p1d = np.fft.ifft(
159  np.fft.fft(profi, axis=0) * np.fft.fft(g_extended, axis=0),
160  axis=0).real.astype(np.float32)
161  p1d = p1d * p1d.shape[0]
162  p1d = np.roll(p1d, int(p_wfs._Ntot / 2. - 0.5), axis=0)
163  p1d = np.abs(p1d)
164 
165  im = np.zeros((p1d.shape[1], p1d.shape[0], p1d.shape[0]), dtype=np.float32)
166  for i in range(p1d.shape[0]):
167  im[:, i, :] = g[i] * p1d.T
168 
169  if (ysubs.size > 1):
170  azimuth = np.arctan2(ysubs[p_wfs._validsubsy // p_wfs.npix] - p_wfs.llty,
171  xsubs[p_wfs._validsubsx // p_wfs.npix] - p_wfs.lltx)
172  else:
173  azimuth = np.arctan2(ysubs - p_wfs.llty, xsubs - p_wfs.lltx)
174 
175  p_wfs._azimuth = azimuth
176 
177  if (center == "image"):
178  xcent = p_wfs._Ntot / 2. - 0.5
179  ycent = xcent
180  else:
181  xcent = p_wfs._Ntot / 2. #+ 1
182  ycent = xcent
183 
184  if (ysubs.size > 0):
185  # TODO rotate
186  # im = util.rotate3d(im, azimuth * 180 / np.pi, xcent, ycent) --> ça marche pas !!
187  # max_im = np.max(im, axis=(1, 2))
188  # im = (im.T / max_im).T
189  for k in range(im.shape[0]):
190  img = im[k, :, :] / im[k, :, :].max()
191  im[k, :, :] = sci.rotate(img, azimuth[k] * 180 / np.pi, reshape=False)
192 
193  else:
194  # im = util.rotate(im, azimuth * 180 / np.pi, xcent, ycent)
195  # im = im / np.max(im)
196  im = sci.rotate(img, azimuth * 180 / np.pi, reshape=False)
197 
198  p_wfs._lgskern = im.T
199 
200 
Here is the caller graph for this function:

◆ prep_lgs_prof()

def shesha.init.lgs_init.prep_lgs_prof ( conf.Param_wfs  p_wfs,
int  nsensors,
conf.Param_tel  p_tel,
Sensors  sensors,
  center = "",
  imat = 0 
)

The function returns an image array(double,n,n) of a laser beacon elongated by perpective effect.

It is obtaind by convolution of a gaussian of width "lgsWidth" arcseconds, with the line of the sodium profile "prof". The altitude of the profile is the array "h".

:parameters:

p_wfs (Param_wfs) : WFS settings

nsensors (int) : wfs index

p_tel (Param_tel) : telescope settings

Sensors (Sensors) : WFS object

center (string) : either "image" or "fourier" depending on where the centre should be.

Computation of LGS spot from the sodium profile: Everything is done here in 1D, because the Na profile is the result of the convolution of a function P(x,y) = profile(x) . dirac(y) by a gaussian function, for which variables x and y can be split : exp(-(x^2+y^2)/2.s^2) = exp(-x^2/2.s^2) * exp(-y^2/2.s^2) The convolution is (symbol $ denotes integral) C(X,Y) = $$ exp(-x^2/2.s^2) * exp(-y^2/2.s^2) * profile(x-X) * dirac(y-Y) dx dy First one performs the integration along y C(X,Y) = exp(-Y^2/2.s^2) $ exp(-x^2/2.s^2) * profile(x-X) dx which shows that the profile can be computed by

  • convolving the 1-D profile
  • multiplying it in the 2nd dimension by a gaussian function

If one has to undersample the inital profile, then some structures may be "lost". In this case, it's better to try to "save" those structures by re-sampling the integral of the profile, and then derivating it afterwards. Now, if the initial profile is a coarse one, and that one has to oversample it, then a simple re-sampling of the profile is adequate.

Definition at line 235 of file lgs_init.py.

235  simple re-sampling of the profile is adequate.
236  """
237  if (p_wfs.proftype is None or p_wfs.proftype == ""):
238  p_wfs.set_proftype(scons.ProfType.GAUSS1)
239 
240  profilename = scons.ProfType.FILES[p_wfs.proftype]
241 
242  profile_path = shesha_savepath + profilename
243  print("reading Na profile from", profile_path)
244  prof = np.load(profile_path)
245  make_lgs_prof1d(p_wfs, p_tel, np.mean(prof[1:, :], axis=0), prof[0, :],
246  p_wfs.beamsize, center="image")
247  p_wfs.set_altna(prof[0, :].astype(np.float32))
248  p_wfs.set_profna(np.mean(prof[1:, :], axis=0).astype(np.float32))
249 
250  p_wfs._prof1d = p_wfs._profna
251  p_wfs._profcum = np.zeros(p_wfs._profna.size + 1, dtype=np.float32)
252  p_wfs._profcum[1:] = p_wfs._profna.cumsum()
253  subapdiam = p_tel.diam / p_wfs.nxsub # diam of subap
254 
255  if (p_wfs.nxsub > 1):
256  xsubs = np.linspace((subapdiam - p_tel.diam) / 2, (p_tel.diam - subapdiam) / 2,
257  p_wfs.nxsub).astype(np.float32)
258  else:
259  xsubs = np.zeros(1, dtype=np.float32)
260  ysubs = xsubs.copy().astype(np.float32)
261 
262  # center of gravity of the profile
263  hG = np.sum(p_wfs._altna * p_wfs._profna) / np.sum(p_wfs._profna)
264  x = np.arange(p_wfs._Ntot).astype(np.float32) - p_wfs._Ntot / 2
265  # x expressed in pixels. (0,0) is in the fourier-center
266 
267  x = x * p_wfs._qpixsize # x expressed in arcseconds
268  # cdef float dx=x[1]-x[0] #UNUSED
269  dh = p_wfs._altna[1] - p_wfs._altna[0]
270 
271  if (p_wfs.nxsub > 1):
272  dOffAxis = np.sqrt((xsubs[p_wfs._validsubsx // p_wfs.npix] - p_wfs.lltx)**2 +
273  (ysubs[p_wfs._validsubsy // p_wfs.npix] - p_wfs.llty)**2)
274  else:
275  dOffAxis = np.sqrt((xsubs - p_wfs.lltx)**2 + (ysubs - p_wfs.llty)**2)
276 
277  if (imat > 0):
278  dOffAxis *= 0.
279 
280  w = p_wfs.beamsize / 2.35482005 # TODO: FIXME
281  if (w == 0):
282  # TODO what is n
283  n = 1
284  g = np.zeros(n, dtype=np.float32)
285  if (center == "image"):
286  g[n / 2 - 1] = 0.5
287  g[n / 2] = 0.5
288  else:
289  g[n / 2] = 1
290 
291  else:
292  if (center == "image"):
293  g = np.exp(-(x + p_wfs._qpixsize / 2)**2 / (2 * w**2.))
294  else:
295  g = np.exp(-x**2 / (2 * w**2.))
296 
297  p_wfs._ftbeam = np.fft.fft(g, axis=0).astype(np.complex64)
298  p_wfs._beam = g
299  # convolved profile in 1D.
300 
301  if (xsubs.size > 1):
302  azimuth = np.arctan2(ysubs[p_wfs._validsubsy // p_wfs.npix] - p_wfs.llty,
303  xsubs[p_wfs._validsubsx // p_wfs.npix] - p_wfs.lltx)
304  else:
305  azimuth = np.arctan2(ysubs - p_wfs.llty, xsubs - p_wfs.lltx)
306 
307  p_wfs._azimuth = azimuth
308 
309  sensors.d_wfs[nsensors].d_gs.d_lgs.lgs_init(
310  p_wfs._prof1d.size, hG, p_wfs._altna[0], dh, p_wfs._qpixsize, dOffAxis,
311  p_wfs._prof1d, p_wfs._profcum, p_wfs._beam, p_wfs._ftbeam, p_wfs._azimuth)
Here is the call graph for this function:

Variable Documentation

◆ shesha_db

string shesha.init.lgs_init.shesha_db = os.environ['SHESHA_DB_ROOT']

Definition at line 40 of file lgs_init.py.

◆ shesha_savepath

shesha.init.lgs_init.shesha_savepath = shesha_db

Definition at line 46 of file lgs_init.py.