mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
turbSubHarmonic.hpp
1 /** \file turbSubHarm.hpp
2  * \brief A class to manage low-frequency sub-harmonic phase screen generation in atmospheric turbulence.
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 
30 #ifndef mx_AO_sim_turbSubHarm_hpp
31 #define mx_AO_sim_turbSubHarm_hpp
32 
33 #include <vector>
34 
35 #include "turbAtmosphere.hpp"
36 
37 #include "../../improc/eigenImage.hpp"
38 #include "../../improc/eigenCube.hpp"
39 
40 #include "../../math/constants.hpp"
41 #include "../../math/func/jinc.hpp"
42 
43 namespace mx
44 {
45 namespace AO
46 {
47 namespace sim
48 {
49 
50 /// A class to manage low-frequency sub-harmonic phase screen generation in atmospheric turbulence.
51 /** Implements the method of Johansson and Gavel (1994)\cite johansson_gavel_1994. This is
52  * needed to generate adequate tip/tilt variance with large outer scales.
53  *
54  * \ingroup mxAOSim
55  */
56 template<typename _turbAtmosphereT>
58 {
59 
60 public:
61 
62  typedef _turbAtmosphereT turbAtmosphereT;
63  typedef typename turbAtmosphereT::realT realT;
64 
65 protected:
66 
67  /** \name Configuration Parameters
68  * @{
69  */
70 
71  turbAtmosphereT * m_turbAtmo {nullptr};
72 
73  unsigned m_level {1}; ///< The subharmonic level to apply.
74 
75  bool m_preCalc {false}; ///< whether or not the modes are pre-calculated.
76 
77  ///@}
78 
79  /** \name Internal State
80  * @{
81  */
82 
83  uint32_t m_scrnSz; ///< The wavefront screen size from the layer being simulated.
84 
85  std::vector<realT> m_noise;
86 
87  std::vector<realT> m_m;
88  std::vector<realT> m_n;
89 
90  std::vector<realT> m_sqrtPSD; //the square-root of the PSD at each point
91 
92  improc::eigenCube<realT> m_modes; ///< the pre-calculated modes
93 
94  ///@}
95 
96 public:
97 
98  /** \name Construction
99  * @{
100  */
101  turbSubHarmonic();
102 
103  ///@}
104 
105  /** \name Configuration
106  * @{
107  */
108 
109  /// Set the pointer to the turbulent atmosphere
110  void turbAtmo( turbAtmosphereT * atm /**< [in] the new pointer to an AO atmosphere*/ );
111 
112  /// Get the pointer to the AO atmosphere
113  /**
114  * \returns the the current pointer to the AO atmosphere
115  */
116  turbAtmosphereT * turbAtmo();
117 
118  /// Set the subharmonic level to apply
119  void level( uint32_t ml /**< [in] the new level*/ );
120 
121  /// Get the subharmonic level
122  /**
123  * \returns the current subharmonic level
124  */
125  uint32_t level();
126 
127  /// Set whether or not to pre-calculate the modes
128  void preCalc( bool pc /**< [in] the new value of the preCalc flag*/ );
129 
130  /// Get whether or not the modes are pre-calculated
131  /**
132  * \returns the current value of the preCalc flag
133  */
134  bool preCalc();
135 
136  ///@}
137 
138  /** \name Screen Generation
139  * @{
140  */
141 
142  void initGrid( uint32_t layerNo );
143 
144  void screen( improc::eigenImage<realT> & scrn );
145 
146  /// Deallocate memory
147  void deinit();
148 
149  ///@}
150 };
151 
152 template<typename turbAtmosphereT>
154 {
155 }
156 
157 template<typename turbAtmosphereT>
158 void turbSubHarmonic<turbAtmosphereT>::turbAtmo( turbAtmosphereT * turbatm )
159 {
160  m_turbAtmo = turbatm;
161 }
162 
163 template<typename turbAtmosphereT>
165 {
166  return m_turbAtmo;
167 }
168 
169 template<typename turbAtmosphereT>
171 {
172  m_level = ml;
173 }
174 
175 template<typename turbAtmosphereT>
177 {
178  return m_level;
179 }
180 
181 template<typename turbAtmosphereT>
183 {
184  m_preCalc = pc;
185 }
186 
187 template<typename turbAtmosphereT>
189 {
190  return m_preCalc;
191 }
192 
193 template<typename turbAtmosphereT>
194 void turbSubHarmonic<turbAtmosphereT>::initGrid( uint32_t layerNo )
195 {
196  int N;
197 
198  if(m_turbAtmo == nullptr)
199  {
200  mxThrowException(err::paramnotset, "mx::AO::sim::turbSubHarmonic::initGrid", "atmosphere is not set (m_turbAtmo is nullptr)");
201  }
202 
203  if(m_turbAtmo->aosys() == nullptr)
204  {
205  mxThrowException(err::paramnotset, "mx::AO::sim::turbSubHarmonic::initGrid", "ao system is not set (m_turbAtmo->m_aosys is nullptr)");
206  }
207 
208  if(m_turbAtmo->nLayers() <= layerNo)
209  {
210  mxThrowException(err::invalidconfig , "mx::AO::sim::turbSubHarmonic::initGrid", "atmosphere is not setup (m_turbAtmo->m_layers size is <= layerNo)");
211  }
212 
213  if(m_level == 0)
214  {
215  N = 0;
216  }
217  else
218  {
219  N = 36.0 + 32.0*(m_level-1);
220  }
221 
222  m_m.resize(N);
223  m_n.resize(N);
224  m_sqrtPSD.resize(N);
225  m_noise.resize(N);
226 
227  m_scrnSz = m_turbAtmo->layer(layerNo).scrnSz();
228 
229 
230  if(N == 0)
231  {
232  m_modes.clear();
233  return;
234  }
235 
236 
237 
238  realT r0 = m_turbAtmo->aosys()->atm.r_0(m_turbAtmo->aosys()->lam_sci());
239  realT D = m_turbAtmo->aosys()->D();
240  uint32_t wfSz = m_turbAtmo->wfSz();
241 
242 
243  realT beta = 0.0218/pow(r0, 5./3.)/pow((D/wfSz)*m_scrnSz,2) ;
244 
245  realT sqrt_alpha = 0.5*11./3.;
246 
247  realT L0 = m_turbAtmo->aosys()->atm.L_0(layerNo);
248 
249  realT L02;
250  if(L0 > 0) L02 = 1.0/pow( L0, 2);
251  else L02 = 0;
252 
253  if(m_preCalc)
254  {
255  m_modes.resize(m_scrnSz, m_scrnSz, N);
256  }
257 
258  int n = 0;
259  for(int nl = 1; nl <= m_level; ++nl)
260  {
261  realT sc = pow(3.0, -nl);
262 
263  for(int mp = -3; mp < 3; ++mp)
264  {
265  for(int np = -3; np < 3; ++np)
266  {
267  if(nl < m_level)
268  {
269  if(mp == -1)
270  {
271  if(np == -1 || np == 0) continue;
272  }
273  else if(mp == 0)
274  {
275  if(np == -1 || np == 0) continue;
276  }
277  }
278 
279  m_m[n] = sc*(mp+0.5);
280  m_n[n] = sc*(np+0.5);
281 
282  realT k = sqrt((pow(m_m[n],2) + pow(m_n[n],2)))/((D/wfSz)*m_scrnSz);
283 
284  realT tpsd = beta / pow( k*k + L02, sqrt_alpha);
285 
286  realT Ppiston = 0;
287  realT Ptiptilt = 0;
288  if(m_turbAtmo->aosys()->psd.subPiston())
289  {
290  Ppiston = pow(2*math::func::jinc(math::pi<realT>() * k * D), 2);
291  }
292 
293  if(m_turbAtmo->aosys()->psd.subTipTilt())
294  {
295  Ptiptilt = pow(4*math::func::jincN(2,math::pi<realT>() * k * D), 2);
296  }
297 
298  m_sqrtPSD[n] = sc*sqrt(tpsd*(1-Ppiston-Ptiptilt));
299 
300  if(m_preCalc)
301  {
302  for(int cc=0; cc < m_scrnSz; ++cc)
303  {
304  realT np = cc - 0.5*m_scrnSz;
305  for(int rr=0; rr < m_scrnSz; ++rr)
306  {
307  realT mp = rr - 0.5*m_scrnSz;
308  m_modes.image(n)(rr,cc) = m_sqrtPSD[n]*cos(math::two_pi<realT>()*(m_m[n]*mp + m_n[n]*np)/m_scrnSz);
309  }
310  }
311  }
312 
313  ++n;
314  }
315  }
316  }
317 }
318 
319 template<typename turbAtmosphereT>
320 void turbSubHarmonic<turbAtmosphereT>::screen(improc::eigenImage<realT> & scrn)
321 {
322  if(m_level == 0)
323  {
324  return;
325  }
326 
327  if(m_turbAtmo == nullptr)
328  {
329  mxThrowException(err::paramnotset, "mx::AO::sim::turbSubHarmonic::screen", "atmosphere is not set (m_turbAtmo is nullptr)");
330  }
331 
332  if(scrn.rows() != m_scrnSz || scrn.cols() != m_scrnSz)
333  {
334  mxThrowException(err::sizeerr, "mx::AO::sim::turbSubHarmonic::screen", "input screen is not the right size");
335  }
336 
337  //Check that we're allocated
338  if(m_preCalc)
339  {
340  if(m_modes.rows() != scrn.rows() || m_modes.cols() != scrn.cols() || m_modes.planes() != m_noise.size())
341  {
342  mxThrowException(err::sizeerr, "mx::AO::sim::turbSubHarmonic::screen", "modes cube wrong size, call initGrid().");
343  }
344  }
345  else
346  {
347  if( m_noise.size() != m_m.size() || m_noise.size() != m_n.size() || m_sqrtPSD.size() != m_noise.size())
348  {
349  mxThrowException(err::sizeerr, "mx::AO::sim::turbSubHarmonic::screen", "vectors not allocated, call initGrid().");
350  }
351  }
352 
353  //Now fill in the noise
354  for(size_t n=0; n < m_noise.size(); ++n)
355  {
356  m_noise[n] = 2*m_turbAtmo->normVar();
357  }
358 
359  #pragma omp parallel for
360  for(int cc = 0; cc < m_scrnSz; ++cc)
361  {
362  realT np = cc - 0.5*m_scrnSz;
363  for(int rr=0; rr < m_scrnSz; ++rr)
364  {
365  realT mp = rr - 0.5*m_scrnSz;
366 
367  if(m_preCalc)
368  {
369  for(unsigned n =0; n < m_noise.size(); ++n)
370  {
371  scrn(rr,cc) += m_noise[n]*m_modes.image(n)(rr,cc);
372  }
373  }
374  else
375  {
376  for(unsigned n =0; n < m_m.size(); ++n)
377  {
378  scrn(rr,cc) += m_noise[n]*m_sqrtPSD[n]*cos(math::two_pi<realT>()*(m_m[n]*mp + m_n[n]*np)/m_scrnSz);
379  }
380  }
381  }
382  }
383 }
384 
385 template<typename turbAtmosphereT>
387 {
388  m_m.clear();
389  m_n.clear();
390  m_sqrtPSD.clear();
391  m_noise.clear();
392  m_modes.resize(0, 0, 0);
393 }
394 
395 } //namespace sim
396 } //namespace AO
397 } //namespace mx
398 
399 #endif //mx_AO_sim_turbSubHarm_hpp
400 
A class to manage low-frequency sub-harmonic phase screen generation in atmospheric turbulence.
void deinit()
Deallocate memory.
uint32_t m_scrnSz
The wavefront screen size from the layer being simulated.
bool preCalc()
Get whether or not the modes are pre-calculated.
improc::eigenCube< realT > m_modes
the pre-calculated modes
turbAtmosphereT * turbAtmo()
Get the pointer to the AO atmosphere.
uint32_t level()
Get the subharmonic level.
bool m_preCalc
whether or not the modes are pre-calculated.
unsigned m_level
The subharmonic level to apply.
mxException for invalid config settings
mxException for parameters which aren't set
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
T2 jincN(const T1 &v, const T2 &x)
The JincN function.
Definition: jinc.hpp:120
T jinc(const T &x)
The Jinc function.
Definition: jinc.hpp:64
The mxlib c++ namespace.
Definition: mxError.hpp:107
Declaration and definition of a turbulent atmosphere.