mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
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 mxAO_analysis_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 "../../mxlib.hpp"
42#include "../../math/constants.hpp"
52
54
55#include "aoSystem.hpp"
56#include "aoPSDs.hpp"
57#include "wfsNoisePSD.hpp"
59#include "clGainOpt.hpp"
60#include "varmapToImage.hpp"
61#include "speckleAmpPSD.hpp"
62
63#include "aoConstants.hpp"
64
65namespace mx
66{
67namespace AO
68{
69namespace analysis
70{
71
72#ifndef WSZ
73
74/** \def WSZ
75 * \brief Size of the GSL integration workspace
76 */
77#define WSZ 100000
78
79#endif
80
81// Forward declaration
82template <typename realT, typename aosysT>
83realT F_zernike( realT kv, void *params );
84
85/// Class to manage the calculation of temporal PSDs of the Fourier modes in atmospheric turbulence.
86/** Works with both basic (sines/cosines) and modified Fourier modes.
87 *
88 * \tparam realT is a real floating point type for calculations. Currently must be double due to gsl_integration.
89 * \tparam aosysT is an AO system type, usually of type ao_system.
90 *
91 * \todo Split off the integration parameters in a separate structure.
92 * \todo once integration parameters are in a separate structure, make this a class with protected members.
93 * \todo GSL error handling
94 *
95 * \ingroup mxAOAnalytic
96 */
97template <typename _realT, typename aosysT>
99{
100 typedef _realT realT;
101 typedef std::complex<realT> complexT;
102
103 /// Pointer to an AO system structure.
104 aosysT *m_aosys{ nullptr };
105
106 protected:
107 realT m_apertureRatio{ 1 }; /// Aperture ratio, < 1, used for segments.
108 realT m_apertureRatio4{ 1 }; /// Aperture ratio ^4 used for calculations
109
110 public:
111 void apertureRatio( realT ar )
112 {
113 m_apertureRatio = ar;
114 m_apertureRatio4 = pow( ar, 4 );
115 }
116
117 realT apertureRatio4()
118 {
119 return m_apertureRatio4;
120 }
121
122 realT m_f{ 0 }; ///< the current temporal frequency
123 realT m_zern_j{ 0 }; ///< the current mode number
124 int m_zern_m{ 0 }; ///< The current mode m
125 int m_zern_n{ 0 }; ///< The current mode n
126
127 realT m_cq{ 0 }; ///< The cosine of the wind direction
128 realT m_sq{ 0 }; ///< The sine of the wind direction
129 realT m_spatialFilter{ false }; ///< Flag indicating if a spatial filter is applied
130
131 int _layer_i; ///< The index of the current layer.
132
133 /// Worskspace for the gsl integrators, allocated to WSZ if constructed as worker (with allocate == true).
134 gsl_integration_workspace *_w;
135
136 realT _absTol; ///< The absolute tolerance to use in the GSL integrator
137 realT _relTol; ///< The relative tolerance to use in the GSL integrator
138
139 std::vector<realT> Jps;
140 std::vector<realT> Jms;
141 std::vector<int> ps;
142 std::vector<realT> ms;
143 std::vector<realT> ns;
144
145 public:
146 /// Default c'tor
148
149 /// Constructor with workspace allocation
150 /**
151 * \param allocate if true, then the workspace for GSL integrators is allocated.
152 */
153 explicit zernikeTemporalPSD( bool allocate );
154
155 /// Destructor
156 /** Frees GSL workspace if it was allocated.
157 */
159
160 protected:
161 /// Initialize parameters to default values.
162 void initialize();
163
164 public:
165 /** \name GSL Integration Tolerances
166 * For good results it seems that absolute tolerance (absTol) needs to be 1e-10. Lower tolerances cause some
167 * frequencies to drop out, etc. Relative tolerance (relTol) seems to be less sensitive, and 1e-4 works on cases
168 * tested as of 1 Jan, 2017.
169 *
170 * See the documentation for the GSL Library integrators at
171 * (https://www.gnu.org/software/gsl/manual/htmlm_node/QAGI-adaptive-integration-on-infinite-intervals.html)
172 * @{
173 */
174
175 /// Set absolute tolerance
176 /**
177 * \param at is the new absolute tolerance.
178 */
179 void absTol( realT at );
180
181 /// Get the current absolute tolerance
182 /**
183 * \returns _absTol
184 */
185 realT absTol();
186
187 /// Set relative tolerance
188 /**
189 * \param rt is the new relative tolerance.
190 */
191 void relTol( realT rt );
192
193 /// Get the current relative tolerance
194 /**
195 * \returns _relTol
196 */
197 realT relTol();
198
199 ///@}
200
201 /// Calculate the temporal PSD for a Zernike mode for a single layer.
202 /**
203 *
204 * \todo implement error checking.
205 * \todo need a way to track convergence failures in integral without throwing an error.
206 * \todo need better handling of averaging for the -17/3 extension.
207 *
208 */
209 int singleLayerPSD( std::vector<realT> &PSD, ///< [out] the calculated PSD
210 std::vector<realT> &freq, ///< [in] the populated temporal frequency grid defining the
211 ///< frequencies at which the PSD is calculated
212 int zern_j, ///< [in]
213 int layer_i, ///< [in] the index of the layer, for accessing the atmosphere parameters
214 realT fmax = 0 ///< [in] [optional] set the maximum temporal frequency for the calculation. The
215 ///< PSD is filled in with a -17/3 power law past this frequency.
216 );
217
218 ///\cond multilayerm_parallel
219 // Conditional to exclude from Doxygen.
220
221 protected:
222 // Type to allow overloading of the multiLayerPSD workers based on whether they are parallelized or not.
223 template <bool m_parallel>
224 struct isParallel
225 {
226 };
227
228 // Parallelized version of multiLayerPSD, with OMP directives.
229 int m_multiLayerPSD(
230 std::vector<realT> &PSD, std::vector<realT> &freq, int zern_j, realT fmax, isParallel<true> parallel );
231
232 // Non-Parallelized version of multiLayerPSD, without OMP directives.
233 int m_multiLayerPSD(
234 std::vector<realT> &PSD, std::vector<realT> &freq, int zern_j, realT fmax, isParallel<false> parallel );
235
236 ///\endcond
237
238 public:
239 /// Calculate the temporal PSD for a Fourier mode in a multi-layer model.
240 /**
241 *
242 * \tparam parallel controls whether layers are calculated in parallel. Default is true. Set to false if this is
243 * called inside a parallelized loop.
244 *
245 * \todo implement error checking
246 * \todo handle reports of convergence failures form singleLayerPSD when implemented.
247 *
248 */
249 template <bool parallel = true>
250 int multiLayerPSD(
251 std::vector<realT> &PSD, ///< [out] the calculated PSD
252 std::vector<realT>
253 &freq, ///< [in] the populated temporal frequency grid defining at which frequencies the PSD is calculated
254 int zern_j,
255 realT fmax =
256 0 ///< [in] [optional] set the maximum temporal frequency for the calculation. The PSD is filled in
257 /// with a -17/3 power law past this frequency. If 0, then it is taken to be 150 Hz + 2*fastestPeak(m,n).
258 );
259};
260
261template <typename realT, typename aosysT>
267
268template <typename realT, typename aosysT>
270{
271 m_aosys = nullptr;
272 initialize();
273
274 if( allocate )
275 {
276 _w = gsl_integration_workspace_alloc( WSZ );
277 }
278}
279
280template <typename realT, typename aosysT>
282{
283 if( _w )
284 {
285 gsl_integration_workspace_free( _w );
286 }
287}
288
289template <typename realT, typename aosysT>
291{
292 _w = 0;
293
294 _absTol = 1e-10;
295 _relTol = 1e-4;
296}
297
298template <typename realT, typename aosysT>
300{
301 _absTol = at;
302}
303
304template <typename realT, typename aosysT>
309
310template <typename realT, typename aosysT>
312{
313 _relTol = rt;
314}
315
316template <typename realT, typename aosysT>
321
322template <typename realT, typename aosysT>
324 std::vector<realT> &PSD, std::vector<realT> &freq, int zern_j, int layer_i, realT fmax )
325{
326 if( fmax == 0 )
327 fmax = freq[freq.size() - 1];
328
329 realT v_wind = m_aosys->atm.layer_v_wind( layer_i );
330 realT q_wind = m_aosys->atm.layer_dir( layer_i );
331
332 // Rotate the basis
333 realT cq = cos( q_wind );
334 realT sq = sin( q_wind );
335
336 realT scale = 2 * ( 1 / v_wind ); // Factor of 2 for negative frequencies.
337
338 // We'll get the occasional failure to reach tolerance error, just ignore them all for now.
339 gsl_set_error_handler_off();
340
341 // Create a local instance so that we're reentrant
343
344 params.m_aosys = m_aosys;
345 params.m_apertureRatio = m_apertureRatio;
347 params._layer_i = layer_i;
348 params.m_zern_j = zern_j;
349 sigproc::noll_nm( params.m_zern_n, params.m_zern_m, params.m_zern_j );
350 params.m_cq = cq; // for de-rotating ku and kv for spatial filtering
351 params.m_sq = sq; // for de-rotation ku and kv for spatial filtering
352 if( m_aosys->spatialFilter_ku() < std::numeric_limits<realT>::max() ||
353 m_aosys->spatialFilter_kv() < std::numeric_limits<realT>::max() )
354 params.m_spatialFilter = true;
355
356 realT result, error;
357
358 // Setup the GSL calculation
359 gsl_function func;
360 func.function = &F_zernike<realT, aosysT>;
361
362 func.params = &params;
363
364 // Here we only calculate up to fmax.
365 size_t i = 0;
366 while( freq[i] <= fmax )
367 {
368 params.m_f = freq[i];
369
370 int ec = gsl_integration_qagi( &func, _absTol, _relTol, WSZ, params._w, &result, &error );
371
372 if( ec == GSL_EDIVERGE )
373 {
374 std::cerr << "GSL_EDIVERGE:" << " " << freq[i] << " " << v_wind << " " << zern_j << "\n";
375 std::cerr << "ignoring . . .\n";
376 }
377
378 PSD[i] = scale * result;
379
380 ++i;
381 if( i >= freq.size() )
382 break;
383 }
384 /*
385 //Now fill in from fmax to the actual max frequency with a -(alpha+2) power law.
386 size_t j=i;
387
388 if(j == freq.size()) return 0;
389
390 //First average result for last 50.
391 PSD[j] = PSD[i-50] * pow( freq[i-50]/freq[j], m_aosys->atm.alpha(layer_i)+2);//seventeen_thirds<realT>());
392 for(size_t k=49; k> 0; --k)
393 {
394 PSD[j] += PSD[i-k] * pow( freq[i-k]/freq[j], m_aosys->atm.alpha(layer_i)+2); //seventeen_thirds<realT>());
395 }
396 PSD[j] /= 50.0;
397 ++j;
398 ++i;
399 if(j == freq.size()) return 0;
400 while(j < freq.size())
401 {
402 PSD[j] = PSD[i-1] * pow( freq[i-1]/freq[j], m_aosys->atm.alpha(layer_i)+2); //seventeen_thirds<realT>());
403 ++j;
404 }
405 */
406
407 return 0;
408}
409
410template <typename realT, typename aosysT>
412 std::vector<realT> &PSD, std::vector<realT> &freq, int zern_j, realT fmax, isParallel<true> parallel )
413{
414 static_cast<void>( parallel );
415
416#pragma omp parallel
417 {
418 // Records each layer PSD
419 std::vector<realT> single_PSD( freq.size() );
420
421#pragma omp for
422 for( size_t i = 0; i < m_aosys->atm.n_layers(); ++i )
423 {
424 singleLayerPSD( single_PSD, freq, zern_j, i, fmax );
425
426// Now add the single layer PSD to the overall PSD, weighted by Cn2
427#pragma omp critical
428 for( size_t j = 0; j < freq.size(); ++j )
429 {
430 PSD[j] += m_aosys->atm.layer_Cn2( i ) * single_PSD[j];
431 }
432 }
433 }
434
435 return 0;
436}
437
438template <typename realT, typename aosysT>
440 std::vector<realT> &PSD, std::vector<realT> &freq, int zern_j, realT fmax, 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
461template <typename realT, typename aosysT>
462template <bool parallel>
464 std::vector<realT> &freq,
465 int zern_j,
466 realT fmax )
467{
468 // PSD is zeroed every time to make sure we don't accumulate on repeated calls
469 for( size_t j = 0; j < PSD.size(); ++j )
470 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/// Worker function for GSL Integration for the Zernike modes.
481/** \ingroup mxAOAnalytic
482 */
483template <typename realT, typename aosysT>
484realT F_zernike( realT kv, void *params )
485{
487
488 realT f = Fp->m_f;
489 realT v_wind = Fp->m_aosys->atm.layer_v_wind( Fp->_layer_i );
490 realT D = Fp->m_aosys->D();
491
492 realT apertureRatio4 = Fp->apertureRatio4();
493
494 realT ku = f / v_wind;
495
496 realT phi = atan( kv / ku );
497
498 realT k = sqrt( pow( ku, 2 ) + pow( kv, 2 ) );
499
500 realT Q2norm = apertureRatio4 * sigproc::zernikeQNorm( k * D / 2.0, phi, Fp->m_zern_n, Fp->m_zern_m );
501
502 realT P = Fp->m_aosys->psd(
503 Fp->m_aosys->atm, Fp->_layer_i, k, Fp->m_aosys->lam_sci(), Fp->m_aosys->lam_wfs(), Fp->m_aosys->secZeta() );
504
505 return P * Q2norm;
506}
507
508/*extern template
509struct zernikeTemporalPSD<float, aoSystem<float, vonKarmanSpectrum<float>, std::ostream>>;*/
510
511// extern template
512// struct zernikeTemporalPSD<double, aoSystem<double, vonKarmanSpectrum<double>, std::ostream>>;
513
514/*
515extern template
516struct zernikeTemporalPSD<long double, aoSystem<long double, vonKarmanSpectrum<long double>, std::ostream>>;
517
518#ifdef HASQUAD
519extern template
520struct zernikeTemporalPSD<__float128, aoSystem<__float128, vonKarmanSpectrum<__float128>, std::ostream>>;
521#endif
522*/
523
524} // namespace analysis
525} // namespace AO
526} // namespace mx
527
528#endif // zernikeTemporalPSD_hpp
Utilities related to the Airy pattern point spread function.
Calculate and provide constants related to adaptive optics.
Spatial power spectra used in adaptive optics.
Declares and defines an analytical AO system.
A utility to read/write vectors of data from/to a binary file.
Provides a class to manage closed loop gain linear predictor determination.
Provides a class to manage closed loop gain optimization.
Declarations of utilities for working with files.
Declares and defines a class to work with a FITS file.
#define WSZ
The size of the gsl_integration workspaces.
@ error
A general error has occurred.
Definition error_t.hpp:28
realT F_zernike(realT kv, void *params)
Worker function for GSL Integration for the Zernike modes.
int noll_nm(int &n, int &m, int j)
Get the Zernike coefficients n,m corrresponding the Noll index j.
Definition zernike.cpp:35
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:598
Declares and defines the Jinc and Jinc2 functions.
Declarations of some libarary wide utilities.
The mxlib c++ namespace.
Definition mxlib.hpp:37
Track iterations in an OMP parallelized looop.
A utility to read in columns from a text file.
Calculates the PSD of speckle intensity given a modified Fourier mode amplitude PSD.
Utilities for working with strings.
Class to manage the calculation of temporal PSDs of the Fourier modes in atmospheric turbulence.
void apertureRatio(realT ar)
Aperture ratio ^4 used for calculations.
realT m_apertureRatio4
Aperture ratio, < 1, used for segments.
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 Zernike 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 frequency
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.
Header for the std::vector utilities.
Declares and defines a function to calculate the measurement noise PSD.
Working with the Zernike polynomials.