mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
turbAtmosphere.hpp
Go to the documentation of this file.
1 /** \file turbAtmosphere.hpp
2  * \brief Declaration and definition of a turbulent atmosphere.
3  *
4  * \author Jared R. Males (jaredmales@gmail.com)
5  *
6  * \ingroup mxAO_sim_files
7  *
8  */
9 
10 //***********************************************************************//
11 // Copyright 2023 Jared R. Males (jaredmales@gmail.com)
12 //
13 // This file is part of mxlib.
14 //
15 // mxlib is free software: you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation, either version 3 of the License, or
18 // (at your option) any later version.
19 //
20 // mxlib is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
27 //***********************************************************************//
28 
29 #ifndef mx_AO_sim_turbAtmosphere_hpp
30 #define mx_AO_sim_turbAtmosphere_hpp
31 
32 #include <vector>
33 #include <iostream>
34 
35 #include "../../sigproc/psdFilter.hpp"
36 #include "../../sigproc/psdUtils.hpp"
37 
38 #include "../../base/changeable.hpp"
39 
40 #include "../../improc/milkImage.hpp"
41 
42 #include "../../math/constants.hpp"
43 #include "../../math/func/jinc.hpp"
44 #include "../../math/randomT.hpp"
45 
46 #include "../../ioutils/fits/fitsFile.hpp"
47 #include "../../ioutils/stringUtils.hpp"
48 #include "../../sys/timeUtils.hpp"
49 
50 #include "turbLayer.hpp"
51 #include "turbSubHarmonic.hpp"
52 #include "wavefront.hpp"
53 
54 #ifdef DEBUG
55 #define BREAD_CRUMB std::cout << "DEBUG: " << __FILE__ << " " << __LINE__ << "\n";
56 #else
57 #define BREAD_CRUMB
58 #endif
59 
60 
61 namespace mx
62 {
63 namespace AO
64 {
65 namespace sim
66 {
67 
68 /// A turbulent atmosphere simulator
69 /** Generate multi-layer phase screens using PSD filtering with the FFT. Manages
70  * the shifting and combination of the phase screens (without propagation).
71  *
72  * The \ref turbSubHarmonic class provides low-frequency sub-harmonics if desired
73  *
74  * The \ref turbLayer class manages the actual interpolation.
75  *
76  * \todo add facility for switching interpolators
77  * \todo need to include wrap detection when using subharmonics (e.g. frames needs to be right)
78  *
79  * \ingroup mxAOSim
80  */
81 template<typename _aoSystemT>
83 {
84 
85 public:
86 
87  typedef _aoSystemT aoSystemT;
88  typedef typename aoSystemT::realT realT;
89  //typedef typename aoSystemT::atmosphereT atmosphereT;
90  typedef Eigen::Array<realT, -1, -1> imageT;
91 
92 protected:
93 
94  /** \name Configuration Data
95  * @{
96  */
97  uint32_t m_wfSz {0}; ///< Size of the wavefront in pixels.
98 
99  uint32_t m_buffSz {0}; ///< Buffer to apply around wavefront for interpolation.
100 
101  aoSystemT * m_aosys {nullptr}; ///< The AO system object containing all system parameters. See \ref aoSystem.
102 
103  uint32_t m_shLevel {0}; /**< Number of subharmonic levels. 0 means no subharmonics. Generally only
104  * 1 level is needed to obtain good Tip/Tilt results. See \ref turbSubHarmonic.
105  */
106 
107  bool m_shPreCalc {true}; /**< Whether or not to pre-calculate the subharmonic modes. This is a
108  * trade of speed vs. memory use. See \ref turbSubHarmonic.
109  */
110 
111  bool m_retain {false}; /**< Whether or not to retain working memory after screen generation. One
112  * would set to true if many screens will be calculated in monte carlo fashion.
113  * For a standard case of one set of screens being shifted by wind velocity,
114  * this should be false to minimize memory use.
115  */
116 
117  bool m_forceGen {true}; /**< Force generation of new screens if true. Note that this class does not presently
118  * do any checks of saved screens to verify they match the current configuration.
119  * Set to true with caution.
120  *
121  * \todo need check for changes in parameters to decide if saved frames are valid per layer
122  */
123 
124  std::string m_dataDir; /**< Specifies a path where phase screens are stored. Leaving this unset or "" is equivalent to
125  * \ref m_forceGen` == true`.
126  */
127 
128  imageT * m_pupil {0}; ///< A pointer to the pupil mask.
129 
130  realT m_timeStep {0}; ///< Length of each iteration, in seconds.
131 
132  ///@}
133 
134  /** \name Internal State
135  * @{
136  */
137  std::vector<turbLayer<aoSystemT>> m_layers; ///< Vector of turbulent layers.
138 
139  std::vector<turbSubHarmonic<turbAtmosphere>> m_subHarms; ///< Sub-harmonic layers
140 
141  size_t m_frames {0}; ///< Length of the turbulence sequence.
142 
143  int m_nWf {0}; ///< Number of iterations which have occurred.
144 
145  math::normDistT<realT> m_normVar; ///< Normal random deviate generator. This seeded in the constructor.
146 
147  improc::eigenImage<realT> m_phaseWork;
148 
149 public:
150 
151  /** \name Construction and Configuration
152  * @{
153  */
154 
155  ///Default c'tor
157 
158  ///Setup the overall atmosphere.
159  /**
160  */
161  void setup( uint32_t wfSz, ///< [in] The size of the wavefront in pixels.
162  uint32_t buffSz, ///< [in] The size of the interpolation buffer to use.
163  aoSystemT * aosys, ///< [in] Pointer to an AO System. See \ref m_aosys.
164  uint32_t shLevel ///< [in] number of subharmonic levels to use. 0 turns off subharmonics. See \ref m_shLevel.
165  );
166 
167  /// Set the wavefront size
168  /**
169  * see \ref m_wfSz
170  */
171  void wfSz( uint32_t ws /**< [in] the new wavefront size */);
172 
173  /// Get the wavefront size
174  /**
175  * \returns the current value of \ref m_wfSz
176  */
177  uint32_t wfSz();
178 
179  /// Set the interpolation buffer size
180  /**
181  * see \ref m_buffSz
182  */
183  void buffSz( uint32_t bs /**< [in] the new buffer size*/);
184 
185  /// Get the interpolation buffer size
186  /**
187  * \returns the current value of \ref m_buffSz
188  */
189  uint32_t buffSz();
190 
191  /// Set the pointer to an AO system object
192  /**
193  * see \ref m_aosys
194  */
195  void aosys( aoSystemT * aos /**< [in] the new pointer to an AO system object*/);
196 
197  /// Get the pointer to an AO system object
198  /**
199  * \returns the current value of \ref m_aosys
200  */
201  aoSystemT * aosys();
202 
203  /// Set the subharmonic level
204  /**
205  * see \ref m_shLevel
206  */
207  void shLevel( uint32_t shl /**< [in] the new subharmonic level*/);
208 
209  /// Get the subharmonic level
210  /**
211  * \returns the current value of \ref m_shLevel
212  */
213  uint32_t shLevel();
214 
215  /// Set whether subharmonic modes are pre-calculated
216  /**
217  * see \ref m_shPreCalc
218  */
219  void shPreCalc( bool shp /**< [in] the new value of flag controlling subharmonic mode precalculation */);
220 
221  /// Get whether subharmonic modes are pre-calculated
222  /**
223  * \returns the current value of \ref m_shPreCalc
224  */
225  bool shPreCalc();
226 
227  /// Set whether memory for screen generation is retained
228  /**
229  * see \ref m_retain
230  */
231  void retain( bool rtn /**< [in] the new value of the flag controlling whether memory is retained*/);
232 
233  /// Get whether memory for screen generation is retained
234  /**
235  * \returns the current value of \ref m_retain
236  */
237  bool retain();
238 
239  /// Set whether new screen generation is forced
240  /**
241  * see \ref m_forceGen
242  */
243  void forceGen( bool fg /**< [in] the new value of the flag controlling whether screen generation is forced*/);
244 
245  /// Get whether new screen generation is forced
246  /**
247  * \returns the current value of m_forceGen
248  */
249  bool forceGen();
250 
251  /// Set the data directory for saving phase screens
252  /**
253  * see \ref m_dataDir
254  */
255  void dataDir( const std::string & dd /**< [in] the new data directory for phase screen saving*/);
256 
257  /// Get the data directory for saving phase screens
258  /**
259  * \returns the current value of m_dataDir
260  */
261  std::string dataDir();
262 
263  /// Setup the layers and prepare them for phase screen generation.
264  /** The number of entries in \p scrnSz must mach the number of layers in the atmosphere
265  * of \ref m_aosys.
266  */
267  void setLayers( const std::vector<size_t> & scrnSz /**< [in] the screen sizes for each layer.*/);
268 
269  /// Setup the layers and prepare them for phase screen generation.
270  /** This sets all layer phase screens to be the same size.
271  *
272  */
273  void setLayers( const size_t scrnSz /**< [in] the screen sizes for all layers.*/);
274 
275  /// Get the number of layers
276  /**
277  * \returns the size of the layers vector \ref m_layers
278  */
279  uint32_t nLayers();
280 
281  /// Get a layer
282  /**
283  * \returns a reference to on entry in \ref m_layers
284  */
286 
287  /// Get the random number generator
288  /**
289  * \returns a reference to \ref m_normVar
290  */
292 
293  ///@} - Construction and Configuration
294 
295  /** \name Screen Generation
296  * @{
297  */
298 
299  /// Generate all phase screens
300  /** Loads them from disk if possible and \ref m_forceGen == false.
301  *
302  * Deallocates working memory when done, unless \ref m_retain == true.
303  */
304  void genLayers();
305 
306  /// @}
307 
308  int shift( improc::milkImage<realT> & phase,
309  realT dt );
310 
311  int frames(int f);
312  size_t frames();
313 
314  int wfPS(realT ps); ///< dummy function for simulatedAOSystem. Does nothing.
315 
316  bool _loopClosed;
317 
318  void nextWF(wavefront<realT> & wf);
319 };
320 
321 template<typename aoSystemT>
323 {
324  m_normVar.seed();
325 }
326 
327 template<typename aoSystemT>
329  uint32_t bs,
330  aoSystemT * aos,
331  uint32_t shl
332  )
333 {
334  wfSz(ws);
335  buffSz(bs);
336  aosys(aos);
337  shLevel(shl);
338 }
339 
340 template<typename aoSystemT>
342 {
343  if(ws != m_wfSz)
344  {
345  changed();
346  }
347 
348  m_wfSz = ws;
349  m_phaseWork.resize(m_wfSz, m_wfSz);
350 }
351 
352 template<typename aoSystemT>
354 {
355  return m_wfSz;
356 }
357 
358 template<typename aoSystemT>
360 {
361  if(bs != m_buffSz)
362  {
363  changed();
364  }
365 
366  m_buffSz = bs;
367 }
368 
369 template<typename aoSystemT>
371 {
372  return m_buffSz;
373 }
374 
375 template<typename aoSystemT>
376 void turbAtmosphere<aoSystemT>::aosys( aoSystemT * aos)
377 {
378  if(aos != m_aosys)
379  {
380  m_aosys = aos;
381  changed();
382  }
383 }
384 
385 template<typename aoSystemT>
387 {
388  return m_aosys;
389 }
390 
391 template<typename aoSystemT>
393 {
394  if(shl != m_shLevel)
395  {
396  m_shLevel = shl;
397  changed();
398  }
399 }
400 
401 template<typename aoSystemT>
403 {
404  return m_shLevel;
405 }
406 
407 template<typename aoSystemT>
409 {
410  if(shp != m_shPreCalc)
411  {
412  m_shPreCalc = shp;
413  changed();
414  }
415 }
416 
417 template<typename aoSystemT>
419 {
420  return m_shPreCalc;
421 }
422 
423 template<typename aoSystemT>
425 {
426  if(rtn != m_retain)
427  {
428  m_retain = rtn;
429  changed();
430  }
431 }
432 
433 template<typename aoSystemT>
435 {
436  return m_retain;
437 }
438 
439 template<typename aoSystemT>
441 {
442  if(fg != m_forceGen)
443  {
444  changed();
445  }
446 
447  m_forceGen = fg;
448 }
449 
450 template<typename aoSystemT>
452 {
453  return m_forceGen;
454 }
455 
456 template<typename aoSystemT>
457 void turbAtmosphere<aoSystemT>::dataDir( const std::string & dd )
458 {
459  if(dd != m_dataDir)
460  {
461  changed();
462  }
463 
464  m_dataDir = dd;
465 }
466 
467 template<typename aoSystemT>
469 {
470  return m_dataDir;
471 }
472 
473 template<typename aoSystemT>
474 void turbAtmosphere<aoSystemT>::setLayers( const std::vector<size_t> & scrnSz)
475 {
476  if(m_aosys == nullptr)
477  {
478  mxThrowException(err::paramnotset, "mx::AO::sim::turbAtmosphere::setLayers", "ao system is not set (m_aosys is nullptr)");
479  }
480 
481  size_t nLayers = scrnSz.size();
482 
483  if(nLayers != m_aosys->atm.n_layers())
484  {
485  mxThrowException(err::invalidarg, "mx::AO::sim::turbAtmosphere::setLayers", "Size of scrnSz vector does not match atmosphere.");
486  }
487 
488  if(nLayers != m_layers.size())
489  {
490  changed();
491  }
492 
493  m_layers.resize(nLayers);
494 
495  for(size_t i=0; i< nLayers; ++i)
496  {
497  m_layers[i].setLayer(this, i, scrnSz[i]);
498  }
499 
500  if(m_shLevel > 0)
501  {
502  if(nLayers != m_subHarms.size())
503  {
504  changed();
505  }
506 
507  m_subHarms.resize(nLayers);
508 
509  for(size_t i=0; i< nLayers; ++i)
510  {
511  m_subHarms[i].turbAtmo(this);
512  m_subHarms[i].preCalc(m_shPreCalc);
513  m_subHarms[i].level(m_shLevel);
514  m_subHarms[i].initGrid(i);
515  }
516  }
517 }
518 
519 template<typename aoSystemT>
520 void turbAtmosphere<aoSystemT>::setLayers( const size_t scrnSz )
521 {
522  if(m_aosys == nullptr)
523  {
524  mxThrowException(err::paramnotset, "mx::AO::sim::turbAtmosphere::setLayers", "atmosphere is not set (m_atm is nullptr)");
525  }
526 
527  size_t n = m_aosys->atm.n_layers();
528 
529  setLayers( std::vector<size_t>(n, scrnSz));
530 }
531 
532 template<typename aoSystemT>
534 {
535  return m_layers.size();
536 }
537 
538 template<typename aoSystemT>
540 {
541  if(n >= m_layers.size())
542  {
543  mxThrowException(err::invalidarg, "mx::AO::sim::turbAtmosphere::layer", "n too large for number of layers.");
544  }
545 
546  return m_layers[n];
547 }
548 
549 template<typename aoSystemT>
551 {
552  return m_normVar;
553 }
554 
555 template<typename aoSystemT>
557 {
558  if(m_dataDir != "" && !m_forceGen)
559  {
560  std::string fbase = m_dataDir;
561  fbase += "/";
562  fbase += "layer_";
563 
565  std::string fname;
566  for(size_t i=0; i< m_layers.size(); ++i)
567  {
568  fname = fbase + ioutils::convertToString<int>(i) + ".fits";
569  ff.read(m_layers[i].m_phase, fname);
570  }
571 
572  return;
573  }
574 
575  sigproc::psdFilter<realT,2> filt;
576 
577  for(size_t i=0; i< m_layers.size(); ++i)
578  {
579  if(m_layers[i].m_phase.rows() != m_layers[i].scrnSz() || m_layers[i].m_phase.cols() != m_layers[i].scrnSz())
580  {
581  mxThrowException(err::sizeerr, "mx::AO::sim::turbAtmosphere::genLayers", "layer phase not allocated.");
582  }
583 
584  if(m_layers[i].m_freq.rows() != m_layers[i].scrnSz() || m_layers[i].m_freq.cols() != m_layers[i].scrnSz())
585  {
586  mxThrowException(err::sizeerr, "mx::AO::sim::turbAtmosphere::genLayers", "layer freq not allocated.");
587  }
588 
589  if(m_layers[i].m_psd.rows() != m_layers[i].scrnSz() || m_layers[i].m_psd.cols() != m_layers[i].scrnSz())
590  {
591  mxThrowException(err::sizeerr, "mx::AO::sim::turbAtmosphere::genLayers", "layer psd not allocated.");
592  }
593 
594  for(size_t jj = 0; jj < m_layers[i].m_scrnSz; ++jj)
595  {
596  for(size_t ii = 0; ii < m_layers[i].m_scrnSz; ++ii)
597  {
598  m_layers[i].m_phase(ii,jj) = m_normVar;
599  }
600  }
601 
602  realT dkx = m_layers[i].m_freq(1,0)-m_layers[i].m_freq(0,0);
603  realT dky = m_layers[i].m_freq(0,1)-m_layers[i].m_freq(0,0);
604  filt.psdSqrt(m_layers[i].m_psd, dkx, dky);
605 
606  filt(m_layers[i].m_phase);
607 
608  if(m_shLevel > 0)
609  {
610  m_subHarms[i].screen(m_layers[i].m_phase);
611  }
612  }
613 
614  if(!m_retain)
615  {
616  for(size_t i=0; i< m_layers.size(); ++i)
617  {
618  m_layers[i].genDealloc();
619  }
620 
621  for(size_t i=0; i< m_subHarms.size(); ++i)
622  {
623  m_subHarms[i].deinit();
624  }
625  }
626 
627  if(m_dataDir != "")
628  {
629  std::string fbase = m_dataDir;
630  fbase += "/";
631  fbase += "layer_";
632 
634  std::string fname;
635  for(size_t i=0; i< m_layers.size(); ++i)
636  {
637  fname = fbase + ioutils::convertToString<int>(i) + ".fits";
638  ff.write(fname, m_layers[i].m_phase);
639  }
640  }
641 }
642 
643 template<typename aoSystemT>
645  realT dt
646  )
647 {
648  improc::eigenMap<realT> phase(milkPhase);
649 
650  if(phase.rows() != m_wfSz || phase.cols() != m_wfSz)
651  {
652  }
653 
654  m_phaseWork.setZero();
655 
656  #pragma omp parallel for
657  for(size_t j=0; j< m_layers.size(); ++j)
658  {
659  m_layers[j].shift( dt );
660 
661  //This is faster than a separate loop by 5-10%
662  #pragma omp critical
663  m_phaseWork += sqrt( m_aosys->atm.layer_Cn2(j)) * m_layers[j].m_shiftPhase.block(m_buffSz, m_buffSz, m_wfSz, m_wfSz);
664  }
665 
666  phase = m_phaseWork;
667  milkPhase.post();
668 
669  return 0;
670 }
671 
672 template<typename aoSystemT>
673 int turbAtmosphere<aoSystemT>::frames(int f)
674 {
675  m_frames = f;
676 
677  return 0;
678 }
679 
680 template<typename aoSystemT>
681 size_t turbAtmosphere<aoSystemT>::frames()
682 {
683  return m_frames;
684 }
685 
686 template<typename aoSystemT>
688 {
689  static_cast<void>(ps);
690  return 0;
691 }
692 
693 template<typename aoSystemT>
695 {
696  if(!m_aosys)
697  {
698  mxThrowException(err::paramnotset, "mx::AO::sim::turbAtmosphere<aoSystemT>::nextWF", "the AO system pointer is not set");
699  }
700 
701  if(!m_pupil)
702  {
703  mxThrowException(err::paramnotset, "mx::AO::sim::turbAtmosphere<aoSystemT>::nextWF", "the m_pupil pointer is not set");
704  }
705 
706  shift( wf.phase, m_nWf * m_timeStep);
707  ++m_nWf;
708 
709  wf.phase *= *m_pupil;
710 
711  realT pixVal = sqrt(m_aosys->Fg())*(m_aosys->D()/m_wfSz);
712 
713  wf.amplitude = pixVal*(*m_pupil);
714 }
715 
716 } //namespace sim
717 } //namespace AO
718 } //namespace mx
719 
720 #endif //mx_AO_sim_turbAtmosphere_hpp
A simple class to track member data changes.
Definition: changeable.hpp:27
mxException for invalid arguments
mxException for parameters which aren't set
mxException for a size error
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 interface with an ImageStreamIO image in shared memory.
Definition: milkImage.hpp:147
void post()
Update the metadata and post all semaphores.
Definition: milkImage.hpp:380
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
Definition: eigenImage.hpp:44
Eigen::Map< Eigen::Array< scalarT, -1, -1 > > eigenMap
Definition of the eigenMap type, which is an alias for Eigen::Map<Array>.
Definition: eigenImage.hpp:50
The mxlib c++ namespace.
Definition: mxError.hpp:107
A turbulent atmosphere simulator.
uint32_t buffSz()
Get the interpolation buffer size.
aoSystemT * m_aosys
The AO system object containing all system parameters. See aoSystem.
uint32_t m_buffSz
Buffer to apply around wavefront for interpolation.
int wfPS(realT ps)
dummy function for simulatedAOSystem. Does nothing.
uint32_t nLayers()
Get the number of layers.
std::vector< turbLayer< aoSystemT > > m_layers
Vector of turbulent layers.
bool forceGen()
Get whether new screen generation is forced.
int m_nWf
Number of iterations which have occurred.
void retain(bool rtn)
Set whether memory for screen generation is retained.
std::vector< turbSubHarmonic< turbAtmosphere > > m_subHarms
Sub-harmonic layers.
void wfSz(uint32_t ws)
Set the wavefront size.
void genLayers()
Generate all phase screens.
std::string dataDir()
Get the data directory for saving phase screens.
aoSystemT * aosys()
Get the pointer to an AO system object.
uint32_t shLevel()
Get the subharmonic level.
imageT * m_pupil
A pointer to the pupil mask.
uint32_t wfSz()
Get the wavefront size.
bool retain()
Get whether memory for screen generation is retained.
realT m_timeStep
Length of each iteration, in seconds.
void setLayers(const std::vector< size_t > &scrnSz)
Setup the layers and prepare them for phase screen generation.
void forceGen(bool fg)
Set whether new screen generation is forced.
bool shPreCalc()
Get whether subharmonic modes are pre-calculated.
math::normDistT< realT > & normVar()
Get the random number generator.
void shPreCalc(bool shp)
Set whether subharmonic modes are pre-calculated.
void shLevel(uint32_t shl)
Set the subharmonic level.
void dataDir(const std::string &dd)
Set the data directory for saving phase screens.
size_t m_frames
Length of the turbulence sequence.
turbLayer< aoSystemT > & layer(uint32_t n)
Get a layer.
uint32_t m_wfSz
Size of the wavefront in pixels.
math::normDistT< realT > m_normVar
Normal random deviate generator. This seeded in the constructor.
void setup(uint32_t wfSz, uint32_t buffSz, aoSystemT *aosys, uint32_t shLevel)
Setup the overall atmosphere.
void buffSz(uint32_t bs)
Set the interpolation buffer size.
void setLayers(const size_t scrnSz)
Setup the layers and prepare them for phase screen generation.
void aosys(aoSystemT *aos)
Set the pointer to an AO system object.
Simulation of a single turbulent layer.
Definition: turbLayer.hpp:56
Structure containing the phase and amplitude of a wavefront
Definition: wavefront.hpp:26
realImageT amplitude
The wavefront amplitude.
Definition: wavefront.hpp:37
realImageT phase
The wavefront phase.
Definition: wavefront.hpp:40
Declaration and definition of a turbulence layer.