mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
zernikeCovariance.hpp
Go to the documentation of this file.
1/** \file zernikeCovariance.hpp
2 * \author Jared R. Males (jaredmales@gmail.com)
3 * \brief Calculation of the modal covariance in the zernike basis.
4 * \ingroup mxAO_files
5 *
6 */
7
8#ifndef zernikeCovariance_hpp
9#define zernikeCovariance_hpp
10
11#include <gsl/gsl_integration.h>
12#include <gsl/gsl_errno.h>
13
14#include "../../math/constants.hpp"
20#include "../../mxlib.hpp"
21#include "../../error/mxExceptionOld.hpp"
25
27
28#include "aoAtmosphere.hpp"
29#include "aoPSDs.hpp"
30#include "aoSystem.hpp"
31#include "varmapToImage.hpp"
32
33namespace mx
34{
35namespace AO
36{
37namespace analysis
38{
39
40/// Structure to manage the zernike mode covariance calculation, passed to integration functions
41/**
42 * \tparam realT a floating point type used for all calculations. As of Dec 2023 must be double due to gsl_integration.
43 * \tparam aosysT the type of the AO system structure
44 */
45template <typename realT, typename aosysT>
47{
48 /// Pointer to an AO system, which contains the relevant spatial PSD of turbulence.
49 aosysT *m_aosys{ nullptr };
50
51 /// The n-index of the unprimed mode.
52 realT m_n;
53
54 /// The m-index of the unprimed mode.
55 realT m_m;
56
57 /// The n-indexof the primed mode, corresponding to the \f$ k_v = n/D \f$ component of spatial frequency.
58 realT m_np;
59
60 /// The m-index of the primed mode, corresponding to the \f$ k_u = m/D \f$ component of spatial frequency.
61 realT m_mp;
62
63 /// Spatial frequency being calculated, passed for use in the integrand worker functions.
64 realT m_k;
65
66 /// Absolute tolerance for the radial integral. Default is 1e-10.
67 realT m_kIntEpsAbs{ 1e-10 };
68
69 /// Relative tolerance for the radial integral. Default is 0, meaning absolute is used.
70 realT m_kIntEpsRel{ 0 };
71
72 /// Absolute tolerance for the azimuthal integral. Default is 1e-10.
73 realT m_phiIntEpsAbs{ 1e-10 };
74
75 /// Relative tolerance for the azimuthal integral. Default is 0, meaning absolute is used.
76 realT m_phiIntEpsRel{ 0 };
77
78 protected:
79 size_t m_workspaceSize{ 1000000 };
80
81 /// Working memory for the azimuthal integral.
82 gsl_integration_workspace *m_workspacePhi{ nullptr };
83
84 /// Working memory for the radial integral.
85 gsl_integration_workspace *m_workspaceK{ nullptr };
86
87 public:
88 /// Constructor
90 {
91 m_workspacePhi = gsl_integration_workspace_alloc( m_workspaceSize );
92 m_workspaceK = gsl_integration_workspace_alloc( m_workspaceSize );
93 }
94
95 /// Destructor
97 {
98 gsl_integration_workspace_free( m_workspacePhi );
99 gsl_integration_workspace_free( m_workspaceK );
100 }
101
102 void workspaceSize( size_t wsz )
103 {
104 gsl_integration_workspace_free( m_workspacePhi );
105 gsl_integration_workspace_free( m_workspaceK );
106
107 m_workspacePhi = gsl_integration_workspace_alloc( m_workspaceSize );
108 m_workspaceK = gsl_integration_workspace_alloc( m_workspaceSize );
109 }
110
111 size_t workspaceSize()
112 {
113 return m_workspaceSize;
114 }
115
116 gsl_integration_workspace *workspacePhi()
117 {
118 return m_workspacePhi;
119 }
120
121 /// Calculate the covariance between the two modes.
122 /** \todo document me
123 * \todo handle gsl errors
124 */
125 realT getCovariance( realT &error )
126 {
127 realT result;
128
129 if( m_aosys == nullptr )
130 {
132 err::paramnotset, "zernikeCovariance::getCovariance", "AO system not setup (aosys is nullptr)" );
133 }
134
135 gsl_function func;
136 func.function = &kInt;
137 func.params = this;
138
139 gsl_set_error_handler_off();
140
141 int ec = gsl_integration_qagiu(
142 &func, 0, m_kIntEpsAbs, m_kIntEpsRel, m_workspaceSize, m_workspaceK, &result, &error );
143
144 return result;
145 }
146
147 /// Worker function for the radial integral in the covariance calculation
148 /**
149 * \returns the value of the integrand at spatial frequency \p k.
150 */
151 static realT kInt( realT k, ///< [in] the spatial frequency at which to evaluate the integrand, meters
152 void *params ///< [in] a pointer to a object of type zernikeCovariance<realT, aosyT>
153 );
154
155 /// Worker for the azimuthal integral (in phi) for the zernike mode covariance.
156 /**
157 * \return the value of the integrand at the angle \p phi
158 */
159 static realT phiInt( realT phi, ///< [in] the angle at which to evaluate the integrand, radians
160 void *params ///< [in] a pointer to a object of type zernikeCovariance<realT, aosyT>
161 );
162};
163
164template <typename realT, typename aosysT>
165realT zernikeCovariance<realT, aosysT>::kInt( realT k, void *params )
166{
168
169 realT result, error;
170
171 gsl_function func;
172
173 func.function = &phiInt;
174
175 func.params = Pp;
176
177 Pp->m_k = k;
178
179 gsl_integration_qag( &func,
180 0.,
182 Pp->m_phiIntEpsAbs,
183 Pp->m_phiIntEpsRel,
184 Pp->workspaceSize(),
185 GSL_INTEG_GAUSS61,
186 Pp->workspacePhi(),
187 &result,
188 &error );
189
190 return result;
191}
192
193template <typename realT, typename aosysT>
194realT zernikeCovariance<realT, aosysT>::phiInt( realT phi, void *params )
195{
197
198 realT D = Pp->m_aosys->D();
199
200 realT k = Pp->m_k;
201
202 /*** no prime ***/
203 realT m = Pp->m_m;
204 realT n = Pp->m_n;
205
206 std::complex<realT> Q_mn = zernikeQ( k * D / 2.0, phi, n, m );
207
208 /*** primed ***/
209 realT mp = Pp->m_mp;
210 realT np = Pp->m_np;
211
212 std::complex<realT> Q_mpnp = zernikeQ( k * D / 2.0, phi, np, mp );
213
214 /*** The product ***/
215 realT QQ = real( conj( Q_mn ) * Q_mpnp );
216
217 realT P = Pp->m_aosys->psd( Pp->m_aosys->atm, 0, k, Pp->m_aosys->lam_sci(), Pp->m_aosys->lam_wfs(), 1.0 );
218
219 return P * k * QQ;
220}
221
222#if 0
223
224///Calculate a vector of zernike mode variances.
225template<typename realT, typename aosysT>
226int zernikeVarVec( const std::string & fname,
227 int N,
228 aosysT & aosys,
229 realT absTol,
230 realT relTol,
231 bool modifed=true
232 )
233{
234
235 std::vector<realT> var(N,0);
236
237 ipc::ompLoopWatcher<> watcher(N, std::cerr);
238
239 realT mnCon = 0;
240 if( aosys.d_min(0) > 0)
241 {
242 mnCon = floor( aosys.D()/aosys.d_min(0)/2.0);
243 }
244
245#pragma omp parallel
246 {
247 zernikeCovariance<realT, aosysT> Pp;
248 Pp.absTol = absTol;
249 Pp.relTol = relTol;
250 Pp.aosys = &aosys;
251
252 Pp.mnCon = mnCon;
253
254 realT result, error;
255
256#pragma omp for schedule( dynamic, 5 )
257 for(int i=0; i< N; ++i)
258 {
259 Pp.p = +1;
260 Pp.m = i+1;
261 Pp.n = 0;
262
263 Pp.pp = +1;
264 Pp.mp = i+1;
265 Pp.np = 0;
266
267 result = Pp.getVariance(error);
268
269 var[i] = result;
270
271 watcher.incrementAndOutputStatus();
272 }
273 }
274
275 std::ofstream fout;
276 fout.open(fname);
277
278 realT D = aosys.D();
279 realT r_0 = aosys.atm.r_0();
280 realT tot = 1.0299*pow(D/r_0, 5./3.);
281
282 realT sum = 0;
283
284 std::cout << 0 << " " << 0 << " " << 0 << " " << tot << "\n";
285 for(int i=0; i<N; ++i)
286 {
287 realT k = (i+1)/D;
288
289 realT P = aosys.psd(aosys.atm, 0, k, 1.0);// aosys.lam_sci(), 0, aosys.lam_wfs(), 1.0);
290
291 if(mnCon > 0 )
292 {
293 if( k*D < mnCon )
294 {
295 P *= pow(math::two_pi<realT>()*aosys.atm.v_wind()* k * (aosys.minTauWFS(0)+aosys.deltaTau()),2);
296 }
297 }
298
299 sum += var[i];
300 fout << i+1 << " " << var[i] << " " << P/pow(D,2) << " " << tot-sum << "\n";
301 }
302
303 fout.close();
304
305 return 0;
306}
307
308///Calculate a map of zernike variances by convolution with the PSD
309/** Uses the Airy pattern for the circularly unobstructed aperture.
310 *
311 * \returns 0 on success
312 * \returns -1 on error
313 */
314template<typename realT, typename aosysT>
315int zernikePSDMap( improc::eigenImage<realT> & var, ///< [out] The variance estimated by convolution with the PSD
316 improc::eigenImage<realT> & psd, ///< [out] the PSD map
317 int N, ///< [in] the number of components to analyze
318 int overSample,
319 aosysT & aosys ///< [in[ the AO system defining the PSD characteristics.
320 )
321{
322 N *= overSample;
323 psd.resize(2*N + 1, 2*N+1);
324
325 realT mnCon = 0;
326 if( aosys.d_min() > 0)
327 {
328 mnCon = floor( aosys.D()/aosys.d_min()/2.0);
329 }
330
331 for(int i=0; i<=N; ++i)
332 {
333 for(int j=-N; j<=N; ++j)
334 {
335
336 realT D = aosys.D();
337 realT k = sqrt( pow(i,2) + pow(j,2))/D/overSample;
338
339 realT P = aosys.psd(aosys.atm, k, aosys.lam_sci(), 0, aosys.lam_wfs(), 1.0);
340
341 if(mnCon > 0 )
342 {
343 if( k*D < mnCon )
344 {
345 P *= pow(math::two_pi<realT>()*aosys.atm.v_wind()* k * (aosys.minTauWFS()+aosys.deltaTau()),2);
346 }
347 }
348
349 psd(N+i, N + j) = P/pow(D*overSample,2);
350 psd(N-i, N - j) = P/pow(D*overSample,2);
351 }
352 }
353
354 //Create Airy PSF for convolution with PSD psd.
355 Eigen::Array<realT, -1,-1> psf;
356 psf.resize(2*N+3,2*N+3);
357 for(int i=0;i<psf.rows();++i)
358 {
359 for(int j=0;j<psf.cols();++j)
360 {
361 psf(i,j) = mx::math::func::airyPattern(sqrt( pow( i-floor(.5*psf.rows()),2) + pow(j-floor(.5*psf.cols()),2))/overSample);
362 }
363 }
364
365 mx::AO::analysis::varmapToImage(var, psd, psf);
366
367 return 0;
368}
369
370template<typename realT>
371int zernikeCovarMap( const std::string & fname, ///< [out] the path where the output FITS file will be written
372 int N, ///< [in] the linear number of zernike modes across the aperture. The Nyquist frequency is set by N/2.
373 realT D,
374 realT L_0,
375 bool subPist,
376 bool subTilt,
377 realT absTol,
378 realT relTol,
379 bool modified=true
380 )
381{
382 std::vector<mx::sigproc::zernikeModeDef> ml;
383 mx::sigproc::makezernikeModeFreqs_Rect(ml, N);
384
385
387
388 aosys.loadMagAOX();
389
390 //This is just a normalization parameter in this context.
391 aosys.atm.r_0(1.0, 0.5e-6);
392
393 aosys.D( D );
394 aosys.atm.L_0( L_0 );
395 aosys.psd.subPiston( subPist );
396 aosys.psd.subTipTilt( subTilt );
397
398 int psz = ml.size();
399
400 Eigen::Array<realT,-1,-1> covar( psz, psz);
401 covar.setZero();
402
403 //int ncalc = 0.5*( psz*psz - psz);
404
405 ipc::ompLoopWatcher<> watcher(psz, std::cout);
406
407 std::cerr << "Starting . . .\n";
408#pragma omp parallel
409 {
411 Pp.absTol = absTol;
412 Pp.relTol = relTol;
413 Pp.aosys = &aosys;
414
415 if(!modified) Pp.useBasic = true;
416
417 realT result, error;
418
419#pragma omp for schedule( static, 5 )
420 for(int i=0; i< psz; ++i)
421 {
422 for(int j=i; j< psz; ++j)
423 {
424 Pp.p = ml[i].p;
425 Pp.m = ml[i].m;
426 Pp.n = ml[i].n;
427
428 Pp.pp = ml[j].p;
429 Pp.mp = ml[j].m;
430 Pp.np = ml[j].n;
431 result = Pp.getVariance(error);
432
433 covar(i,j) = result;
434
435 }
436 watcher.incrementAndOutputStatus();
437 }
438 }
439
440 fits::fitsHeader head;
441 head.append("DIAMETER", aosys.D(), "Diameter in meters");
442 head.append("NSUBAP", N, "Linear number of s.f. sampled");
443 head.append("L0", aosys.atm.L_0(0), "Outer scale (L_0) in meters");
444 head.append("SUBPIST", aosys.psd.subPiston(), "Piston subtractioon true/false flag");
445 head.append("SUBTILT", aosys.psd.subTipTilt(), "Tip/Tilt subtractioon true/false flag");
446 head.append("ABSTOL", absTol, "Absolute tolerance in qagiu");
447 head.append("RELTOL", relTol, "Relative tolerance in qagiu");
448
449 fitsHeaderGitStatus( head, "mxlib", mxlib_comp_current_sha1(), mxlib_comp_repo_modified() );
450
451 fits::fitsFile<realT> ff;
452 ff.write(fname + ".fits", covar, head);
453
454 return 0;
455}
456
457
458
459
460template<typename realT, typename aosysT>
461int zernikeCovarMapSeparated( const std::string & fname,
462 int N,
463 aosysT & aosys,
464 realT absTol,
465 realT relTol,
466 bool modified=true)
467{
468 std::vector<mx::sigproc::zernikeModeDef> ml;
469 mx::sigproc::makezernikeModeFreqs_Rect(ml, N);
470
471 int psz = 0.5*ml.size();
472
473 Eigen::Array<realT,-1,-1> covar_pp( (int) (0.5*psz), (int)(.5*psz)), covar_ppp( (int) (0.5*psz), (int)(0.5*psz));
474 covar_pp.setZero();
475 covar_ppp.setZero();
476
477 realT mnCon = 0;
478 if( aosys.d_min() > 0)
479 {
480 mnCon = floor( aosys.D()/aosys.d_min()/2.0);
481 }
482
483 ipc::ompLoopWatcher<> watcher((psz+1)*0.125*(psz+1)*2, std::cout);
484
485#pragma omp parallel
486 {
487
489 Pp.absTol = absTol;
490 Pp.relTol = relTol;
491 Pp.aosys = &aosys;
492
493 if(!modified) Pp.useBasic = true;
494
495 Pp.mnCon = mnCon;
496
497 realT result, error;
498
499#pragma omp for schedule( dynamic, 5 )
500 for(int i=0; i< psz; i+=2)
501 {
502 for(int j=0; j<= 0.5*i; ++j)
503 {
504 for(int k=0; k< 2; ++k)
505 {
506 Pp.p = ml[i].p;
507 Pp.m = ml[i].m;
508 Pp.n = ml[i].n;
509
510 Pp.pp = ml[2*j + k].p;
511 Pp.mp = ml[2*j + k].m;
512 Pp.np = ml[2*j + k].n;
513 result = Pp.getVariance(error);
514
515 if( Pp.p == Pp.pp)
516 {
517 covar_pp(i/2, j) = result;
518 }
519 else
520 {
521 covar_ppp(i/2, j) = result;
522 }
523 watcher.incrementAndOutputStatus();
524 }
525 }
526 }
527 }
528
529 fits::fitsHeader head;
530 head.append("DIAMETER", aosys.D(), "Diameter in meters");
531 head.append("L0", aosys.atm.L_0(), "Outer scale (L_0) in meters");
532 head.append("SUBPIST", aosys.psd.subPiston(), "Piston subtractioon true/false flag");
533 head.append("SUBTILT", aosys.psd.subTipTilt(), "Tip/Tilt subtractioon true/false flag");
534 head.append("ABSTOL", absTol, "Absolute tolerance in qagiu");
535 head.append("RELTOL", relTol, "Relative tolerance in qagiu");
536
537 fitsHeaderGitStatus( head, "mxlib", mxlib_comp_current_sha1(), mxlib_comp_repo_modified() );
538
539 fits::fitsFile<realT> ff;
540 ff.write(fname + "_pp.fits", covar_pp, head);
541 ff.write(fname + "_ppp.fits", covar_ppp, head);
542
543 return 0;
544}
545
546template<typename realT>
547void calcKLCoeffs( const std::string & outFile,
548 const std::string & cvFile )
549{
550 fits::fitsFile<realT> ff;
551
552 Eigen::Array<realT,-1,-1> cvT, cv, evecs, evals;
553
554 ff.read(cv, cvFile);
555
556 //cvT = cv.block(0,0, 1000,1000);//.transpose();
557
558 std::cerr << cvT.rows() << " " << cvT.cols() << "\n";
559 std::cerr << "1\n";
560 math::syevrMem<double> mem;
561
562 double t0 = sys::get_curr_time();
563
564 int info = math::eigenSYEVR<double,double>(evecs, evals, cv, 0, -1, 'U', &mem);
565
566 double t1 = sys::get_curr_time();
567
568 std::cerr << "2\n";
569
570 if(info !=0 )
571 {
572 std::cerr << "info =" << info << "\n";
573 exit(0);
574 }
575
576 std::cerr << "Time = " << t1-t0 << " secs\n";
577
578 //Normalize the eigenvectors
579 for(int i=0;i< evecs.cols(); ++i)
580 {
581 evecs.col(i) = evecs.col(i)/sqrt(fabs(evals(i)));
582 }
583
584 ff.write(outFile, evecs);
585}
586
587template<typename eigenArrT1, typename eigenArrT2, typename eigenArrT3>
588void makeKL( eigenArrT1 & kl,
589 eigenArrT2 & evecs,
590 eigenArrT3 && rvecs )
591{
592
593 int tNims = evecs.rows();
594 int tNpix = rvecs.rows();
595
596 int n_modes = tNims;
597
598 //Now calculate KL images
599 /*
600 * KL = E^T * R ==> C = A^T * B
601 */
602 math::gemm<typename eigenArrT1::Scalar>(CblasColMajor, CblasTrans, CblasTrans, n_modes, tNpix,
603 tNims, 1., evecs.data(), evecs.rows(), rvecs.data(), rvecs.rows(),
604 0., kl.data(), kl.rows());
605
606}
607
608template<typename realT>
609void makeFKL( const std::string & outFile,
610 const std::string & coeffs,
611 int N,
612 int pupSize )
613{
614 fits::fitsFile<realT> ff;
615 Eigen::Array<realT, -1, -1> evecs;
616
617 ff.read(evecs, coeffs);
618
619 improc::eigenCube<realT> Rims;
620 sigproc::makezernikeBasis_Rect(Rims, pupSize, N, MX_zernike_MODIFIED);
621
622 std::cout << Rims.planes() << " " << evecs.cols() << "\n";
623
624 Eigen::Array<realT,-1,-1> kl;
625 kl.resize( Rims.planes(), Rims.rows()*Rims.cols());
626
627 std::cerr << 1 << "\n";
628 makeKL( kl, evecs, Rims.cube());
629 std::cerr << 2 << "\n";
630
631 Eigen::Array<realT,-1,-1> klT = kl.transpose();
632 //kl.resize(0,0);
633 //Rims.resize(0,0);
634 //evecs.resize(0,0);
635
636 improc::eigenCube<realT> klims(klT.data(), Rims.rows(), Rims.cols(), Rims.planes());
637
638 improc::eigenCube<realT> klimsR;
639 klimsR.resize( klims.rows(), klims.cols(), klims.planes());
640
641 for(int i=0; i< klims.planes(); ++i)
642 {
643 klimsR.image(i) = klims.image(klims.planes()-1-i);
644 }
645
646 ff.write(outFile, klimsR);
647 std::cerr << 3 << "\n";
648}
649
650#endif
651
652} // namespace analysis
653} // namespace AO
654} // namespace mx
655
656#endif // zernikeCovariance_hpp
Utilities related to the Airy pattern point spread function.
Provides a class to specify atmosphere parameters.
Spatial power spectra used in adaptive optics.
Declares and defines an analytical AO system.
Describes an analytic adaptive optics (AO) system.
Definition aoSystem.hpp:64
void loadMagAOX()
Load parameters corresponding to the MagAO-X system.
mxException for parameters which aren't set
A class to track the number of iterations in an OMP parallelized loop.
An image cube with an Eigen API.
Tools for using the eigen library for image processing.
Interfaces to Lapack and BLAS for Eigen-like arrays.
Declares and defines a class to work with a FITS file.
@ modified
The modified Fourier basis from males_guyon_2018.
constexpr units::realT k()
Boltzmann Constant.
Definition constants.hpp:69
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
MXLAPACK_INT eigenSYEVR(arrT &eigvec, arrT &eigval, arrT &X, int ev0=0, int ev1=-1, char UPLO='L', syevrMem< typename arrT::Scalar > *mem=0)
Calculate select eigenvalues and eigenvectors of an Eigen Array.
@ error
A general error has occurred.
Definition error_t.hpp:28
#define mxThrowException(extype, src, expl)
Throw an exception. This macro takes care of the file and line.
void fitsHeaderGitStatus(fitsHeaderT &head, const std::string &repoName, const char *sha1, int modified)
Write the status of a Git repository to HISTORY in a FITS header.
realT airyPattern(realT x)
The classical Airy pattern.
constexpr T two_pi()
Get the value of 2pi.
void varmapToImage(imageT &im, imageT &varmap, imageT &psf)
Convert a wavefront variance map to an intensity image by convolving with the PSF.
void gemm(const CBLAS_ORDER Order, const CBLAS_TRANSPOSE TransA, const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K, const dataT &alpha, const dataT *A, const int lda, const dataT *B, const int ldb, const dataT &beta, dataT *C, const int ldc)
Template Wrapper for cblas xGEMM.
typeT get_curr_time()
Get the current system time in seconds.
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.
Structure to manage the zernike mode covariance calculation, passed to integration functions.
realT m_kIntEpsAbs
Absolute tolerance for the radial integral. Default is 1e-10.
aosysT * m_aosys
Pointer to an AO system, which contains the relevant spatial PSD of turbulence.
realT m_k
Spatial frequency being calculated, passed for use in the integrand worker functions.
realT m_m
The m-index of the unprimed mode.
realT m_mp
The m-index of the primed mode, corresponding to the component of spatial frequency.
realT m_np
The n-indexof the primed mode, corresponding to the component of spatial frequency.
gsl_integration_workspace * m_workspacePhi
Working memory for the azimuthal integral.
realT m_phiIntEpsRel
Relative tolerance for the azimuthal integral. Default is 0, meaning absolute is used.
static realT phiInt(realT phi, void *params)
Worker for the azimuthal integral (in phi) for the zernike mode covariance.
realT getCovariance(realT &error)
Calculate the covariance between the two modes.
realT m_n
The n-index of the unprimed mode.
realT m_phiIntEpsAbs
Absolute tolerance for the azimuthal integral. Default is 1e-10.
gsl_integration_workspace * m_workspaceK
Working memory for the radial integral.
realT m_kIntEpsRel
Relative tolerance for the radial integral. Default is 0, meaning absolute is used.
static realT kInt(realT k, void *params)
Worker function for the radial integral in the covariance calculation.
Utilities for working with time.
A utility to convert a wavefront variance map to an intensity image.
Working with the Zernike polynomials.