mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
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#ifndef mx_AO_sim_turbSubHarm_hpp
30#define mx_AO_sim_turbSubHarm_hpp
31
32#include <vector>
33
34#include "turbAtmosphere.hpp"
35
36#include "../../base/changeable.hpp"
37
38#include "../../improc/eigenImage.hpp"
39#include "../../improc/eigenCube.hpp"
40
41#include "../../math/constants.hpp"
42#include "../../math/func/jinc.hpp"
43
44namespace mx
45{
46namespace AO
47{
48namespace sim
49{
50
51/// A class to manage low-frequency sub-harmonic phase screen generation in atmospheric turbulence.
52/** Implements the method of Johansson and Gavel (1994)\cite johansson_gavel_1994. This is
53 * needed to generate adequate tip/tilt variance with large outer scales.
54 *
55 * \ingroup mxAOSim
56 */
57template <typename _turbAtmosphereT, class _verboseT = mx::verbose::d>
58class turbSubHarmonic : public base::changeable<turbSubHarmonic<_turbAtmosphereT>>
59{
60
61 public:
62 typedef _turbAtmosphereT turbAtmosphereT;
63 typedef _verboseT verboseT;
64
65 typedef typename turbAtmosphereT::realT realT;
66
67 protected:
68 /** \name Configuration Parameters
69 * @{
70 */
71
72 turbAtmosphereT *m_turbAtmo{ nullptr }; ///< Pointer to the parent atmosphere object.
73
74 unsigned m_level{ 1 }; ///< The subharmonic level to apply.
75
76 bool m_outerSubHarmonics{ true }; ///< Whether or not to include the outer subharmonics
77
78 bool m_preCalc{ false }; ///< whether or not the modes are pre-calculated.
79
80 ///@}
81
82 /** \name Internal State
83 * @{
84 */
85
86 uint32_t m_scrnSz; ///< The wavefront screen size from the layer being simulated.
87
88 std::vector<realT> m_noise; ///< Vector of Gaussian deviates prepared for each screen generation.
89
90 std::vector<realT> m_m; ///< m-coordinate fractional spatial frequency indices of the subharmonics
91 std::vector<realT> m_n; ///< n-coordinate fractional spatial frequency indices of the subharmonics
92
93 std::vector<realT> m_sqrtPSD; // the square-root of the PSD at each point
94
95 improc::eigenCube<realT> m_modes; ///< the pre-calculated modes
96
97 ///@}
98
99 public:
100 /** \name Construction
101 * @{
102 */
104
105 ///@}
106
107 /** \name Configuration
108 * @{
109 */
110
111 /// Set the pointer to the turbulent atmosphere
112 void turbAtmo( turbAtmosphereT *atm /**< [in] the new pointer to an AO atmosphere*/ );
113
114 /// Get the pointer to the AO atmosphere
115 /**
116 * \returns the the current pointer to the AO atmosphere
117 */
118 turbAtmosphereT *turbAtmo();
119
120 /// Set the subharmonic level to apply
121 void level( uint32_t ml /**< [in] the new level*/ );
122
123 /// Get the subharmonic level
124 /**
125 * \returns the current subharmonic level
126 */
127 uint32_t level();
128
129 /// Set whether or not the outer subharmonics are included
130 void outerSubHarmonics( bool osh /**< [in] the new value of the \ref m_outerSubHarmonics flag */ );
131
132 /// Get whether or not the outer subharmonics are included
133 /**
134 * \returns the current value of the \ref m_outerSubHarmonics flag
135 */
136 bool outerSubHarmonics();
137
138 /// Set whether or not to pre-calculate the modes
139 void preCalc( bool pc /**< [in] the new value of the \ref m_preCalc flag*/ );
140
141 /// Get whether or not the modes are pre-calculated
142 /**
143 * \returns the current value of the preCalc flag
144 */
145 bool preCalc();
146
147 ///@}
148
149 /** \name Screen Generation
150 * @{
151 */
152
153 /// Allocate needed memory and initialize the subharmonic transform
154 void initGrid( uint32_t layerNo );
155
156 /// Generate a realization of the subharmonic phase screen and add it to the input screen
157 void screen( improc::eigenImage<realT> & scrn /**< [in] the input phase screen to which to add the low-frequency screen. Must be m_scrnSz X m_scrnsz*/ );
158
159 /// Deallocate memory
160 void deinit();
161
162 ///@}
163};
164
165template <typename turbAtmosphereT, class verboseT>
167{
168}
169
170template <typename turbAtmosphereT, class verboseT>
172{
173 if( turbatm != m_turbAtmo )
174 {
175 m_turbAtmo = turbatm;
176 this->changed();
177 }
178}
179
180template <typename turbAtmosphereT, class verboseT>
182{
183 return m_turbAtmo;
184}
185
186template <typename turbAtmosphereT, class verboseT>
188{
189 if( ml != m_level )
190 {
191 m_level = ml;
192 this->changed();
193 }
194}
195
196template <typename turbAtmosphereT, class verboseT>
198{
199 return m_level;
200}
201
202template <typename turbAtmosphereT, class verboseT>
204{
205 if( osh != m_outerSubHarmonics )
206 {
207 m_outerSubHarmonics = osh;
208 this->changed();
209 }
210}
211
212template <typename turbAtmosphereT, class verboseT>
214{
215 return m_outerSubHarmonics;
216}
217
218template <typename turbAtmosphereT, class verboseT>
220{
221 if( pc != m_preCalc )
222 {
223 m_preCalc = pc;
224 this->changed();
225 }
226}
227
228template <typename turbAtmosphereT, class verboseT>
230{
231 return m_preCalc;
232}
233
234template <typename turbAtmosphereT, class verboseT>
236{
237 int N;
238
239 if( m_turbAtmo == nullptr )
240 {
241 throw mx::exception<verboseT>( error_t::paramnotset, "atmosphere is not set (m_turbAtmo is nullptr)" );
242 }
243
244 if( m_turbAtmo->aosys() == nullptr )
245 {
246 throw mx::exception<verboseT>( error_t::paramnotset, "ao system is not set (m_turbAtmo->m_aosys is nullptr)" );
247 }
248
249 if( m_turbAtmo->nLayers() <= layerNo )
250 {
252 "atmosphere is not setup (m_turbAtmo->m_layers size is <= layerNo)" );
253 }
254
255 if( m_level == 0 )
256 {
257 N = 0;
258 }
259 else
260 {
261 N = 36.0 + 32.0 * ( m_level - 1 );
262
263 if( m_outerSubHarmonics )
264 {
265 N += 20;
266 }
267 }
268
269 m_m.resize( 0 ); // resized dynamically below
270 m_n.resize( 0 );
271 m_sqrtPSD.resize( N );
272 m_noise.resize( N );
273
274 m_scrnSz = m_turbAtmo->layer( layerNo ).scrnSz();
275
276 if( N == 0 )
277 {
278
279 m_modes.clear();
280 this->setChangePoint();
281 return;
282 }
283
284 realT r0 = m_turbAtmo->aosys()->atm.r_0( m_turbAtmo->aosys()->lam_sci() );
285 realT D = m_turbAtmo->aosys()->D();
286 uint32_t wfSz = m_turbAtmo->wfSz();
287
288 realT beta = 0.0218 / pow( r0, 5. / 3. ) / pow( ( D / wfSz ) * m_scrnSz, 2 );
289
290 realT sqrt_alpha = 0.5 * 11. / 3.;
291
292 realT L0 = m_turbAtmo->aosys()->atm.L_0( layerNo );
293
294 realT L02;
295 if( L0 > 0 )
296 L02 = 1.0 / pow( L0, 2 );
297 else
298 L02 = 0;
299
300 if( m_preCalc )
301 {
302 m_modes.resize( m_scrnSz, m_scrnSz, N );
303 }
304
305 std::vector<realT> scs;
306
307 if( m_outerSubHarmonics )
308 {
309 realT sc = 0.25;
310
311 m_m = std::vector<realT>( { -1.25, -0.75, -0.25, 0.25, 0.75, 1.25, -1.25, 1.25, -1.25, 1.25,
312 -1.25, 1.25, -1.25, 1.25, -1.25, -0.75, -0.25, 0.25, 0.75, 1.25 } );
313 m_n = std::vector<realT>( { -1.25, -1.25, -1.25, -1.25, -1.25, -1.25, -0.75, -0.75, -0.25, -0.25,
314 0.25, 0.25, 0.75, 0.75, 1.25, 1.25, 1.25, 1.25, 1.25, 1.25 } );
315 scs.resize( m_m.size(), sc );
316 }
317
318 for( int nl = 1; nl <= m_level; ++nl )
319 {
320 realT sc = pow( 3.0, -nl );
321
322 for( int mp = -3; mp < 3; ++mp )
323 {
324 for( int np = -3; np < 3; ++np )
325 {
326 if( nl < m_level )
327 {
328 if( mp == -1 )
329 {
330 if( np == -1 || np == 0 )
331 continue;
332 }
333 else if( mp == 0 )
334 {
335 if( np == -1 || np == 0 )
336 continue;
337 }
338 }
339
340 m_m.push_back( sc * ( mp + 0.5 ) );
341 m_n.push_back( sc * ( np + 0.5 ) );
342 scs.push_back( sc );
343 }
344 }
345 }
346
347 int n = 0;
348 for( int n = 0; n < m_m.size(); ++n )
349 {
350 realT k = sqrt( ( pow( m_m[n], 2 ) + pow( m_n[n], 2 ) ) ) / ( ( D / wfSz ) * m_scrnSz );
351
352 realT tpsd = beta / pow( k * k + L02, sqrt_alpha );
353
354 realT Ppiston = 0;
355 realT Ptiptilt = 0;
356 if( m_turbAtmo->aosys()->psd.subPiston() )
357 {
358 Ppiston = pow( 2 * math::func::jinc( math::pi<realT>() * k * D ), 2 );
359 }
360
361 if( m_turbAtmo->aosys()->psd.subTipTilt() )
362 {
363 Ptiptilt = pow( 4 * math::func::jincN( 2, math::pi<realT>() * k * D ), 2 );
364 }
365
366 m_sqrtPSD[n] = scs[n] * sqrt( tpsd * ( 1 - Ppiston - Ptiptilt ) );
367
368 if( m_preCalc )
369 {
370 for( int cc = 0; cc < m_scrnSz; ++cc )
371 {
372 realT np = cc - 0.5 * m_scrnSz;
373 for( int rr = 0; rr < m_scrnSz; ++rr )
374 {
375 realT mp = rr - 0.5 * m_scrnSz;
376 m_modes.image( n )( rr, cc ) =
377 m_sqrtPSD[n] * cos( math::two_pi<realT>() * ( m_m[n] * mp + m_n[n] * np ) / m_scrnSz );
378 }
379 }
380 }
381 }
382
383 this->setChangePoint();
384}
385
386template <typename turbAtmosphereT, class verboseT>
388{
389 if( m_level == 0 )
390 {
391 return;
392 }
393
394 if( m_turbAtmo == nullptr )
395 {
396 throw mx::exception<verboseT>( error_t::paramnotset, "atmosphere is not set (m_turbAtmo is nullptr)" );
397 }
398
399 if( this->isChanged() )
400 {
401 throw mx::exception<verboseT>( error_t::invalidconfig, "configuration has changed but not re-initialized" );
402 }
403
404 if( scrn.rows() != m_scrnSz || scrn.cols() != m_scrnSz )
405 {
406 throw mx::exception<verboseT>( error_t::sizeerr, "input screen is not the right size" );
407 }
408
409 // Check that we're allocated
410 if( m_preCalc )
411 {
412 if( m_modes.rows() != scrn.rows() || m_modes.cols() != scrn.cols() || m_modes.planes() != m_noise.size() )
413 {
414 throw mx::exception<verboseT>( error_t::sizeerr, "modes cube wrong size, call initGrid()." );
415 }
416 }
417 else
418 {
419 if( m_noise.size() != m_m.size() || m_noise.size() != m_n.size() || m_sqrtPSD.size() != m_noise.size() )
420 {
421 throw mx::exception<verboseT>( error_t::sizeerr, "vectors not allocated, call initGrid()." );
422 }
423 }
424
425 // Now fill in the noise
426 for( size_t n = 0; n < m_noise.size(); ++n )
427 {
428 m_noise[n] = 2 * m_turbAtmo->normVar();
429 }
430
431#pragma omp parallel for
432 for( int cc = 0; cc < m_scrnSz; ++cc )
433 {
434 realT np = cc - 0.5 * m_scrnSz;
435 for( int rr = 0; rr < m_scrnSz; ++rr )
436 {
437 realT mp = rr - 0.5 * m_scrnSz;
438
439 if( m_preCalc )
440 {
441 for( unsigned n = 0; n < m_noise.size(); ++n )
442 {
443 scrn( rr, cc ) += m_noise[n] * m_modes.image( n )( rr, cc );
444 }
445 }
446 else
447 {
448 for( unsigned n = 0; n < m_m.size(); ++n )
449 {
450 scrn( rr, cc ) += m_noise[n] * m_sqrtPSD[n] *
451 cos( math::two_pi<realT>() * ( m_m[n] * mp + m_n[n] * np ) / m_scrnSz );
452 }
453 }
454 }
455 }
456}
457
458template <typename turbAtmosphereT, class verboseT>
460{
461 m_m.clear();
462 m_n.clear();
463 m_sqrtPSD.clear();
464 m_noise.clear();
465 m_modes.resize( 0, 0, 0 );
466
467 this->changed();
468}
469
470} // namespace sim
471} // namespace AO
472} // namespace mx
473
474#endif // mx_AO_sim_turbSubHarm_hpp
A class to manage low-frequency sub-harmonic phase screen generation in atmospheric turbulence.
bool m_outerSubHarmonics
Whether or not to include the outer subharmonics.
void deinit()
Deallocate memory.
improc::eigenCube< realT > m_modes
the pre-calculated modes
uint32_t level()
Get the subharmonic level.
bool preCalc()
Get whether or not the modes are pre-calculated.
uint32_t m_scrnSz
The wavefront screen size from the layer being simulated.
std::vector< realT > m_m
m-coordinate fractional spatial frequency indices of the subharmonics
std::vector< realT > m_noise
Vector of Gaussian deviates prepared for each screen generation.
bool outerSubHarmonics()
Get whether or not the outer subharmonics are included.
turbAtmosphereT * turbAtmo()
Get the pointer to the AO atmosphere.
std::vector< realT > m_n
n-coordinate fractional spatial frequency indices of the subharmonics
turbAtmosphereT * m_turbAtmo
Pointer to the parent atmosphere object.
unsigned m_level
The subharmonic level to apply.
bool m_preCalc
whether or not the modes are pre-calculated.
void initGrid(uint32_t layerNo)
Allocate needed memory and initialize the subharmonic transform.
void screen(improc::eigenImage< realT > &scrn)
Generate a realization of the subharmonic phase screen and add it to the input screen.
A simple class to track member data changes.
Augments an exception with the source file and line.
Definition exception.hpp:42
An image cube with an Eigen-like API.
Definition eigenCube.hpp:30
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
@ sizeerr
A size was invalid or calculated incorrectly.
@ paramnotset
A parameter was not set.
@ invalidconfig
A config setting was invalid.
T2 jincN(const T1 &v, const T2 &x)
The JincN function.
Definition jinc.hpp:112
T jinc(const T &x)
The Jinc function.
Definition jinc.hpp:61
constexpr floatT six_fifths()
Return 6/5 in the specified precision.
The mxlib c++ namespace.
Definition mxlib.hpp:37
Declaration and definition of a turbulent atmosphere.