mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
zernikeTemporalPSD.hpp
Go to the documentation of this file.
1 /** \file zernikeTemporalPSD.hpp
2  * \author Jared R. Males (jaredmales@gmail.com)
3  * \brief Calculation of the temporal PSD of Zernike modes.
4  * \ingroup mxAOm_files
5  *
6  */
7 
8 //***********************************************************************//
9 // Copyright 2022 Jared R. Males (jaredmales@gmail.com)
10 //
11 // This file is part of mxlib.
12 //
13 // mxlib is free software: you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation, either version 3 of the License, or
16 // (at your option) any later version.
17 //
18 // mxlib is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
25 //***********************************************************************//
26 
27 #ifndef zernikeTemporalPSD_hpp
28 #define zernikeTemporalPSD_hpp
29 
30 #include <iostream>
31 #include <fstream>
32 #include <cmath>
33 
34 #include <sys/stat.h>
35 
36 #include <gsl/gsl_integration.h>
37 #include <gsl/gsl_errno.h>
38 
39 #include <Eigen/Dense>
40 
41 #include "../../math/constants.hpp"
42 #include "../../math/func/jinc.hpp"
43 #include "../../math/func/airyPattern.hpp"
44 #include "../../math/vectorUtils.hpp"
45 #include "../../ioutils/fits/fitsFile.hpp"
46 #include "../../sigproc/zernike.hpp"
47 #include "../../ioutils/stringUtils.hpp"
48 #include "../../ioutils/readColumns.hpp"
49 #include "../../ioutils/binVector.hpp"
50 #include "../../ioutils/fileUtils.hpp"
51 
52 #include "../../ipc/ompLoopWatcher.hpp"
53 #include "../../mxError.hpp"
54 
55 #include "aoSystem.hpp"
56 #include "aoPSDs.hpp"
57 #include "wfsNoisePSD.hpp"
58 #include "clAOLinearPredictor.hpp"
59 #include "clGainOpt.hpp"
60 #include "varmapToImage.hpp"
61 #include "speckleAmpPSD.hpp"
62 
63 #include "aoConstants.hpp"
64 
65 namespace mx
66 {
67 namespace AO
68 {
69 namespace analysis
70 {
71 
72 #ifndef WSZ
73 
74 /** \def WFZ
75  * \brief Size of the GSL integration workspace
76  */
77 #define WSZ 100000
78 
79 #endif
80 
81 
82 
83 //Forward declaration
84 template<typename realT, typename aosysT>
85 realT F_zernike (realT kv, void * params);
86 
87 
88 ///Class to manage the calculation of temporal PSDs of the Fourier modes in atmospheric turbulence.
89 /** Works with both basic (sines/cosines) and modified Fourier modes.
90  *
91  * \tparam realT is a real floating point type for calculations. Currently must be double due to gsl_integration.
92  * \tparam aosysT is an AO system type, usually of type ao_system.
93  *
94  * \todo Split off the integration parameters in a separate structure.
95  * \todo once integration parameters are in a separate structure, make this a class with protected members.
96  * \todo GSL error handling
97  *
98  * \ingroup mxAOAnalytic
99  */
100 template<typename _realT, typename aosysT>
102 {
103  typedef _realT realT;
104  typedef std::complex<realT> complexT;
105 
106  ///Pointer to an AO system structure.
107  aosysT * m_aosys {nullptr};
108 
109  realT m_f {0}; ///< the current temporal frequency0
110  realT m_zern_j {0}; ///< the current mode number
111  int m_zern_m {0}; ///< The current mode m
112  int m_zern_n {0}; ///< The current mode n
113 
114  realT m_cq {0}; ///< The cosine of the wind direction
115  realT m_sq {0}; ///< The sine of the wind direction
116  realT m_spatialFilter {false}; ///< Flag indicating if a spatial filter is applied
117 
118  int _layer_i; ///< The index of the current layer.
119 
120  ///Worskspace for the gsl integrators, allocated to WSZ if constructed as worker (with allocate == true).
121  gsl_integration_workspace * _w;
122 
123  realT _absTol; ///< The absolute tolerance to use in the GSL integrator
124  realT _relTol; ///< The relative tolerance to use in the GSL integrator
125 
126 
127 
128  std::vector<realT> Jps;
129  std::vector<realT> Jms;
130  std::vector<int> ps;
131  std::vector<realT> ms;
132  std::vector<realT> ns;
133 
134 
135 public:
136  ///Default c'tor
138 
139  ///Constructor with workspace allocation
140  /**
141  * \param allocate if true, then the workspace for GSL integrators is allocated.
142  */
143  explicit zernikeTemporalPSD(bool allocate);
144 
145  ///Destructor
146  /** Frees GSL workspace if it was allocated.
147  */
149 
150 protected:
151  ///Initialize parameters to default values.
152  void initialize();
153 
154 public:
155 
156  /** \name GSL Integration Tolerances
157  * For good results it seems that absolute tolerance (absTol) needs to be 1e-10. Lower tolerances cause some frequencies to drop out, etc.
158  * Relative tolerance (relTol) seems to be less sensitive, and 1e-4 works on cases tested as of 1 Jan, 2017.
159  *
160  * See the documentation for the GSL Library integrators at (https://www.gnu.org/software/gsl/manual/htmlm_node/QAGI-adaptive-integration-on-infinite-intervals.html)
161  * @{
162  */
163 
164  ///Set absolute tolerance
165  /**
166  * \param at is the new absolute tolerance.
167  */
168  void absTol(realT at);
169 
170  ///Get the current absolute tolerance
171  /**
172  * \returns _absTol
173  */
174  realT absTol();
175 
176  ///Set relative tolerance
177  /**
178  * \param rt is the new relative tolerance.
179  */
180  void relTol(realT rt);
181 
182  ///Get the current relative tolerance
183  /**
184  * \returns _relTol
185  */
186  realT relTol();
187 
188  ///@}
189 
190 
191  ///Calculate the temporal PSD for a Fourier mode for a single layer.
192  /**
193  *
194  * \todo implement error checking.
195  * \todo need a way to track convergence failures in integral without throwing an error.
196  * \todo need better handling of averaging for the -17/3 extension.
197  *
198  */
199  int singleLayerPSD( std::vector<realT> &PSD, ///< [out] the calculated PSD
200  std::vector<realT> &freq, ///< [in] the populated temporal frequency grid defining the frequencies at which the PSD is calculated
201  int zern_j, ///< [in]
202  int layer_i, ///< [in] the index of the layer, for accessing the atmosphere parameters
203  realT fmax = 0 ///< [in] [optional] set the maximum temporal frequency for the calculation. The PSD is filled in with a -17/3 power law past this frequency.
204  );
205 
206  ///\cond multilayerm_parallel
207  //Conditional to exclude from Doxygen.
208 
209 protected:
210  //Type to allow overloading of the multiLayerPSD workers based on whether they are parallelized or not.
211  template<bool m_parallel>
212  struct isParallel{};
213 
214  //Parallelized version of multiLayerPSD, with OMP directives.
215  int m_multiLayerPSD( std::vector<realT> & PSD,
216  std::vector<realT> & freq,
217  int zern_j,
218  realT fmax,
219  isParallel<true> parallel );
220 
221  //Non-Parallelized version of multiLayerPSD, without OMP directives.
222  int m_multiLayerPSD( std::vector<realT> & PSD,
223  std::vector<realT> & freq,
224  int zern_j,
225  realT fmax,
226  isParallel<false> parallel );
227 
228  ///\endcond
229 
230 public:
231  ///Calculate the temporal PSD for a Fourier mode in a multi-layer model.
232  /**
233  *
234  * \tparam parallel controls whether layers are calculated in parallel. Default is true. Set to false if this is called inside a parallelized loop, as in \ref makePSDGrid.
235  *
236  * \todo implement error checking
237  * \todo handle reports of convergence failures form singleLayerPSD when implemented.
238  *
239  */
240  template<bool parallel=true>
241  int multiLayerPSD( std::vector<realT> & PSD, ///< [out] the calculated PSD
242  std::vector<realT> & freq, ///< [in] the populated temporal frequency grid defining at which frequencies the PSD is calculated
243  int zern_j,
244  realT fmax = 0 ///< [in] [optional] set the maximum temporal frequency for the calculation. The PSD is filled in
245  /// with a -17/3 power law past this frequency. If 0, then it is taken to be 150 Hz + 2*fastestPeak(m,n).
246  );
247 
248 
249 };
250 
251 template<typename realT, typename aosysT>
253 {
254  m_aosys = nullptr;
255  initialize();
256 }
257 
258 template<typename realT, typename aosysT>
260 {
261  m_aosys = nullptr;
262  initialize();
263 
264  if(allocate)
265  {
266  _w = gsl_integration_workspace_alloc (WSZ);
267  }
268 }
269 
270 template<typename realT, typename aosysT>
272 {
273  if(_w)
274  {
275  gsl_integration_workspace_free (_w);
276  }
277 }
278 
279 template<typename realT, typename aosysT>
281 {
282  _w = 0;
283 
284  _absTol = 1e-10;
285  _relTol = 1e-4;
286 }
287 
288 template<typename realT, typename aosysT>
290 {
291  _absTol = at;
292 }
293 
294 template<typename realT, typename aosysT>
296 {
297  return _absTol;
298 }
299 
300 template<typename realT, typename aosysT>
302 {
303  _relTol = rt;
304 }
305 
306 template<typename realT, typename aosysT>
308 {
309  return _relTol;
310 }
311 
312 
313 template<typename realT, typename aosysT>
315  std::vector<realT> &freq,
316  int zern_j,
317  int layer_i,
318  realT fmax )
319 {
320  if(fmax == 0) fmax = freq[freq.size()-1];
321 
322  realT v_wind = m_aosys->atm.layer_v_wind(layer_i);
323  realT q_wind = m_aosys->atm.layer_dir(layer_i);
324 
325  //Rotate the basis
326  realT cq = cos(q_wind);
327  realT sq = sin(q_wind);
328 
329 
330  realT scale = 2*(1/v_wind); //Factor of 2 for negative frequencies.
331 
332  //We'll get the occasional failure to reach tolerance error, just ignore them all for now.
333  gsl_set_error_handler_off();
334 
335  //Create a local instance so that we're reentrant
337 
338  params.m_aosys = m_aosys;
339  params._layer_i = layer_i;
340  params.m_zern_j = zern_j;
341  sigproc::noll_nm(params.m_zern_n, params.m_zern_m, params.m_zern_j);
342  params.m_cq = cq; //for de-rotating ku and kv for spatial filtering
343  params.m_sq = sq; //for de-rotation ku and kv for spatial filtering
344  if(m_aosys->spatialFilter_ku() < std::numeric_limits<realT>::max() || m_aosys->spatialFilter_kv() < std::numeric_limits<realT>::max()) params.m_spatialFilter = true;
345 
346 
347 
348  realT result, error;
349 
350  //Setup the GSL calculation
351  gsl_function func;
352  func.function = &F_zernike<realT, aosysT>;
353 
354 
355  func.params = &params;
356 
357 
358  //Here we only calculate up to fmax.
359  size_t i=0;
360  while( freq[i] <= fmax )
361  {
362  params.m_f = freq[i];
363 
364  int ec = gsl_integration_qagi (&func, _absTol, _relTol, WSZ, params._w, &result, &error);
365 
366  if(ec == GSL_EDIVERGE)
367  {
368  std::cerr << "GSL_EDIVERGE:" << " " << freq[i] << " " << v_wind << " " << zern_j << "\n";
369  std::cerr << "ignoring . . .\n";
370  }
371 
372  PSD[i] = scale*result;
373 
374  ++i;
375  if(i >= freq.size()) break;
376  }
377 /*
378  //Now fill in from fmax to the actual max frequency with a -(alpha+2) power law.
379  size_t j=i;
380 
381  if(j == freq.size()) return 0;
382 
383  //First average result for last 50.
384  PSD[j] = PSD[i-50] * pow( freq[i-50]/freq[j], m_aosys->atm.alpha(layer_i)+2);//seventeen_thirds<realT>());
385  for(size_t k=49; k> 0; --k)
386  {
387  PSD[j] += PSD[i-k] * pow( freq[i-k]/freq[j], m_aosys->atm.alpha(layer_i)+2); //seventeen_thirds<realT>());
388  }
389  PSD[j] /= 50.0;
390  ++j;
391  ++i;
392  if(j == freq.size()) return 0;
393  while(j < freq.size())
394  {
395  PSD[j] = PSD[i-1] * pow( freq[i-1]/freq[j], m_aosys->atm.alpha(layer_i)+2); //seventeen_thirds<realT>());
396  ++j;
397  }
398 */
399 
400  return 0;
401 }
402 
403 
404 template<typename realT, typename aosysT>
405 int zernikeTemporalPSD<realT, aosysT>::m_multiLayerPSD( std::vector<realT> & PSD,
406  std::vector<realT> & freq,
407  int zern_j,
408  realT fmax,
409  isParallel<true> parallel )
410 {
411  static_cast<void>(parallel);
412 
413  #pragma omp parallel
414  {
415  //Records each layer PSD
416  std::vector<realT> single_PSD(freq.size());
417 
418  #pragma omp for
419  for(size_t i=0; i< m_aosys->atm.n_layers(); ++i)
420  {
421  singleLayerPSD(single_PSD, freq, zern_j, i, fmax);
422 
423  //Now add the single layer PSD to the overall PSD, weighted by Cn2
424  #pragma omp critical
425  for(size_t j=0; j<freq.size(); ++j)
426  {
427  PSD[j] += m_aosys->atm.layer_Cn2(i)*single_PSD[j];
428  }
429  }
430  }
431 
432  return 0;
433 }
434 
435 template<typename realT, typename aosysT>
436 int zernikeTemporalPSD<realT, aosysT>::m_multiLayerPSD( std::vector<realT> & PSD,
437  std::vector<realT> & freq,
438  int zern_j,
439  realT fmax,
440  isParallel<false> parallel )
441 {
442  static_cast<void>(parallel);
443 
444  //Records each layer PSD
445  std::vector<realT> single_PSD(freq.size());
446 
447  for(size_t i=0; i< m_aosys->atm.n_layers(); ++i)
448  {
449  singleLayerPSD(single_PSD, freq, zern_j, i, fmax);
450 
451  //Now add the single layer PSD to the overall PSD, weighted by Cn2
452  for(size_t j=0; j<freq.size(); ++j)
453  {
454  PSD[j] += m_aosys->atm.layer_Cn2(i)*single_PSD[j];
455  }
456  }
457 
458  return 0;
459 }
460 
461 
462 template<typename realT, typename aosysT>
463 template<bool parallel>
465  std::vector<realT> & freq,
466  int zern_j,
467  realT fmax )
468 {
469  //PSD is zeroed every time to make sure we don't accumulate on repeated calls
470  for(size_t j=0;j<PSD.size(); ++j) PSD[j] = 0;
471 
472  /*if(fmax == 0)
473  {
474  fmax = 150 + 2*fastestPeak(m, n);
475  }*/
476 
477  return m_multiLayerPSD( PSD, freq, zern_j, fmax, isParallel<parallel>());
478 
479 
480 }
481 
482 
483 
484 
485 
486 ///Worker function for GSL Integration for the basic sin/cos Fourier modes.
487 /** \ingroup mxAOAnalytic
488  */
489 template<typename realT, typename aosysT>
490 realT F_zernike (realT kv, void * params)
491 {
493 
494  realT f = Fp->m_f;
495  realT v_wind = Fp->m_aosys->atm.layer_v_wind(Fp->_layer_i);
496  realT D = Fp->m_aosys->D();
497 
498  realT ku = f/v_wind;
499 
500  realT phi = atan(kv/ku);
501 
502  realT k = sqrt( pow(ku,2) + pow(kv,2) );
503 
504  realT Q2norm = sigproc::zernikeQNorm(k*D/2.0, phi, Fp->m_zern_n, Fp->m_zern_m);
505 
506  realT P = Fp->m_aosys->psd(Fp->m_aosys->atm, Fp->_layer_i, k, Fp->m_aosys->lam_sci(), Fp->m_aosys->lam_wfs(), Fp->m_aosys->secZeta() );
507 
508 
509  return P*Q2norm ;
510 }
511 
512 
513 
514 
515 /*extern template
516 struct zernikeTemporalPSD<float, aoSystem<float, vonKarmanSpectrum<float>, std::ostream>>;*/
517 
518 
519 //extern template
520 //struct zernikeTemporalPSD<double, aoSystem<double, vonKarmanSpectrum<double>, std::ostream>>;
521 
522 /*
523 extern template
524 struct zernikeTemporalPSD<long double, aoSystem<long double, vonKarmanSpectrum<long double>, std::ostream>>;
525 
526 #ifdef HASQUAD
527 extern template
528 struct zernikeTemporalPSD<__float128, aoSystem<__float128, vonKarmanSpectrum<__float128>, std::ostream>>;
529 #endif
530 */
531 
532 } //namespace analysis
533 } //namespace AO
534 } //namespace mx
535 
536 #endif //zernikeTemporalPSD_hpp
Calculate and provide constants related to adaptive optics.
Spatial power spectra used in adaptive optics.
Declares and defines an analytical AO system.
Provides a class to manage closed loop gain linear predictor determination.
Provides a class to manage closed loop gain optimization.
constexpr units::realT k()
Boltzmann Constant.
Definition: constants.hpp:71
realT F_zernike(realT kv, void *params)
Worker function for GSL Integration for the basic sin/cos Fourier modes.
int noll_nm(int &n, int &m, int j)
Get the Zernike coefficients n,m corrresponding the Noll index j.
Definition: zernike.cpp:38
realT zernikeQNorm(realT k, realT phi, int n, int m)
Calculate the square-normed Fourier transform of a Zernike polynomial at position (k,...
Definition: zernike.hpp:569
The mxlib c++ namespace.
Definition: mxError.hpp:107
Calculates the PSD of speckle intensity given a modified Fourier mode amplitude PSD.
Class to manage the calculation of temporal PSDs of the Fourier modes in atmospheric turbulence.
int singleLayerPSD(std::vector< realT > &PSD, std::vector< realT > &freq, int zern_j, int layer_i, realT fmax=0)
Calculate the temporal PSD for a Fourier mode for a single layer.
realT m_spatialFilter
Flag indicating if a spatial filter is applied.
realT m_zern_j
the current mode number
void initialize()
Initialize parameters to default values.
gsl_integration_workspace * _w
Worskspace for the gsl integrators, allocated to WSZ if constructed as worker (with allocate == true)...
realT _relTol
The relative tolerance to use in the GSL integrator.
realT m_cq
The cosine of the wind direction.
aosysT * m_aosys
Pointer to an AO system structure.
int _layer_i
The index of the current layer.
realT m_f
the current temporal frequency0
realT relTol()
Get the current relative tolerance.
int multiLayerPSD(std::vector< realT > &PSD, std::vector< realT > &freq, int zern_j, realT fmax=0)
Calculate the temporal PSD for a Fourier mode in a multi-layer model.
realT m_sq
The sine of the wind direction.
realT absTol()
Get the current absolute tolerance.
realT _absTol
The absolute tolerance to use in the GSL integrator.
A utility to convert a wavefront variance map to an intensity image.
Declares and defines a function to calculate the measurement noise PSD.