mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
basis.hpp
Go to the documentation of this file.
1 /** \file basis.hpp
2  * \author Jared R. Males (jaredmales@gmail.com)
3  * \brief Utilities for working with a modal basis.
4  * \ingroup mxAO_files
5  *
6  */
7 
8 #ifndef __basis_hpp__
9 #define __basis_hpp__
10 
11 #include "../ioutils/fits/fitsFile.hpp"
12 using namespace mx::fits;
13 
14 #include "../sigproc/fourierModes.hpp"
15 #include "../sigproc/gramSchmidt.hpp"
16 //#include "../improc/eigenUtils.hpp"
17 #include "../improc/imageFilters.hpp"
18 #include "../improc/imagePads.hpp"
19 #include "../improc/eigenCube.hpp"
20 
21 #include "../sigproc/signalWindows.hpp"
22 using namespace mx::improc;
23 using namespace mx::sigproc;
24 
25 #include "../math/eigenLapack.hpp"
26 using namespace mx::math;
27 
28 #include "aoPaths.hpp"
29 
30 namespace mx
31 {
32 
33 namespace AO
34 {
35 
36 ///Multiply a raw modal basis by a pupil mask.
37 template<typename realT>
39  const std::string & basisName,
40  const std::string & pupilName,
41  realT fwhm = 0 )
42 {
43  fitsFile<realT> ff;
44 
45  std::string rawFName = mx::AO::path::basis::modes(basisName);
46  ff.read(modes, rawFName);
47 
48  std::string pupilFName = mx::AO::path::pupil::pupilFile(pupilName);
49  Eigen::Array<realT, -1, -1> pupil;
50  ff.read(pupil, pupilFName);
51 
52  Eigen::Array<realT, -1, -1> im, pim, fim;
53 
54  for(int i=0; i< modes.planes(); ++i)
55  {
56 
57  modes.image(i) = modes.image(i) * pupil;
58 
59  if(fwhm > 0)
60  {
61  im = modes.image(i);
62 
63  padImage(pim, im, pupil,3);
64 
65  filterImage(fim, pim, gaussKernel<Eigen::Array<realT, -1, -1>>(fwhm));
66 
67  modes.image(i) += fim* (pupil - 1).abs();
68  }
69 
70  }
71 
72  //std::string procFName = mx::AO::path::basis::pupilModes(basisName, pupilName, true);
73 
74  //mx::fitsHeader head;
75  //head.append( "FWHM", fwhm, "FWHM of smoothing kernel used for slaving");
76  //ff.write(procFName, rawModes, head);
77 
78 }
79 
80 /** \def MXAO_ORTHO_METHOD_SGS
81  * \brief Constant to specify using the stabilized Gramm Schmidt (SGS) orthogonalization procedure.
82  */
83 #define MXAO_ORTHO_METHOD_SGS 0
84 
85 /** \def MXAO_ORTHO_METHOD_SVD
86  * \brief Constant to specify using the singular value decomposition (SVD) for orthogonalizing a modal basis.
87  */
88 #define MXAO_ORTHO_METHOD_SVD 1
89 
90 
91 template<typename realT, typename spectRealT>
92 int orthogonalizeBasis( eigenCube<realT> & ortho,
93  Eigen::Array<spectRealT, -1, -1> & spect,
94  eigenCube<realT> & modes,
95  int method,
96  realT psum = 1.0
97  )
98 {
99 
100 
101  if(method == MXAO_ORTHO_METHOD_SGS)
102  {
103  ortho.resize(modes.rows(), modes.cols(), modes.planes());
104  Eigen::Map< Eigen::Array<realT, -1, -1>> gsout(ortho.data(), ortho.rows()*ortho.cols(), ortho.planes());
105 
106  gramSchmidtSpectrum<1>(gsout, spect, modes.asVectors(), psum);
107 
108  }
109  else if (method == MXAO_ORTHO_METHOD_SVD)
110  {
111  Eigen::Array<realT, -1, -1> U, S, VT, A;
112  int info;
113  A = modes.asVectors();
114  info = eigenGESDD(U,S,VT,A);
115 
116  if(info != 0)
117  {
118  std::cerr << "Some error occurred in SVD\n";
119  return -1;
120  }
121 
122  //This maps to a cube, but does not copy or take ownership.
123 
124  eigenCube<realT> omodes( U.data(), modes.rows(), modes.cols(), modes.planes());
125 
126  ortho.resize(modes.rows(), modes.cols(), modes.planes());
127  ortho = omodes;
128 
129  }
130  else
131  {
132  std::cerr << "Invalid orthogonalization method.\n";
133  return -1;
134  }
135 
136 
137  return 0;
138 
139 }
140 
141 ///Calculate an orthogonal projection of a basis on a pupil.
142 /** Can use either the stabilized Gramm Schmidt (SGS) procedure, or Singular Value Decomposition (SVD).
143  * This calls \ref applyPupil2Basis as a first step..
144  *
145  * \param [in] basisName the name of the basis to orthogonalize
146  * \param [in] pupilName the name of the pupil on which to orthogonalize.
147  * \param [in] method either \ref MXAO_ORTHO_METHOD_SGS (for SGS) or \ref MXAO_ORTHO_METHOD_SVD (for SVD)
148  *
149  * \tparam realT
150  */
151 template<typename realT>
152 void orthogonalizeBasis( const std::string & orthoName,
153  const std::string & basisName,
154  const std::string & pupilName,
155  int method )
156 {
157  fitsFile<realT> ff;
158  fitsHeader head;
159 
160 
161  eigenCube<realT> modes;
162  applyPupil2Basis(modes, basisName, pupilName);
163 
164  eigenCube<realT> ortho;
165  Eigen::Array<realT, -1, -1> spect;
166 
167  orthogonalizeBasis(ortho, spect, modes, method);
168 
169  modes.resize(1,1,1);
170 
171  std::string pupilFName = mx::AO::path::pupil::pupilFile(pupilName);
172  Eigen::Array<realT, -1, -1> pupil;
173  ff.read(pupil, pupilFName);
174 
175  realT psum = pupil.sum();
176  realT norm;
177 
178  for(int i=0; i< ortho.planes(); ++i)
179  {
180  norm = ortho.image(i).square().sum()/psum;
181  ortho.image(i)/= sqrt(norm);
182 
183  if(method == MXAO_ORTHO_METHOD_SGS)
184  {
185  //if(i == 0) std::cout << sqrt(norm) << "\n";
186 
187  //spect.row(i) /= sqrt(norm);
188  }
189  }
190 
191  std::string orthoFName = mx::AO::path::basis::modes(orthoName, true);
192 
193  std::string orthm = "UNK";
194  if(method == MXAO_ORTHO_METHOD_SGS) orthm = "SGS";
195  if(method == MXAO_ORTHO_METHOD_SVD) orthm = "SVD";
196 
197  head.append("ORTHMETH", orthm, "Orthogonalization method.");
198  head.append("ORTHPUP", pupilName, "Pupil used for orthogonalization");
199 
200 
201  ff.write(orthoFName, ortho, head);
202 
203  if(method == MXAO_ORTHO_METHOD_SGS)
204  {
205  std::string orthoFName = mx::AO::path::basis::spectrum(orthoName, true);
206  ff.write(orthoFName, spect, head);
207  }
208 
209 }
210 
211 template<typename spectRealT, typename realT>
212 void normalizeSpectrum( mx::improc::eigenImage<spectRealT> & spectrum,
216  )
217 {
218  int nModes = modes.planes();
219 
220  std::vector<realT> w(nModes);
221 
222  size_t psum = pupil.sum();
223 
224  #pragma omp parallel
225  {
226  std::vector<realT> amps( nModes, 0.0 );
227  std::vector<realT> uwAmps(nModes);
228  std::vector<realT> rat(psum);
229 
230  #pragma omp for
231  for(int k=0; k< nModes; ++k)
232  {
233  amps[k] = 1.0;
234 
235  for(int j=0; j< nModes; ++j)
236  {
237  uwAmps[j] = 0;// amps(j, k)*spectrum(j,j);
238 
239  for(int i= j; i < nModes; ++i)
240  {
241  uwAmps[j] += amps[i]*spectrum(i,j);
242  }
243  }
244 
245  mx::improc::eigenImage<realT> uwp(modes.rows(), modes.cols());
246  uwp.setZero();
247 
248  for(int i=0;i<nModes; ++i) uwp+= uwAmps[i]*modes.image(i);
249 
250  int idx = 0;
251  for(int r=0; r< pupil.rows(); ++r)
252  {
253  for(int c=0;c < pupil.cols(); ++c)
254  {
255  if(pupil(r,c) == 1) rat[idx++] = ( uwp(r,c) / ortho.image(k)(r,c));
256  }
257  }
258 
260 
261  if(k == 1200) std::cout << w[k] << "\n";
262 
263  amps[k] = 0.0;
264  }
265  }
266 
267  for(int i=0; i< nModes; ++i) spectrum.row(i)/= w[i];
268 
269  return;
270 }
271 
272 template<typename realT>
273 int slaveBasis( const std::string & outputBasisN,
274  const std::string & inputBasisN,
275  const std::string & pupilN,
276  const std::string & dmN,
277  realT fwhm,
278  realT fsmooth,
279  int firstMode = 0
280  )
281 {
282  fitsFile<realT> ff;
283  fitsHeader head;
284 
285  //get DM size and clear memory first
286  std::string dmFName = mx::AO::path::dm::influenceFunctions(dmN);
287  eigenCube<realT> inf;
288  ff.read(dmFName, inf);
289 
290  int dmSz = inf.rows();
291 
292  inf.resize(0,0);
293 
294  //Now load basis
295  std::string basisFName = mx::AO::path::basis::modes(inputBasisN);//, pupilName);
296  eigenCube<realT> modes;
297  ff.read(basisFName, modes);
298 
299  //Now load pupil
300  std::string pupilFName = mx::AO::path::pupil::pupilFile(pupilN);
301  Eigen::Array<realT, -1, -1> pupil;
302  ff.read(pupilFName, pupil);
303 
304  Eigen::Array<realT, -1, -1> im, ppim, pim, fim, ppupil;
305 
306  int padw = 2*fwhm;
307 
308  if(fwhm ==0 ) padw = 2*fsmooth;
309 
310  int cutw = 0.5*(dmSz - modes.rows());
311 
312  if( padw < cutw )
313  {
314  padw = cutw;
315  }
316 
317  eigenCube<realT> oModes;
318 
319  oModes.resize( modes.rows()+2*cutw, modes.cols()+2*cutw, modes.planes());
320 
321 
322  if(modes.rows() > pupil.rows())
323  {
324  padImage(ppupil, pupil, 0.5*(modes.rows()-pupil.rows()));
325  pupil = ppupil;
326  }
327 
328 
329  padImage(ppupil, pupil, padw);
330 
331 
332  for(int i=0;i<firstMode; ++i)
333  {
334  im = modes.image(i);
335  padImage(ppim, im, padw);
336  cutPaddedImage(im, ppim, 0.5*ppim.rows() - (0.5*modes.rows()+cutw));
337 
338  oModes.image(i) = im;
339  }
340 
341  for(int i=firstMode; i< modes.planes(); ++i)
342  {
343 
344  if(fwhm > 0 || fsmooth > 0)
345  {
346 
347  im = modes.image(i)*pupil;
348  padImage(ppim, im, padw);
349  padImage(pim, im, ppupil, padw);//ppupil,4);
350 
351 
352  if( fwhm > 0 )
353  {
354  filterImage(fim, pim, gaussKernel<Eigen::Array<realT, -1, -1>>(fwhm));
355  fim = ppim + fim*(ppupil - 1).abs();
356  }
357  else
358  {
359  fim = ppim;
360  }
361 
362  if(fsmooth > 0)
363  {
364  pim = fim;
365 
366  filterImage(fim, pim, gaussKernel<Eigen::Array<realT, -1, -1>>(fsmooth));
367  }
368 
369 
370  cutPaddedImage(im, fim, 0.5*fim.rows() - (0.5*modes.rows()+cutw));
371 
372  oModes.image(i) = im;
373  }
374  }
375 
376  std::string oFName = mx::AO::path::basis::modes(outputBasisN, true);
377  ff.write(oFName, oModes);
378 
379  return 0;
380 }
381 
382 
383 template<typename realT>
384 int apodizeBasis( const std::string & outputBasisN,
385  const std::string & inputBasisN,
386  realT tukeyAlpha,
387  realT centralObs,
388  realT overScan,
389  int firstMode )
390 {
391  fitsFile<realT> ff;
392  fitsHeader head;
393 
394  std::string basisFName = mx::AO::path::basis::modes(inputBasisN);//, pupilName);
395  eigenCube<realT> modes;
396  ff.read(basisFName, modes);
397 
398  realT cen = 0.5*(modes.rows() - 1.0);
399 
400  Eigen::Array<realT, -1, -1> pupil;
401  pupil.resize( modes.rows(), modes.cols());
402 
403  if(centralObs == 0)
404  {
405  window::tukey2d<realT>(pupil.data(), modes.rows(), modes.rows() + overScan, tukeyAlpha, cen,cen);
406  }
407  else
408  {
409  window::tukey2dAnnulus<realT>(pupil.data(), modes.rows(), modes.rows() + overScan, centralObs, tukeyAlpha, cen,cen);
410  }
411 
412 
413 
414  for(int i=firstMode; i< modes.planes(); ++i)
415  {
416  modes.image(i) = modes.image(i)*pupil;
417  }
418 
419  std::string oFName = mx::AO::path::basis::modes(outputBasisN, true);
420  ff.write(oFName, modes);
421 
422  return 0;
423 }
424 
425 
426 template<typename realT>
427 int subtractBasis( const std::string & basisName,
428  const std::string & basisName1,
429  const std::string & basisName2,
430  const std::string & pupilName,
431  int method )
432 {
433  fitsFile<realT> ff;
434  fitsHeader head;
435 
436  std::string basisFName1 = mx::AO::path::basis::modes(basisName1);//, pupilName);
437  eigenCube<realT> modes1;
438  ff.read(basisFName1, modes1);
439 
440  std::string basisFName2 = mx::AO::path::basis::modes(basisName2);//, pupilName);
441  eigenCube<realT> modes2;
442  ff.read(basisFName2, modes2, head);
443  realT fwhm = head["FWHM"].value<realT>();
444 
445  eigenCube<realT> modes;
446  modes.resize( modes1.rows(), modes1.cols(), modes1.planes() + modes2.planes());
447 
448  std::string pupilFName = mx::AO::path::pupil::pupilFile(pupilName);
449  Eigen::Array<realT, -1, -1> pupil;
450  ff.read(pupilFName, pupil);
451 
452  for(int i=0; i < modes1.planes(); ++i)
453  {
454  modes.image(i) = modes1.image(i)*pupil;
455  }
456 
457  for(int i=0; i < modes2.planes(); ++i)
458  {
459  modes.image( modes1.planes() + i) = modes2.image(i)*pupil;
460  }
461 
462  eigenCube<realT> ortho;
463 
464  orthogonalizeBasis(ortho, modes, method);
465 
466  //Now copy out just the modes corresponding to the 2nd basis set.
467  //Apply the gaussian smooth FWHM again.
468  Eigen::Array<realT, -1, -1> im, ppim, pim, fim, ppupil;
469 
470  modes.resize(ortho.rows(), ortho.cols(), modes2.planes());
471 
472  realT fsmooth = 6.0;
473 
474  padImage(ppupil, pupil, 10);
475 
476  for(int i=0; i< modes2.planes(); ++i)
477  {
478  modes.image(i) = ortho.image( modes1.planes() + i)*pupil;
479 
480  if(fwhm > 0)
481  {
482  im = modes.image(i);
483  padImage(ppim, im, 10);
484  padImage(pim, im, 10);//ppupil,4);
485 
486  filterImage(fim, pim, gaussKernel<Eigen::Array<realT, -1, -1>>(fwhm));
487  fim = ppim + fim*(ppupil - 1).abs();
488 
489  if(fsmooth > 0)
490  {
491  pim = fim;
492  filterImage(fim, pim, gaussKernel<Eigen::Array<realT, -1, -1>>(fsmooth));
493  }
494 
495  cutPaddedImage(im, fim, 10);
496  modes.image(i) = im;
497  }
498  }
499 
500 
501 
502  std::string orthoFName = mx::AO::path::basis::modes(basisName, true);
503  ff.write(orthoFName, modes);
504 
505  return 0;
506 
507 }
508 
509 } //namespace mx
510 } //namespace AO
511 
512 
513 #endif //__basis_hpp__
Standardized paths for the mx::AO system.
std::string U(const std::string &sysName, const std::string &dmName, const std::string &wfsName, const std::string &pupilName, const std::string &basisName, const std::string &id, bool create=false)
Path for the system response interaction matrix.
Definition: aoPaths.hpp:474
std::string pupilFile(const std::string &pupilName, bool create=false)
The path for the pupil FITS file.
Definition: aoPaths.hpp:328
std::string influenceFunctions(const std::string &dmName, bool create=false)
The path for the deformable mirror (DM) influence functions.
Definition: aoPaths.hpp:109
void applyPupil2Basis(eigenCube< realT > &modes, const std::string &basisName, const std::string &pupilName, realT fwhm=0)
Multiply a raw modal basis by a pupil mask.
Definition: basis.hpp:38
Class to manage interactions with a FITS file.
Definition: fitsFile.hpp:54
int write(const dataT *im, int d1, int d2, int d3, fitsHeader *head)
Write the contents of a raw array to the FITS file.
Definition: fitsFile.hpp:1301
int read(dataT *data)
Read the contents of the FITS file into an array.
Definition: fitsFile.hpp:828
Class to manage a complete fits header.
Definition: fitsHeader.hpp:52
void append(fitsHeaderCard card)
Append a fitsHeaderCard to the end of the header.
Definition: fitsHeader.cpp:156
Eigen::Map< Eigen::Array< dataT, Eigen::Dynamic, Eigen::Dynamic > > image(Index n)
Returns a 2D Eigen::Eigen::Map pointed at the specified image.
Definition: eigenCube.hpp:376
constexpr units::realT c()
The speed of light.
Definition: constants.hpp:60
constexpr units::realT k()
Boltzmann Constant.
Definition: constants.hpp:71
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
Definition: eigenImage.hpp:44
MXLAPACK_INT eigenGESDD(Eigen::Array< dataT,-1,-1 > &U, Eigen::Array< dataT,-1,-1 > &S, Eigen::Array< dataT,-1,-1 > &VT, Eigen::Array< dataT,-1,-1 > &A)
Compute the SVD of an Eigen::Array using LAPACK's xgesdd.
void filterImage(imageOutT &fim, imageInT im, kernelT kernel, int maxr=0)
Filter an image with a mean kernel.
int padImage(imOutT &imOut, imInT &imIn, unsigned int padSz, typename imOutT::Scalar value)
Pad an image with a constant value.
Definition: imagePads.hpp:57
int cutPaddedImage(imOutT &imOut, const imInT &imIn, unsigned int padSz)
Cut down a padded image.
Definition: imagePads.hpp:273
vectorT::value_type vectorMedianInPlace(vectorT &vec)
Calculate median of a vector in-place, altering the vector.
The mxlib c++ namespace.
Definition: mxError.hpp:107
Symetric Gaussian smoothing kernel.