LCOV - code coverage report
Current view: top level - include/ao/analysis - aoSystem.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 55.4 % 1028 570
Test Date: 2026-08-30 22:23:37 Functions: 53.4 % 116 62

            Line data    Source code
       1              : /** \file aoSystem.hpp
       2              :  * \author Jared R. Males (jaredmales@gmail.com)
       3              :  * \brief Declares and defines an analytical AO system
       4              :  * \ingroup mxAO_files
       5              :  *
       6              :  */
       7              : 
       8              : //***********************************************************************//
       9              : // Copyright 2015-2022 Jared R. Males (jaredmales@gmail.com)
      10              : //
      11              : // This file is part of mxlib.
      12              : //
      13              : // mxlib is free software: you can redistribute it and/or modify
      14              : // it under the terms of the GNU General Public License as published by
      15              : // the Free Software Foundation, either version 3 of the License, or
      16              : // (at your option) any later version.
      17              : //
      18              : // mxlib is distributed in the hope that it will be useful,
      19              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      20              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      21              : // GNU General Public License for more details.
      22              : //
      23              : // You should have received a copy of the GNU General Public License
      24              : // along with mxlib.  If not, see <http://www.gnu.org/licenses/>.
      25              : //***********************************************************************//
      26              : 
      27              : #ifndef aoSystem_hpp
      28              : #define aoSystem_hpp
      29              : 
      30              : #include "../../mxlib.hpp"
      31              : #include "../../math/constants.hpp"
      32              : #include "../../math/roots.hpp"
      33              : 
      34              : #include "aoConstants.hpp"
      35              : 
      36              : #include "aoAtmosphere.hpp"
      37              : #include "aoPSDs.hpp"
      38              : #include "aoWFS.hpp"
      39              : 
      40              : #define FITTING_ERROR_NO 0
      41              : #define FITTING_ERROR_ZERO 1
      42              : #define FITTING_ERROR_X 2
      43              : #define FITTING_ERROR_Y 3
      44              : 
      45              : namespace mx
      46              : {
      47              : namespace AO
      48              : {
      49              : namespace analysis
      50              : {
      51              : 
      52              : /// Describes an analytic adaptive optics (AO) system.
      53              : /**
      54              :  * Templatized by the turbulence power spectral density (PSD).
      55              :  *
      56              :  * \tparam realT the floating point type used for all calculations
      57              :  * \tparam inputSpecT specifies the turbulence spatial PSD type
      58              :  * \tparam iosT is an output stream type with operator \<\< defined (default is std::ostream)
      59              :  *
      60              :  * \ingroup mxAOAnalytic
      61              :  */
      62              : template <typename _realT, class _inputSpectT, typename iosT = std::ostream>
      63              : class aoSystem
      64              : {
      65              :     typedef verbose::d verboseT;
      66              : 
      67              :   public:
      68              :     typedef _realT realT;
      69              :     typedef _inputSpectT inputSpectT;
      70              :     typedef aoAtmosphere<realT> aoAtmosphereT;
      71              : 
      72              :     aoAtmosphereT atm;
      73              :     inputSpectT psd;
      74              : 
      75              :   protected:
      76              :     realT m_D{ 0 };                  ///< Telescope diameter [m]
      77              : 
      78              :     std::vector<realT> m_d_min{ 0 }; ///< Minimum AO system actuator pitch [m].  One per WFS mode.
      79              : 
      80              :     realT m_d_opt{ 0 };              ///< Current optimum AO system actuator pitch [m]
      81              :     bool m_optd{ false }; ///< Flag controlling whether actuator pitch is optimized (true) or just uses m_d_min (false).
      82              :                           ///< Default: true.
      83              :     realT m_optd_delta{ 1.0 }; ///< The fractional change from d_min used in optimization.  Set to 1 for integer
      84              :                                ///< binnings, > 1 for finer sampling.
      85              : 
      86              :     wfs<realT, iosT> *m_wfsBeta{ nullptr }; ///< The WFS beta_p class.
      87              :     bool m_ownWfsBeta{ false };             ///< Flag indicating if the WFS beta_p pointer is owned by this instance.
      88              : 
      89              :     realT m_opticalGain{ 1 };               /**< The optical gain of the WFS, 0-1. Treated as a sensitivity
      90              :                                                  reduction in measurement noise.  Default 1.*/
      91              : 
      92              :   protected:
      93              :     realT m_lam_wfs{ 0 }; ///< WFS wavelength [m]
      94              : 
      95              :     // WFS detector parameters, which may be a function of binning
      96              :     std::vector<realT> m_npix_wfs{ 0 };  ///< Number of WFS pixels.  One per WFS mode.
      97              :     std::vector<realT> m_ron_wfs{ 0 };   ///< WFS readout noise [electrons/pix].  One per WFS mode.
      98              :     std::vector<realT> m_Fbg{ 0 };       ///< Background flux, [photons/sec/pixel].  One per WFS mode.
      99              :     std::vector<realT> m_minTauWFS{ 0 }; ///< Minimum WFS exposure time [sec].  One per WFS mode.
     100              : 
     101              :     bool m_bin_npix{ true }; ///< Flag controlling whether or not to bin WFS pixels according to the actuator spacing.
     102              : 
     103              :     int m_bin_opt{ 0 };      /**< The optimum binning factor.  If WFS modes are used, this is the mode
     104              :                                   index (0 to N-1).  If not, it is 1 minus the pixel binning factor.
     105              :                                   It is always 1 minus the actuator binning factor. */
     106              : 
     107              :     realT m_tauWFS{ 0 };     ///< Actual WFS exposure time [sec]
     108              : 
     109              :     realT m_deltaTau{ 0 };   ///< Loop latency [sec]
     110              : 
     111              :     bool m_optTau{ true };   ///< Flag controlling whether optimum integration time is calculated (true) enforcing
     112              :                              ///< m_minTauWFS, or if m_tauWFS is used (false). Default: true.
     113              : 
     114              :     realT m_lam_sci{ 0 };    ///< Science wavelength [m]
     115              : 
     116              :     realT m_zeta{ 0 };       ///<  Zenith angle [radians]
     117              :     realT m_secZeta{ 1 };    ///< Secant of the Zenith angle (calculated)
     118              : 
     119              :     int m_fit_mn_max{ 100 }; ///< Maximum spatial frequency index to use for fitting error calculation.
     120              : 
     121              :     bool m_circularLimit{ false }; ///< Flag to indicate that the spatial frequency limit is circular, not square.
     122              : 
     123              :     realT m_spatialFilter_ku{ std::numeric_limits<realT>::max() }; ///< The spatial filter cutoff in u, [m^-1]
     124              :     realT m_spatialFilter_kv{ std::numeric_limits<realT>::max() }; ///< The spatial filter cutoff in v, [m^-1]
     125              : 
     126              :     realT m_ncp_wfe{ 0 };                                          ///< Static WFE [m rms]
     127              :     realT m_ncp_alpha{ 2 };                      ///< Power-law exponent for the NCP aberations.  Default is 2.
     128              : 
     129              :     realT m_F0{ 0 };                             ///< 0 mag flux from star at WFS [photons/sec]
     130              : 
     131              :     realT m_starMag{ 0 };                        ///< The magnitude of the star.
     132              : 
     133              :     bool m_specsChanged{ true };                 ///< Flag to indicate that a specification has changed.
     134              :     bool m_dminChanged{ true };                  ///< Flag to indicate that d_min has changed.
     135              : 
     136              :     Eigen::Array<int, -1, -1> m_controlledModes; ///< Map of which modes are under control.  Set by calcStrehl.
     137              : 
     138              :     realT m_wfeMeasurement{ 0 };                 ///< Total WFE due to measurement a error [rad^2 at m_lam_sci]
     139              :     realT m_wfeTimeDelay{ 0 };                   ///< Total WFE due to time delay [rad^2 at m_lam_sci]
     140              :     realT m_wfeFitting{ 0 };                     ///< Total WFE due to fitting error [rad^2 at m_lam_sci]
     141              :     realT m_wfeChromScintOPD{ 0 }; ///< Total WFE due to the chromaticity of scintillation OPD [rad^2 at lam_sc]
     142              :     realT m_wfeChromIndex{ 0 };    ///< Total WFE due to the chromaticity of the index of refraction [rad^2 at lam_Sci]
     143              :     realT m_wfeAnisoOPD{ 0 };      ///< Total WFE due to dispersive anisoplanatism OPD.
     144              : 
     145              :     realT m_wfeNCP{ 0 };           ///< Total WFE due to NCP errors [rad^2 at m_lam_sci]
     146              : 
     147              :     realT m_wfeVar{ 0 };           ///< The WFE variance, in meters^2.  Never use this directly, instead use wfeVar().
     148              :     realT m_strehl{ 0 }; ///< Strehl ratio, a calculated quantity.  Never use this directdy, instead use strehl().
     149              : 
     150              :   public:
     151              :     /// Default c'tor
     152              :     /** Calls initialize().
     153              :      */
     154              :     aoSystem();
     155              : 
     156              :     /// Destructor
     157              :     ~aoSystem();
     158              : 
     159              :     /// Load the default parameters from Guyon, 2005 \cite guyon_2005.
     160              :     /**
     161              :      *
     162              :      */
     163              :     void loadGuyon2005();
     164              : 
     165              :     /// Load parameters corresponding to the MagAO-X system.
     166              :     void loadMagAOX();
     167              : 
     168              :     /// Load parameters corresponding to the G-MagAO-X system.
     169              :     void loadGMagAOX();
     170              : 
     171              :     /// Set the value of the primary mirror diameter.
     172              :     /**
     173              :      */
     174              :     void D( realT nD /**< [in] is the new value of m_D. */ );
     175              : 
     176              :     /// Get the value of the primary mirror diamter
     177              :     /**
     178              :      * \returns the current value of m_D.
     179              :      */
     180              :     realT D();
     181              : 
     182              :     /// Set the minimum subaperture sampling for each WFS mode.
     183              :     /** This sets the vector
     184              :      */
     185              :     void d_min( const std::vector<realT> &nd /**< [in] is the new values of m_d_min */ );
     186              : 
     187              :     /// Set the minimum subaperture sampling for a given WFS mode
     188              :     /** This sets the entry in the vector, if it has the proper length.
     189              :      */
     190              :     void d_min( int idx, ///< [in] the index of the WFS mode
     191              :                 realT nd ///< [in] is the new value of m_d_min[idx]
     192              :     );
     193              : 
     194              :     /// Get the minimum subaperture sampling for a given WFS mode
     195              :     /**
     196              :      * \returns the current value of m_d_min[idx]
     197              :      */
     198              :     realT d_min( size_t idx /**< [in] the index of the WFS mode.*/ );
     199              : 
     200              :     /// Get the minimum subaperture sampling for all WFS modes.
     201              :     /**
     202              :      * \returns the current value of m_d_min.
     203              :      */
     204              :     std::vector<realT> d_min();
     205              : 
     206              :     /// Set whether or not the value of d is optimized or just set to m_d_min.
     207              :     /**
     208              :      */
     209              :     void optd( bool od /**< [in] is the new value of m_optd */ );
     210              : 
     211              :     /// Get the value of m_optd.
     212              :     /**
     213              :      * \returns the new value of m_optd_delta.
     214              :      */
     215              :     bool optd();
     216              : 
     217              :     /// Set the fractional change in actuator spacing for optimization.
     218              :     /** Sets the fraction of m_d_min by which the optimizer changes actautor spacing.
     219              :      */
     220              :     void optd_delta( realT odd /**< [in] is the new value of m_optd_delta */ );
     221              : 
     222              :     /// Get the value of the fractional change in actuator spacing for optimization..
     223              :     /**
     224              :      * \returns the value of m_optd_delta.
     225              :      */
     226              :     realT optd_delta();
     227              : 
     228              :     /// Set the WFS Beta pointer
     229              :     /** The WFS beta parameter determines the photon noise sensitivity.
     230              :      *
     231              :      * \tparam wfsT is a type derived from ao::analysis::wfs
     232              :      */
     233              :     template <typename wfsT>
     234              :     void wfsBeta( const wfsT &w /**< [in] an object derived from ao::analysis::wfs base class*/ );
     235              : 
     236              :     /// Set the WFS Beta pointer
     237              :     /** The WFS beta parameter determines the photon noise sensitivity.
     238              :      *
     239              :      * \tparam wfsT is a type derived from ao::analysis::wfs
     240              :      */
     241              :     template<typename wfsT>
     242              :    void wfsBeta( const wfsT * w /**< [in] pointer to an object derived from ao::analysis::wfs base class. If nullptr then a new object is allocated and managed.*/);
     243              : 
     244              :     /// Get the WFS Beta pointer
     245              :     /**
     246              :      * \returns a pointer to the current m_wfsBeta
     247              :      */
     248              :     wfs<realT, iosT> *wfsBeta();
     249              : 
     250              :     /// Get the value of beta_p for a spatial frequency
     251              :     /** beta_p is the photon noise sensitivity of the WFS.
     252              :      *
     253              :      * \returns beta_p as calculated by the WFS.
     254              :      */
     255              :     realT beta_p( realT m, ///< [in] the spatial frequency index
     256              :                   realT n  ///< [in] the spatial frequency index
     257              :     );
     258              : 
     259              :     /// Get the value of beta_r for a spatial frequency
     260              :     /** beta_r is the read noise sensitivity of the WFS.
     261              :      *
     262              :      * \returns beta_r as calculated by the WFS.
     263              :      */
     264              :     realT beta_r( realT m, ///< [in] the spatial frequency index
     265              :                   realT n  ///< [in] the spatial frequency index
     266              :     );
     267              : 
     268              :     /// Set the value of the Optical Gain.
     269              :     /**
     270              :      */
     271              :     void opticalGain( realT og /**< [in] the new value of m_opticalGain*/ );
     272              : 
     273              :     /// Get the value of the optical gain
     274              :     /** Optical gain is applied in the WFS noise calculation
     275              :      *
     276              :      * \returns the current value of m_opticalGain
     277              :      */
     278              :     realT opticalGain();
     279              : 
     280              :     /// Set the value of the WFS wavelength.
     281              :     /**
     282              :      */
     283              :     void lam_wfs( realT nlam /**< [in] is the new value of m_lam_wfs */ );
     284              : 
     285              :     /// Get the value of the WFS wavelength.
     286              :     /**
     287              :      * \returns the current value of m_lam_wfs.
     288              :      */
     289              :     realT lam_wfs();
     290              : 
     291              :     /// Set the number of pixels in the WFS for each WFS mode
     292              :     /** This sets the vector.
     293              :      */
     294              :     void npix_wfs( const std::vector<realT> &npix /**< [in] is the new values of m_npix_wfs */ );
     295              : 
     296              :     /// Set the number of pixels in the WFS for a given WFS mode
     297              :     /** This sets the entry in the vector, if it has the proper length.
     298              :      */
     299              :     void npix_wfs( int idx,   ///< [in] the index of the WFS mode
     300              :                    realT npix ///< [in] is the new value of m_npix_wfs[idx]
     301              :     );
     302              : 
     303              :     /// Get the number of pixels in the WFS for a given WFS mode
     304              :     /**
     305              :      * \returns the current value of m_npix_wfs[idx]
     306              :      */
     307              :     realT npix_wfs( size_t idx /**< [in] the index of the WFS mode.*/ );
     308              : 
     309              :     /// Get the number of pixels in the WFS for all WFS modes
     310              :     /**
     311              :      * \returns the current value of m_npix_wfs
     312              :      */
     313              :     std::vector<realT> npix_wfs();
     314              : 
     315              :     /// Set the value of the WFS readout noise for each WFS mode
     316              :     /** This sets the vector.
     317              :      */
     318              :     void ron_wfs( const std::vector<realT> &nron /**< [in] is the new value of m_ron_wfs */ );
     319              : 
     320              :     /// Set the value of the WFS readout noise for a given bining mode
     321              :     /** This sets the entry in the vector if the vector has the proper length
     322              :      */
     323              :     void ron_wfs( int idx,   ///< [in] the index of the WFS mode
     324              :                   realT nron ///< [in] is the new value of m_ron_wfs [idx]
     325              :     );
     326              : 
     327              :     /// Get the value of the WFS readout noise for a given WFS mode
     328              :     /**
     329              :      * \returns the current value of m_ron_wfs[idx]
     330              :      */
     331              :     realT ron_wfs( size_t idx /**< [in] the index of the WFS mode.*/ );
     332              : 
     333              :     /// Get the value of the WFS readout noise for all WFS modes
     334              :     /**
     335              :      * \returns the current value of m_ron_wfs
     336              :      */
     337              :     std::vector<realT> ron_wfs();
     338              : 
     339              :     /// Set the value of the background fluxes.
     340              :     /** This sets the value of the background flux for each WFS mode
     341              :      */
     342              :     void Fbg( const std::vector<realT> &fbg /**< [in] is the new value of m_Fbg */ );
     343              : 
     344              :     /// Set a single value of the background flux for a given WFS mode
     345              :     /** This sets the entry in the vector if the vector has the proper length
     346              :      */
     347              :     void Fbg( int idx,  ///< [in] the index of the WFS mode
     348              :               realT fbg ///< [in] is the new value of m_Fbg[idx]
     349              :     );
     350              : 
     351              :     /// Get the value of the background flux for a given WFS mode
     352              :     /**
     353              :      * \returns m_Fbg[idx]
     354              :      */
     355              :     realT Fbg( size_t idx /**< [in] the index of the WFS mode.*/ );
     356              : 
     357              :     /// Get the value of the background flux for all WFS modes
     358              :     /**
     359              :      * \returns the current value of m_Fbg
     360              :      */
     361              :     std::vector<realT> Fbg();
     362              : 
     363              :     /// Set the value of the minimum WFS exposure times.
     364              :     /** This sets the vector of the minimum WFS exposure times
     365              :      */
     366              :     void minTauWFS( const std::vector<realT> &ntau /**< [in] is the new value of m_minTauWFS */ );
     367              : 
     368              :     /// Set a single value of the minimum WFS exposure time for a given WFS mode.
     369              :     /** This sets the entry in the vector if the vector has sufficient length.
     370              :      */
     371              :     void minTauWFS( size_t idx, ///< [in] the index of the WFS mode
     372              :                     realT ntau  ///< [in] is the new value of m_minTauWFS
     373              :     );
     374              : 
     375              :     /// Get the value of the minimum WFS exposure time for a given binning.
     376              :     /**
     377              :      * \returns the current value of m_minTauWFS[idx].
     378              :      */
     379              :     realT minTauWFS( size_t idx /**< [in] the index of the WFS mode.*/ );
     380              : 
     381              :     /// Get the values of the minimum WFS exposure time.
     382              :     /**
     383              :      * \returns the current value of m_minTauWFS.
     384              :      */
     385              :     std::vector<realT> minTauWFS();
     386              : 
     387              :     /// Set the value of the pixel binning flag
     388              :     /**
     389              :      */
     390              :     void bin_npix( bool bnp /**< [in] is the new value of m_bin_npix */ );
     391              : 
     392              :     /// Get the value of the pixel binngin flag
     393              :     /**
     394              :      * \returns
     395              :      */
     396              :     bool bin_npix();
     397              : 
     398              :     /// Get the value of the optimum binning factor/index.
     399              :     /** If WFS modes are used, this is the mode index (0 to N-1).  If not, it is 1 minus the pixel binning factor.
     400              :      * It is always 1 minus the actuator binning factor.
     401              :      * This calls opt_d() to perform the optimization if needed.
     402              :      *
     403              :      * \returns the current value of m_bin_opt.
     404              :      */
     405              :     int bin_opt();
     406              : 
     407              :     /// Set the value of the WFS exposure time.
     408              :     /**
     409              :      */
     410              :     void tauWFS( realT ntau /**< [in] is the new value of m_tauWFS */ );
     411              : 
     412              :     /// Get the value of the minimum WFS exposure time.
     413              :     /**
     414              :      * \returns the current value of m_tauWFS.
     415              :      */
     416              :     realT tauWFS();
     417              : 
     418              :     /// Set the value of m_deltaTau.
     419              :     /**
     420              :      */
     421              :     void deltaTau( realT ndel /**< [in] is the new value of m_deltaTau*/ );
     422              : 
     423              :     /// Get the value of m_deltaTau.
     424              :     /**
     425              :      * \returns the current value of m_deltaTau.
     426              :      */
     427              :     realT deltaTau();
     428              : 
     429              :     /// Set the value of m_optTau.
     430              :     /**
     431              :      */
     432              :     void optTau( bool ot /**< [in] is the new value of m_optTau */ );
     433              : 
     434              :     /// Get the value of m_optTau.
     435              :     /**
     436              :      * \returns the current value of m_optTau.
     437              :      */
     438              :     bool optTau();
     439              : 
     440              :     /// Set the science wavelength.
     441              :     /**
     442              :      */
     443              :     void lam_sci( realT nlam /**< [in] is the new value of m_lam_sci */ );
     444              : 
     445              :     /// Get the science wavelength.
     446              :     /**
     447              :      * \returns the current value of m_lam_sci.
     448              :      */
     449              :     realT lam_sci();
     450              : 
     451              :     /// Set the zenith angle, and its secant.
     452              :     /**
     453              :      */
     454              :     void zeta( realT nz /**< [in] The new value of m_zeta */ );
     455              : 
     456              :     /// Get the zenith angle
     457              :     /**
     458              :      * \return the current value of m_zeta
     459              :      */
     460              :     realT zeta();
     461              : 
     462              :     /// Get the zecant of the zenith angle
     463              :     /**
     464              :      * \return the current value of m_secZeta
     465              :      */
     466              :     realT secZeta();
     467              : 
     468              :     /// Set the value of m_fit_mn_max
     469              :     /**
     470              :      */
     471              :     void fit_mn_max( int mnm /**< [in] is the new value of m_fit_mn_max */ );
     472              : 
     473              :     /// Get the value of m_fit_mn_max
     474              :     /**
     475              :      */
     476              :     int fit_mn_max();
     477              : 
     478              :     /// Set the value of the circularLimit flag
     479              :     /** If this is true, then the spatial frequency limits are treated as a circle
     480              :      * rather than a square.  This will create a circular dark hole.
     481              :      */
     482              :     void circularLimit( bool cl /**< [in] the new value of the circularLimit flag*/ );
     483              : 
     484              :     /// Get the value of the circularLimit flag
     485              :     /**
     486              :      * \returns the current value of m_circularLimit
     487              :      */
     488              :     bool circularLimit();
     489              : 
     490              :     /// Set the value of spatialFilter_ku
     491              :     /**
     492              :      */
     493              :     void spatialFilter_ku( realT ku /**< [in] is the new value of spatialFilter_ku*/ );
     494              : 
     495              :     /// Get the value of spatialFilter_ku
     496              :     /** \returns the current value of m_spatialFilter_ku
     497              :      */
     498              :     realT spatialFilter_ku();
     499              : 
     500              :     /// Set the value of spatialFilter_kv
     501              :     /**
     502              :      */
     503              :     void spatialFilter_kv( realT kv /**< [in] is the new value of spatialFilter_kv*/ );
     504              : 
     505              :     /// Get the value of spatialFilter_kv
     506              :     /** \returns the current value of m_spatialFilter_kv
     507              :      */
     508              :     realT spatialFilter_kv();
     509              : 
     510              :     /// Set the value of the non-common path WFE.
     511              :     /**
     512              :      */
     513              :     void ncp_wfe( realT nwfe /**< [in] is the new value of m_ncp_wfe*/ );
     514              : 
     515              :     /// Get the value of the non-common path WFE.
     516              :     /**
     517              :      * \returns the current value of m_ncp_wfe.
     518              :      */
     519              :     realT ncp_wfe();
     520              : 
     521              :     /// Set the value of the non-common path WFE PSD index.
     522              :     /**
     523              :      */
     524              :     void ncp_alpha( realT alpha /**< [in] is the new value of m_ncp_alpha*/ );
     525              : 
     526              :     /// Get the value of the non-common path WFE PSD index.
     527              :     /**
     528              :      * \returns the current value of m_ncp_alpha.
     529              :      */
     530              :     realT ncp_alpha();
     531              : 
     532              :     /// Set the value of the 0 magnitude photon rate
     533              :     /** This is the photon rate at the WFS, photons/sec.
     534              :      *
     535              :      */
     536              :     void F0( realT nF0 /**< [in] is the new value of m_F0.*/ );
     537              : 
     538              :     /// Get the value of the 0 magnitude photon rate
     539              :     /**
     540              :      * \returns the current value of m_F0.
     541              :      */
     542              :     realT F0();
     543              : 
     544              :     /// Set the value of the Star's magnitude
     545              :     /**
     546              :      */
     547              :     void starMag( realT nmag /**< [in] is the new value of m_starMag.*/ );
     548              : 
     549              :     /// Get the value of the Star's magnitude
     550              :     /**
     551              :      * \returns the current value of m_starMag
     552              :      */
     553              :     realT starMag();
     554              : 
     555              :     /// The photon flux at a given star magnitude.
     556              :     /**
     557              :      * \returns the photon flux of the star in the bandpass assumed by F0.
     558              :      */
     559              :     realT Fg( realT mag /**< [in] is the magnitude of the star. */ );
     560              : 
     561              :     /// Get the photon rate at the current Star magnitude.
     562              :     /** Calculates \f$ F_\gamma = F_0 10^{-0.4 m} \f$ where \f$ F_0 \f$ is m_F0 and \f$ m \f$ is m_starMag.
     563              :      *
     564              :      * \returns the current value of the current photon rate.
     565              :      */
     566              :     realT Fg();
     567              : 
     568              :     /// Access the array of controlledModes
     569              :     /** This array indicates which modes are controlled after optimizeStrehl chooses them.
     570              :      *
     571              :      * \returns a const reference to m_controlledModes.
     572              :      */
     573              :     const Eigen::Array<int, -1, -1> &controlledModes();
     574              : 
     575              :     /** \name Measurement Error
     576              :      * Calculating the WFE due to WFS measurement noise.
     577              :      * @{
     578              :      */
     579              : 
     580              :     /// Calculate the terms of the signal to noise ratio squared (S/N)^2 for the WFS measurement
     581              :     /** The S/N squared is
     582              :        \f[
     583              :        (S/N)^2 = \frac{ F_\gamma^2 \tau_{wfs}^2 }{ F_\gamma \tau_{wfs} + n_{pix} F_{bg} \tau_{wfs} + n_{pix}
     584              :       \sigma_{ron}^2 } \f]
     585              : 
     586              :       *
     587              :       * \returns the S/N squared
     588              :       */
     589              :     realT
     590              :     signal2Noise2( realT &Nph,    ///< [out] the number of photons
     591              :                    realT tau_wfs, ///< [in] specifies the WFS exposure time.  If 0, then optimumTauWFS is used
     592              :                    realT d,       ///< [in] the actuator spacing in meters, used if binning WFS pixels
     593              :                    int b ///< [in] the binning parameter.  Either the WFS mode index, or the binning factor minus 1.
     594              :     );
     595              : 
     596              :     /// Calculate the measurement noise at a spatial frequency and specified actuator spacing
     597              :     /** Calculates the wavefront phase variance due measurement noise at \f$ k = (m/D)\hat{u} + (n/D)\hat{v} \f$.
     598              :      *
     599              :      * \returns the measurement noise in rad^2 rms at the science wavelength
     600              :      */
     601              :     realT
     602              :     measurementError( realT m, ///< [in] specifies the u component of the spatial frequency
     603              :                       realT n, ///< [in] specifies the v component of the spatial frequency
     604              :                       realT d, ///< [in] the actuator spacing in meters
     605              :                       int b ///< [in] the binning parameter.  Either the WFS mode index, or the binning factor minus 1.
     606              :     );
     607              : 
     608              :     /// Calculate the total measurement error over all corrected spatial frequencies
     609              :     /** This totals the measurement WFE at the optimum actuator spacing and binning.
     610              :      *
     611              :      * \overload
     612              :      *
     613              :      * \returns the total WFE due to measurement error.
     614              :      */
     615              :     realT measurementErrorTotal();
     616              : 
     617              :     ///@}
     618              : 
     619              :     /** \name Time Delay Error
     620              :      * Calculating the WFE due to time delay.
     621              :      * @{
     622              :      */
     623              : 
     624              :     /// Calculate the time delay at a spatial frequency at the optimum exposure time and the specified actuator spacing
     625              :     /** Calculates the wavefront phase variance due to time delay at \f$ f = (m/D)\hat{u} + (n/D)\hat{v} \f$.
     626              :      *
     627              :      * \returns the measurement noise in rad^2 rms at the science wavelength
     628              :      */
     629              :     realT
     630              :     timeDelayError( realT m, ///< [in] specifies the u component of the spatial frequency
     631              :                     realT n, ///< [in] specifies the v component of the spatial frequency
     632              :                     realT d, ///< [in] the actuator spacing, in meters
     633              :                     int b    ///< [in] the binning parameter.  Either the WFS mode index, or the binning factor minus 1.
     634              :     );
     635              : 
     636              :     /// Calculate the time delay error over all corrected spatial frequencies
     637              :     /** This totals the time delay WFE at the optimum actuator spacing and binning.
     638              :      *
     639              :      * \overload
     640              :      *
     641              :      * \returns the total WFE due to time delay.
     642              :      */
     643              :     realT timeDelayErrorTotal();
     644              : 
     645              :     ///@}
     646              : 
     647              :     /** \name Fitting Error
     648              :      * Calculating the WFE due to uncorrected spatial frequencies.
     649              :      * @{
     650              :      */
     651              : 
     652              :     /// Calculate the fitting error at a specific spatial frequency.
     653              :     /**
     654              :      * \returns the fitting error in rad^2 rms at the science wavelength at (m,n).
     655              :      */
     656              :     realT fittingError( realT m, ///< [in] specifies the u component of the spatial frequency
     657              :                         realT n  ///< [in] specifies the v component of the spatial frequency
     658              :     );
     659              : 
     660              :     /// Calculate the total fitting error over all uncorrected spatial frequencies.
     661              :     /** This totals the fitting WFE for the optimum actuator spacing and binning.
     662              :      *
     663              :      * \overload
     664              :      *
     665              :      * \returns the total fitting error.
     666              :      */
     667              :     realT fittingErrorTotal();
     668              : 
     669              :     ///@}
     670              : 
     671              :     /** \name Chromatic Errors
     672              :      * Calculating the WFE due to chromaticity in scintillaion, index of refraction, and dispersion.
     673              :      * @{
     674              :      */
     675              : 
     676              :     /// Calculate the wavefront error due to scintillation chromaticity in the OPD at a spatial frequency.
     677              :     /** This totals the WFE from scintillation chromaticity in the OPD.
     678              :      *
     679              :      * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
     680              :      */
     681              :     realT chromScintOPDError( int m, ///< [in] specifies the u component of the spatial frequency
     682              :                               int n  ///< [in] specifies the v component of the spatial frequency
     683              :     );
     684              : 
     685              :     /// Calculate the wavefront error due to scintillation chromaticity in the OPD over all spatial frequencies.
     686              :     /** This totals the WFE from scintillation chromaticity in the OPD for the optimum actuator spacing.
     687              :      *
     688              :      * \overload
     689              :      *
     690              :      * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
     691              :      */
     692              :     realT chromScintOPDErrorTotal();
     693              : 
     694              :     /// Calculate the wavefront error due to scintillation chromaticity in amplitude for a given spatial frequency.
     695              :     /**
     696              :      * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
     697              :      */
     698              :     realT chromScintAmpError( int m, ///< [in] specifies the u component of the spatial frequency
     699              :                               int n  ///< [in] specifies the v component of the spatial frequency
     700              :     );
     701              : 
     702              :     /// Calculate the wavefront error due to scintillation chromaticity in amplitude over all spatial frequencies.
     703              :     /**
     704              :      * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
     705              :      */
     706              :     realT chromScintAmpError();
     707              : 
     708              :     /// Calculate the wavefront error due to chromaticity in the index of refraction at a given spatial frequency.
     709              :     /** This totals the WFE from chromaticity in the index of refraction.
     710              :      *
     711              :      * \overload
     712              :      *
     713              :      * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
     714              :      */
     715              :     realT chromIndexError( int m, ///< [in] specifies the u component of the spatial frequency
     716              :                            int n  ///< [in] specifies the v component of the spatial frequency
     717              :     );
     718              : 
     719              :     /// Calculate the wavefront error due to chromaticity in the index of refraction at a specific spatial frequency.
     720              :     /** This totals the WFE from chromaticity in the index of refraction for the optimum actuator spacing.
     721              :      *
     722              :      * \overload
     723              :      *
     724              :      * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
     725              :      */
     726              :     realT chromIndexErrorTotal();
     727              : 
     728              :     /// Calculate the wavefront error due to dispersive anisoplanatism in the OPD at a given spatial frequency
     729              :     /** This calculates the WFE from dispersive anisoplanatism in the OPD.
     730              :      *
     731              :      * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
     732              :      */
     733              :     realT dispAnisoOPDError( int m, ///< [in] specifies the u component of the spatial frequency
     734              :                              int n  ///< [in] specifies the v component of the spatial frequency
     735              :     );
     736              : 
     737              :     /// Calculate the wavefront error due to dispersive anisoplanatism in the OPD over all specific spatial frequencies.
     738              :     /** This totals the WFE from dispersive anisoplanatism in the OPD for the optimum actuator spacing.
     739              :      *
     740              :      * \overload
     741              :      *
     742              :      * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
     743              :      */
     744              :     realT dispAnisoOPDErrorTotal();
     745              : 
     746              :     /// Calculate the wavefront error due to dispersive anisoplanatism in the amplitude at a specific spatial frequency.
     747              :     /**
     748              :      * \returns the WFE in rad^2 rms at the science wavelength at (m,n).
     749              :      */
     750              :     realT dispAnisoAmpError();
     751              : 
     752              :     ///@}
     753              : 
     754              :     /** \name Optimum Parameters
     755              :      * Functions to calculate the optimum integration time and actuator spacing.
     756              :      *
     757              :      * @{
     758              :      */
     759              : 
     760              :     /// Calculate the optimum exposure time for a given spatial frequency at a specified actuator spacing
     761              :     /** Finds the optimum exposure time at \f$ k = (m/D)\hat{u} + (n/D)\hat{v} \f$.
     762              :      *
     763              :      * \todo Check inclusion of X in parameters
     764              :      *
     765              :      * \returns the optimum expsoure time.
     766              :      */
     767              :     realT
     768              :     optimumTauWFS( realT m, ///< [in] is the spatial frequency index in u
     769              :                    realT n, ///< [in] is the spatial frequency index in v
     770              :                    realT d, ///< [in] the actuator spacing, in meters
     771              :                    int b    ///< [in] the binning parameter.  Either the WFS mode index, or the binning factor minus 1.
     772              :     );
     773              : 
     774              :     /// Calculate the optimum exposure time for a given spatial frequency at the optimum actuator spacing.
     775              :     /** Finds the optimum exposure time at \f$ k = (m/D)\hat{u} + (n/D)\hat{v} \f$.
     776              :      * If optd == false this uses the minimum actuator spacing.
     777              :      *
     778              :      * \todo Check inclusion of X in parameters
     779              :      *
     780              :      * \returns the optimum expsoure time.
     781              :      */
     782              :     realT optimumTauWFS( realT m, ///< [in] is the spatial frequency index in u
     783              :                          realT n  ///< [in] is the spatial frequency index in v
     784              :     );
     785              : 
     786              :     /// Calculate the optimum actuator spacing.
     787              :     /** Finds the value of m_d_opt where the fitting error is less than than the combined time delay and measurement
     788              :      * error.
     789              :      *
     790              :      * \returns the current value of m_d_opt.
     791              :      */
     792              :     realT d_opt();
     793              : 
     794              :     /// @}
     795              : 
     796              :     /// Calculate the NCP variance at a spatial frequency.
     797              :     /** Finds the NCP variance at \f$ k = (m/D)\hat{u} + (n/D)\hat{v} \f$.
     798              :      *
     799              :      * \returns the NCP variance at k.
     800              :      */
     801              :     realT ncpError( int m, ///< [in] is the spatial frequency index in u
     802              :                     int n  ///< [in] is the spatial frequency index in v
     803              :     );
     804              : 
     805              :     /// Calculate the total NCP variance in rad^2.
     806              :     /**
     807              :      * \returns the total NCP variance.
     808              :      */
     809              :     realT ncpError();
     810              : 
     811              :     /** \name Overall WFE and Strehl Ratio
     812              :      * Functions to calculate the total WFE and Strehl ratio.
     813              :      *
     814              :      * @{
     815              :      */
     816              : 
     817              :   protected:
     818              :     /// Calculate the component WFE, total WFE, and Strehl ratio.
     819              :     /** This should only be called when something changes.
     820              :      */
     821              :     void calcStrehl();
     822              : 
     823              :   public:
     824              :     /// Get the current value of the total WFE variance.
     825              :     /** If no changes, merely returns m_wfeVar.  Calls calcStrehl if there are changes.
     826              :      *
     827              :      * \returns the current value of m_wfeVar;
     828              :      */
     829              :     realT wfeVar();
     830              : 
     831              :     /// Get the Strehl ratio for a given actuator pitch and WFS WFS mode.
     832              :     /**
     833              :       * Strehl is calculated using the extended Marechal approximation:
     834              :       *
     835              :       \f[
     836              :        S = e^{-\sigma_{wfe}^2}
     837              :        \f]
     838              :       * where \f$ \sigma_{wfe}^2 \f$ is the WFE for the spacing and binning
     839              :       *
     840              :       * \returns the strehl for these values
     841              :       */
     842              :     realT strehl( realT d, ///< [in] the actuator spacing, in meters
     843              :                   int b    ///< [in] the binning parameter.  Either the WFS mode index, or the binning factor minus 1.
     844              :     );
     845              : 
     846              :     /// Get the current Strehl ratio.
     847              :     /** If no changes, merely returns m_strehl.  Calls calcStrehl if there are changes.
     848              :       * Strehl is calculated using the extended Marechal approximation:
     849              :       *
     850              :       \f[
     851              :        S = e^{-\sigma_{wfe}^2}
     852              :        \f]
     853              :       * where \f$ \sigma_{wfe}^2 \f$ is the current value of m_wfeVar.
     854              :       *
     855              :       * \returns the current value of m_strehl.
     856              :       */
     857              :     realT strehl();
     858              : 
     859              :     /// @}
     860              : 
     861              :     /** \name Contrast
     862              :      * Calculating contrast
     863              :      *
     864              :      * @{
     865              :      */
     866              : 
     867              :     /// Worker function for raw contrast fuctions
     868              :     /** The logic in this calculation is the same regardless of component, except for the calculation of variance.
     869              :      * The varFunc function pointer is used to calculate the variance, otherwise  This handles the other details such
     870              :      * as Strehl normalization, fitting error (if appropriate), and bounds checking.
     871              :      *
     872              :      * \note this gives the raw PSD, it must be convolved with the PSF for the true contrast.
     873              :      *
     874              :      * \tparam varFuncT  is a function-pointer-to-member (of this class) with signature realT(*f)(realT, realT)
     875              :      */
     876              :     template <typename varFuncT>
     877              :     realT C_( int m,             ///< [in] is the spatial frequency index in u/
     878              :               int n,             ///< [in] is the spatial frequency index in v.
     879              :               bool normStrehl,   ///< [in] flag controls whether the contrast is normalized by Strehl ratio/
     880              :               varFuncT varFunc,  ///< [in] the variance function to use.
     881              :               int doFittingError ///< [in] flag to describe how fitting error should be considered for this term:
     882              :                                  ///< FITTING_ERROR_NO, FITTING_ERROR_ZERO, FITTING_ERROR_X, or FITTING_ERROR_Y.
     883              :     );
     884              : 
     885              :     /// Worker function for the contrast-map functions.
     886              :     /** The map calculation is the same for all terms, except for the calculation of variance at each pixel.
     887              :      * The Cfunc function pointer is used to actually get the contrast.
     888              :      *
     889              :      * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
     890              :      *
     891              :      * \tparam imageT is an Eigen-like image
     892              :      * \tparam CfuncT is a function-pointer-to-member (of this class) with signature realT (*f)(realT, realT, bool)
     893              :      */
     894              :     template <typename imageT, typename CfuncT>
     895              :     void C_Map( imageT &im,     ///< [out] the map image to be filled in.
     896              :                 CfuncT Cfunc,   ///< [in] the raw contrast function to use for filling in the map.
     897              :                 bool normStrehl ///< [in] flag controlling whether or not the map is normalized by Strehl
     898              :     );
     899              : 
     900              :     /// Calculate the residual variance due to uncorrected phase at a spatial frequency.
     901              :     /** Used to calculate contrast \ref C0().
     902              :      *
     903              :      * \returns variance at (m,n).
     904              :      */
     905              :     realT C0var( realT m, ///< [in] is the spatial frequency index in u
     906              :                  realT n  ///< [in] is the spatial frequency index in v
     907              :     );
     908              : 
     909              :     /// Calculate the contrast due to uncorrected phase, C0.
     910              :     /** Contrast C0 is the uncorrected phase, with the effects of scintillation included.  See Guyon (2005)
     911              :      * \cite guyon_2005 and the updated derivation in Males \& Guyon (2018) \cite males_guyon_2018
     912              :      *
     913              :      * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
     914              :      *
     915              :      * \returns C0.
     916              :      */
     917              :     realT C0( realT m,               ///< [in] is the spatial frequency index in u
     918              :               realT n,               ///< [in] is the spatial frequency index in v
     919              :               bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio
     920              :     );
     921              : 
     922              :     /// Calculate a 2D map of contrast C0
     923              :     /**
     924              :      * \tparam imageT is an Eigen-like image type.
     925              :      */
     926              :     template <typename imageT>
     927              :     void C0Map( imageT &map, ///< [in] the map image to be filled in with contrast
     928              :                 bool normStrehl );
     929              : 
     930              :     /// Calculate the residual variance due to uncorrected amplitude at a spatial frequency.
     931              :     /** Used to calculate contrast \ref C1().
     932              :      *
     933              :      * \returns variance at (m,n).
     934              :      */
     935              :     realT C1var( realT m, ///< [in] is the spatial frequency index in u
     936              :                  realT n  ///< [in] is the spatial frequency index in v
     937              :     );
     938              : 
     939              :     /// Calculate the contrast due to uncorrected amplitude, C1.
     940              :     /** Contrast C1 is the uncorrected amplitude due to scintillation.  See Guyon (2005) \cite guyon_2005, and the
     941              :      * updated derivation in Males \& Guyon (2018) \cite males_guyon_2018.
     942              :      *
     943              :      * \returns C0.
     944              :      */
     945              :     realT C1( realT m,               ///< [in] is the spatial frequency index in u
     946              :               realT n,               ///< [in] is the spatial frequency index in v
     947              :               bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio
     948              :     );
     949              : 
     950              :     /// Calculate a 2D map of contrast C1.
     951              :     /** The contrast is Strehl-normalized here.
     952              :      *
     953              :      * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
     954              :      *
     955              :      * \tparam imageT is an Eigen-like image type.
     956              :      */
     957              :     template <typename imageT>
     958              :     void C1Map( imageT &map, ///< [in] the map image to be filled in with contrast
     959              :                 bool normStrehl );
     960              : 
     961              :     /// Calculate the residual variance due to measurement and time delay errors in phase/OPD at a spatial frequency.
     962              :     /** Used to calculate contrast \ref C2().
     963              :      *
     964              :      * \returns variance at (m,n).
     965              :      */
     966              :     realT C2var( realT m, ///< [in] is the spatial frequency index in u
     967              :                  realT n  ///< [in] is the spatial frequency index in v
     968              :     );
     969              : 
     970              :     /// Calculate the contrast due to measurement and time delay errors in phase/OPD at a spatial frequency.
     971              :     /** Contrast C2 is just the total variance due to time delay and measurement errors,
     972              :      * divided by the Strehl ratio.   See Guyon (2005) \cite guyon_2005, and the updated
     973              :      * derivation in Males \& Guyon (2018) \cite males_guyon_2018.
     974              :      *
     975              :      * \returns C2.
     976              :      */
     977              :     realT C2( realT m,               ///< [in] is the spatial frequency index in u
     978              :               realT n,               ///< [in]  is the spatial frequency index in v
     979              :               bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio.
     980              :     );
     981              : 
     982              :     /// Calculate a 2D map of contrast \ref C2().
     983              :     /** The contrast is Strehl-normalized here.
     984              :      *
     985              :      *  \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
     986              :      *
     987              :      * \tparam imageT is an Eigen-like image type.
     988              :      */
     989              :     template <typename imageT>
     990              :     void C2Map( imageT &map, ///< [in] the map image to be filled in with contrast
     991              :                 bool normStrehl );
     992              : 
     993              :     /// Calculate the residual variance due to measurement and time delay errors in amplitude at a spatial frequency.
     994              :     /** Used to calculate contrast \ref C3().
     995              :      *
     996              :      * \returns variance at (m,n).
     997              :      */
     998              :     realT C3var( realT m, ///< [in] is the spatial frequency index in u
     999              :                  realT n  ///< [in] is the spatial frequency index in v
    1000              :     );
    1001              : 
    1002              :     /// Calculate the contrast due to measurement and time delay errors in amplitude at a spatial frequency.
    1003              :     /** Contrast C3 is just the total variance due to time delay and measurement errors,
    1004              :      * divided by the Strehl ratio.    See Guyon (2005) \cite guyon_2005, and the updated
    1005              :      * derivation in Males \& Guyon (2018) \cite males_guyon_2018.
    1006              :      *
    1007              :      * \returns C3.
    1008              :      */
    1009              :     realT C3( realT m,               ///< [in] is the spatial frequency index in u
    1010              :               realT n,               ///< [in]  is the spatial frequency index in v
    1011              :               bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio.
    1012              :     );
    1013              : 
    1014              :     /// Calculate a 2D map of contrast C3.
    1015              :     /** The contrast is Strehl-normalized here.
    1016              :      *
    1017              :      *  \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
    1018              :      *
    1019              :      * \tparam imageT is an Eigen-like image type.
    1020              :      */
    1021              :     template <typename imageT>
    1022              :     void C3Map( imageT &map, ///< [in] the map image to be filled in with contrast
    1023              :                 bool normStrehl );
    1024              : 
    1025              :     /// Calculate the residual variance due to scintilation-OPD chromaticity.
    1026              :     /** Used to calculate contrast \ref C4().
    1027              :      *
    1028              :      * \returns variance at (m,n).
    1029              :      */
    1030              :     realT C4var( realT m, ///< [in] is the spatial frequency index in u
    1031              :                  realT n  ///< [in] is the spatial frequency index in v
    1032              :     );
    1033              : 
    1034              :     /// Calculate the contrast due to scintilation-OPD chromaticity.
    1035              :     /** Contrast C4 is due to the chromaticity of scintillation, causing the ODP measurement to be slightly incorrect at
    1036              :      * the science wavelength. See Guyon (2005) \cite guyon_2005, and the updated derivation in Males \& Guyon (2018)
    1037              :      * \cite males_guyon_2018.
    1038              :      *
    1039              :      * \returns C4.
    1040              :      */
    1041              :     realT C4( realT m,               ///< [in] is the spatial frequency index in u
    1042              :               realT n,               ///< [in]  is the spatial frequency index in v
    1043              :               bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio.
    1044              :     );
    1045              : 
    1046              :     /// Calculate a 2D map of contrast C4
    1047              :     /** The contrast is Strehl-normalized here.
    1048              :      *
    1049              :      *  \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
    1050              :      *
    1051              :      * \tparam imageT is an Eigen-like image type.
    1052              :      */
    1053              :     template <typename imageT>
    1054              :     void C4Map( imageT &map, ///< [in] the map image to be filled in with contrast
    1055              :                 bool normStrehl );
    1056              : 
    1057              :     /// Calculate the residual variance due to to scintilation-amplitude chromaticity.
    1058              :     /** Used to calculate contrast \ref C5().
    1059              :      *
    1060              :      *  \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
    1061              :      *
    1062              :      * \returns variance at (m,n).
    1063              :      */
    1064              :     realT C5var( realT m, ///< [in] is the spatial frequency index in u
    1065              :                  realT n  ///< [in] is the spatial frequency index in v
    1066              :     );
    1067              : 
    1068              :     /// Calculate the contrast due to scintilation-amplitude chromaticity.
    1069              :     /** Contrast C5 is due to the chromaticity of scintillation, causing the amplitude measurement to be slightly
    1070              :      * incorrect at the science wavelength. See Guyon (2005) \cite guyon_2005, and the updated derivation in Males \&
    1071              :      * Guyon (2018) \cite males_guyon_2018.
    1072              :      *
    1073              :      * \returns C4.
    1074              :      */
    1075              :     realT C5( realT m,               ///< [in] is the spatial frequency index in u
    1076              :               realT n,               ///< [in]  is the spatial frequency index in v
    1077              :               bool normStrehl = true ///< [in] flag controls whether the contrast is normalized by Strehl ratio.
    1078              :     );
    1079              : 
    1080              :     /// Calculate a 2D map of contrast C5
    1081              :     /** The contrast is Strehl-normalized here.
    1082              :      *
    1083              :      *  \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
    1084              :      *
    1085              :      * \tparam imageT is an Eigen-like image type.
    1086              :      */
    1087              :     template <typename imageT>
    1088              :     void C5Map( imageT &map, ///< [in] the map image to be filled in with contrast
    1089              :                 bool normStrehl );
    1090              : 
    1091              :     /// Calculate the residual variance due to chromaticity of the index of refraction of air.
    1092              :     /** Used to calculate contrast \ref C6().
    1093              :      *
    1094              :      * \returns variance at (m,n).
    1095              :      */
    1096              :     realT C6var( realT m, ///< [in] is the spatial frequency index in u
    1097              :                  realT n  ///< [in] is the spatial frequency index in v
    1098              :     );
    1099              : 
    1100              :     /// Calculate the contrast due to chromaticity of the index of refraction of air.
    1101              :     /** Contrast C6 is due to the index of refraction of air being wavelength dependent,  causing the ODP measurement to
    1102              :      * be slightly incorrect at the science wavelength.   See Guyon (2005) \cite guyon_2005, and the updated derivation
    1103              :      * in Males \& Guyon (2018) \cite males_guyon_2018.
    1104              :      *
    1105              :      * \returns C6.
    1106              :      */
    1107              :     realT C6( realT m,               ///< [in] is the spatial frequency index in u
    1108              :               realT n,               ///< [in] is the spatial frequency index in v
    1109              :               bool normStrehl = true ///< flag controls whether the contrast is normalized by Strehl ratio.
    1110              :     );
    1111              : 
    1112              :     /// Calculate a 2D map of contrast C6
    1113              :     /** The contrast is Strehl-normalized here.
    1114              :      *
    1115              :      * \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
    1116              :      *
    1117              :      * \tparam imageT is an Eigen-like image type.
    1118              :      */
    1119              :     template <typename imageT>
    1120              :     void C6Map( imageT &map, ///< [in] the map image to be filled in with contrast
    1121              :                 bool normStrehl );
    1122              : 
    1123              :     /// Calculate the residual variance due to dispersive anisoplanatism.
    1124              :     /** Used to calculate contrast \ref C7().
    1125              :      *
    1126              :      * \returns variance at (m,n).
    1127              :      */
    1128              :     realT C7var( realT m, ///< [in] is the spatial frequency index in u
    1129              :                  realT n  ///< [in] is the spatial frequency index in v
    1130              :     );
    1131              : 
    1132              :     /// Calculate the contrast due to dispersive anisoplanatism.
    1133              :     /** Contrast C7 is due to atmospheric dispersion causing light at different wavelengths to take different paths
    1134              :      * through atmospheric turbulence. See Fitzgerald (2017, in prep).
    1135              :      *
    1136              :      * \returns C7.
    1137              :      */
    1138              :     realT C7( realT m,               ///< [in] is the spatial frequency index in u
    1139              :               realT n,               ///< [in] is the spatial frequency index in v
    1140              :               bool normStrehl = true ///< flag controls whether the contrast is normalized by Strehl ratio.
    1141              :     );
    1142              : 
    1143              :     /// Calculate a 2D map of contrast C7
    1144              :     /** The contrast is Strehl-normalized here.
    1145              :      *
    1146              :      *  \note this is the raw PSD, it must be convolved with the PSF for the true contrast.
    1147              :      *
    1148              :      * \tparam imageT is an Eigen-like image type.
    1149              :      */
    1150              :     template <typename imageT>
    1151              :     void C7Map( imageT &map, ///< [in] the map image to be filled in with contrast
    1152              :                 bool normStrehl );
    1153              : 
    1154              :     ///@}
    1155              : 
    1156              :     /// Output current parameters to a stream
    1157              :     /** Outputs a formatted list of all current parameters.
    1158              :      *
    1159              :      */
    1160              :     iosT &dumpAOSystem( iosT &ios /**< [in] a std::ostream-like stream. */ );
    1161              : 
    1162              :     /// Setup the configurator to configure this class.
    1163              :     void setupConfig( app::appConfigurator &config /**< [in] the app::configurator object*/ );
    1164              : 
    1165              :     /// Load the configuration of this class from a configurator.
    1166              :     /**
    1167              :      * \returns `error_t::noerror` when the configured atmosphere is valid, or its typed validation error.
    1168              :      */
    1169              :     error_t loadConfig( app::appConfigurator &config /**< [in] the app::configurator object*/ );
    1170              : };
    1171              : 
    1172              : template <typename realT, class inputSpectT, typename iosT>
    1173          528 : aoSystem<realT, inputSpectT, iosT>::aoSystem()
    1174              : {
    1175           48 :     wfsBeta<wfs<realT, iosT>>( nullptr ); // allocate a default ideal WFS.
    1176           48 : }
    1177              : 
    1178              : template <typename realT, class inputSpectT, typename iosT>
    1179           48 : aoSystem<realT, inputSpectT, iosT>::~aoSystem()
    1180              : {
    1181           48 :     if( m_wfsBeta && m_ownWfsBeta )
    1182              :     {
    1183           48 :         delete m_wfsBeta;
    1184              :     }
    1185           48 : }
    1186              : 
    1187              : template <typename realT, class inputSpectT, typename iosT>
    1188            0 : void aoSystem<realT, inputSpectT, iosT>::loadGuyon2005()
    1189              : {
    1190            0 :     atm.loadGuyon2005();
    1191              : 
    1192            0 :     F0( 1.75e9 * 0.25 * math::pi<realT>() * 64. * 0.18 ); // Converting to photons/sec
    1193            0 :     lam_wfs( 0.55e-6 );
    1194            0 :     lam_sci( 1.6e-6 );
    1195            0 :     D( 8. );
    1196            0 :     starMag( 5 );
    1197              : 
    1198              :     // The rest of Guyon 05 is very ideal
    1199            0 :     npix_wfs( std::vector<realT>( { 12868 } ) );
    1200            0 :     ron_wfs( std::vector<realT>( { 0.0 } ) );
    1201            0 :     Fbg( std::vector<realT>( { 0.0 } ) );
    1202              : 
    1203            0 :     d_min( 8.0 / 1e3 );               // Allow super fine sampling
    1204            0 :     minTauWFS( (realT)( 1. / 1e9 ) ); // Just be super fast.
    1205            0 :     tauWFS( 1. / 1e9 );               // Just be super fast
    1206              : 
    1207            0 :     m_specsChanged = true;
    1208            0 :     m_dminChanged = true;
    1209            0 : }
    1210              : 
    1211              : template <typename realT, class inputSpectT, typename iosT>
    1212            0 : void aoSystem<realT, inputSpectT, iosT>::loadMagAOX()
    1213              : {
    1214            0 :     atm.loadLCO();
    1215            0 :     F0( 4.2e10 );
    1216            0 :     lam_wfs( 0.791e-6 );
    1217            0 :     lam_sci( 0.656e-6 );
    1218              : 
    1219            0 :     npix_wfs( std::vector<realT>( { 9024 } ) );
    1220            0 :     ron_wfs( std::vector<realT>( { 0.57 } ) );
    1221            0 :     Fbg( std::vector<realT>( { 0.22 } ) );
    1222              : 
    1223            0 :     d_min( std::vector<realT>( { 6.5 / 48.0 } ) );
    1224            0 :     minTauWFS( std::vector<realT>( { 1. / 3622. } ) );
    1225            0 :     tauWFS( 1. / 3622. );
    1226              : 
    1227            0 :     D( 6.5 );
    1228              : 
    1229            0 :     m_specsChanged = true;
    1230            0 :     m_dminChanged = true;
    1231            0 : }
    1232              : 
    1233              : template <typename realT, class inputSpectT, typename iosT>
    1234            0 : void aoSystem<realT, inputSpectT, iosT>::loadGMagAOX()
    1235              : {
    1236              : 
    1237            0 :     loadMagAOX();
    1238              : 
    1239            0 :     F0( 7.6e10 * 368.0 / ( 0.25 * math::pi<realT>() * 6.5 * 6.5 * ( 1. - 0.29 * 0.29 ) ) ); // Scaled up.
    1240              : 
    1241            0 :     D( 25.4 );
    1242              : 
    1243            0 :     m_specsChanged = true;
    1244            0 :     m_dminChanged = true;
    1245            0 : }
    1246              : 
    1247              : template <typename realT, class inputSpectT, typename iosT>
    1248           39 : void aoSystem<realT, inputSpectT, iosT>::D( realT nD )
    1249              : {
    1250           39 :     m_D = nD;
    1251           39 :     psd.D( m_D );
    1252              : 
    1253           39 :     m_specsChanged = true;
    1254           39 :     m_dminChanged = true;
    1255           39 : }
    1256              : 
    1257              : template <typename realT, class inputSpectT, typename iosT>
    1258       137485 : realT aoSystem<realT, inputSpectT, iosT>::D()
    1259              : {
    1260       137485 :     return m_D;
    1261              : }
    1262              : 
    1263              : template <typename realT, class inputSpectT, typename iosT>
    1264            1 : void aoSystem<realT, inputSpectT, iosT>::d_min( const std::vector<realT> &nd )
    1265              : {
    1266            1 :     m_d_min.assign( nd.begin(), nd.end() );
    1267            1 :     m_specsChanged = true;
    1268            1 :     m_dminChanged = true;
    1269            1 : }
    1270              : 
    1271              : template <typename realT, class inputSpectT, typename iosT>
    1272            0 : void aoSystem<realT, inputSpectT, iosT>::d_min( int idx, realT nd )
    1273              : {
    1274            0 :     if( m_d_min.size() < idx + 1 )
    1275              :     {
    1276            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_d_min" ) );
    1277              :     }
    1278              : 
    1279            0 :     m_d_min[idx] = nd;
    1280              : 
    1281            0 :     m_specsChanged = true;
    1282            0 :     m_dminChanged = true;
    1283            0 : }
    1284              : 
    1285              : template <typename realT, class inputSpectT, typename iosT>
    1286            3 : realT aoSystem<realT, inputSpectT, iosT>::d_min( size_t idx )
    1287              : {
    1288            3 :     if( m_d_min.size() < idx + 1 )
    1289              :     {
    1290            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_d_min" ) );
    1291              :     }
    1292              : 
    1293            3 :     return m_d_min[idx];
    1294              : }
    1295              : 
    1296              : template <typename realT, class inputSpectT, typename iosT>
    1297           10 : std::vector<realT> aoSystem<realT, inputSpectT, iosT>::d_min()
    1298              : {
    1299           10 :     return m_d_min;
    1300              : }
    1301              : 
    1302              : template <typename realT, class inputSpectT, typename iosT>
    1303            1 : void aoSystem<realT, inputSpectT, iosT>::optd( bool od )
    1304              : {
    1305            1 :     m_optd = od;
    1306            1 :     m_specsChanged = true;
    1307            1 :     m_dminChanged = true;
    1308            1 : }
    1309              : 
    1310              : template <typename realT, class inputSpectT, typename iosT>
    1311           12 : bool aoSystem<realT, inputSpectT, iosT>::optd()
    1312              : {
    1313           12 :     return m_optd;
    1314              : }
    1315              : 
    1316              : template <typename realT, class inputSpectT, typename iosT>
    1317            1 : void aoSystem<realT, inputSpectT, iosT>::optd_delta( realT odd )
    1318              : {
    1319            1 :     m_optd_delta = odd;
    1320            1 :     m_specsChanged = true;
    1321            1 :     m_dminChanged = true;
    1322            1 : }
    1323              : 
    1324              : template <typename realT, class inputSpectT, typename iosT>
    1325           13 : realT aoSystem<realT, inputSpectT, iosT>::optd_delta()
    1326              : {
    1327           13 :     return m_optd_delta;
    1328              : }
    1329              : 
    1330              : template <typename realT, class inputSpectT, typename iosT>
    1331              : template <typename wfsT>
    1332              : void aoSystem<realT, inputSpectT, iosT>::wfsBeta( const wfsT &w )
    1333              : {
    1334              :     if( m_wfsBeta && m_ownWfsBeta )
    1335              :     {
    1336              :         delete m_wfsBeta;
    1337              :         m_wfsBeta = nullptr;
    1338              :     }
    1339              : 
    1340              :     m_wfsBeta = (wfs<realT, iosT> *)&w;
    1341              :     m_ownWfsBeta = false;
    1342              : }
    1343              : 
    1344              : template <typename realT, class inputSpectT, typename iosT>
    1345              : template <typename wfsT>
    1346           52 : void aoSystem<realT, inputSpectT, iosT>::wfsBeta( const wfsT *w )
    1347              : {
    1348           52 :     if( m_wfsBeta && m_ownWfsBeta )
    1349              :     {
    1350            4 :         delete m_wfsBeta;
    1351            4 :         m_wfsBeta = nullptr;
    1352              :     }
    1353              : 
    1354           52 :     if( w )
    1355              :     {
    1356            0 :         m_wfsBeta = (wfs<realT, iosT> *)w;
    1357            0 :         m_ownWfsBeta = false;
    1358              :     }
    1359              :     else
    1360              :     {
    1361           52 :         m_wfsBeta = new wfsT;
    1362           52 :         m_ownWfsBeta = true;
    1363              :     }
    1364           52 : }
    1365              : 
    1366              : template <typename realT, class inputSpectT, typename iosT>
    1367            5 : wfs<realT, iosT> *aoSystem<realT, inputSpectT, iosT>::wfsBeta()
    1368              : {
    1369            5 :     return m_wfsBeta;
    1370              : }
    1371              : 
    1372              : template <typename realT, class inputSpectT, typename iosT>
    1373            0 : realT aoSystem<realT, inputSpectT, iosT>::beta_p( realT m, realT n )
    1374              : {
    1375            0 :     if( m_wfsBeta == 0 )
    1376              :     {
    1377            0 :         throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
    1378              :     }
    1379              : 
    1380            0 :     return m_wfsBeta->beta_p( m, n, m_D, d_opt(), atm.r_0( m_lam_sci ) );
    1381              : }
    1382              : 
    1383              : template <typename realT, class inputSpectT, typename iosT>
    1384            0 : realT aoSystem<realT, inputSpectT, iosT>::beta_r( realT m, realT n )
    1385              : {
    1386            0 :     if( m_wfsBeta == 0 )
    1387              :     {
    1388            0 :         throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
    1389              :     }
    1390            0 :     return m_wfsBeta->beta_r( m, n, m_D, d_opt(), atm.r_0( m_lam_sci ) );
    1391              : }
    1392              : 
    1393              : template <typename realT, class inputSpectT, typename iosT>
    1394            0 : void aoSystem<realT, inputSpectT, iosT>::opticalGain( realT og )
    1395              : {
    1396            0 :     m_opticalGain = og;
    1397            0 :     m_specsChanged = true;
    1398            0 :     m_dminChanged = true;
    1399            0 : }
    1400              : 
    1401              : template <typename realT, class inputSpectT, typename iosT>
    1402            0 : realT aoSystem<realT, inputSpectT, iosT>::opticalGain()
    1403              : {
    1404            0 :     return m_opticalGain;
    1405              : }
    1406              : 
    1407              : template <typename realT, class inputSpectT, typename iosT>
    1408           16 : void aoSystem<realT, inputSpectT, iosT>::lam_wfs( realT nlam )
    1409              : {
    1410           16 :     m_lam_wfs = nlam;
    1411           16 :     m_specsChanged = true;
    1412           16 :     m_dminChanged = true;
    1413           16 : }
    1414              : 
    1415              : template <typename realT, class inputSpectT, typename iosT>
    1416           13 : realT aoSystem<realT, inputSpectT, iosT>::lam_wfs()
    1417              : {
    1418           13 :     return m_lam_wfs;
    1419              : }
    1420              : 
    1421              : template <typename realT, class inputSpectT, typename iosT>
    1422            1 : void aoSystem<realT, inputSpectT, iosT>::npix_wfs( const std::vector<realT> &npix )
    1423              : {
    1424            1 :     m_npix_wfs.resize( npix.size() );
    1425            3 :     for( size_t n = 0; n < npix.size(); ++n )
    1426              :     {
    1427            2 :         m_npix_wfs[n] = npix[n];
    1428              :     }
    1429              : 
    1430            1 :     m_specsChanged = true;
    1431            1 :     m_dminChanged = true;
    1432            1 : }
    1433              : 
    1434              : template <typename realT, class inputSpectT, typename iosT>
    1435            0 : void aoSystem<realT, inputSpectT, iosT>::npix_wfs( int idx, realT npix )
    1436              : {
    1437            0 :     if( m_npix_wfs.size() < idx + 1 )
    1438              :     {
    1439            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_npix_wfs" ) );
    1440              :     }
    1441              : 
    1442            0 :     m_npix_wfs[idx] = npix;
    1443              : 
    1444            0 :     m_specsChanged = true;
    1445            0 :     m_dminChanged = true;
    1446            0 : }
    1447              : 
    1448              : template <typename realT, class inputSpectT, typename iosT>
    1449            4 : realT aoSystem<realT, inputSpectT, iosT>::npix_wfs( size_t idx )
    1450              : {
    1451            4 :     if( m_npix_wfs.size() < idx + 1 )
    1452              :     {
    1453            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_npix_wfs" ) );
    1454              :     }
    1455              : 
    1456            4 :     return m_npix_wfs[idx];
    1457              : }
    1458              : 
    1459              : template <typename realT, class inputSpectT, typename iosT>
    1460           13 : std::vector<realT> aoSystem<realT, inputSpectT, iosT>::npix_wfs()
    1461              : {
    1462           13 :     return m_npix_wfs;
    1463              : }
    1464              : 
    1465              : template <typename realT, class inputSpectT, typename iosT>
    1466            1 : void aoSystem<realT, inputSpectT, iosT>::ron_wfs( const std::vector<realT> &nron )
    1467              : {
    1468            1 :     m_ron_wfs.resize( nron.size() );
    1469            3 :     for( size_t n = 0; n < nron.size(); ++n )
    1470              :     {
    1471            2 :         m_ron_wfs[n] = nron[n];
    1472              :     }
    1473              : 
    1474            1 :     m_specsChanged = true;
    1475            1 :     m_dminChanged = true;
    1476            1 : }
    1477              : 
    1478              : template <typename realT, class inputSpectT, typename iosT>
    1479            0 : void aoSystem<realT, inputSpectT, iosT>::ron_wfs( int idx, realT nron )
    1480              : {
    1481            0 :     if( m_ron_wfs.size() < idx + 1 )
    1482              :     {
    1483            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_ron_wfs" ) );
    1484              :     }
    1485              : 
    1486            0 :     m_ron_wfs[idx] = nron;
    1487              : 
    1488            0 :     m_specsChanged = true;
    1489            0 :     m_dminChanged = true;
    1490            0 : }
    1491              : 
    1492              : template <typename realT, class inputSpectT, typename iosT>
    1493            4 : realT aoSystem<realT, inputSpectT, iosT>::ron_wfs( size_t idx )
    1494              : {
    1495            4 :     if( m_ron_wfs.size() < idx + 1 )
    1496              :     {
    1497            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_ron_wfs" ) );
    1498              :     }
    1499              : 
    1500            4 :     return m_ron_wfs[idx];
    1501              : }
    1502              : 
    1503              : template <typename realT, class inputSpectT, typename iosT>
    1504           13 : std::vector<realT> aoSystem<realT, inputSpectT, iosT>::ron_wfs()
    1505              : {
    1506           13 :     return m_ron_wfs;
    1507              : }
    1508              : 
    1509              : template <typename realT, class inputSpectT, typename iosT>
    1510            1 : void aoSystem<realT, inputSpectT, iosT>::Fbg( const std::vector<realT> &fbg )
    1511              : {
    1512            1 :     m_Fbg.resize( fbg.size() );
    1513            3 :     for( size_t n = 0; n < fbg.size(); ++n )
    1514              :     {
    1515            2 :         m_Fbg[n] = fbg[n];
    1516              :     }
    1517              : 
    1518            1 :     m_specsChanged = true;
    1519            1 :     m_dminChanged = true;
    1520            1 : }
    1521              : 
    1522              : template <typename realT, class inputSpectT, typename iosT>
    1523            0 : void aoSystem<realT, inputSpectT, iosT>::Fbg( int idx, realT fbg )
    1524              : {
    1525            0 :     if( m_Fbg.size() < idx + 1 )
    1526              :     {
    1527            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_Fbg" ) );
    1528              :     }
    1529              : 
    1530            0 :     m_Fbg[idx] = fbg;
    1531              : 
    1532            0 :     m_specsChanged = true;
    1533            0 :     m_dminChanged = true;
    1534            0 : }
    1535              : 
    1536              : template <typename realT, class inputSpectT, typename iosT>
    1537            4 : realT aoSystem<realT, inputSpectT, iosT>::Fbg( size_t idx )
    1538              : {
    1539            4 :     if( m_Fbg.size() < idx + 1 )
    1540              :     {
    1541            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_Fbg" ) );
    1542              :     }
    1543              : 
    1544            4 :     return m_Fbg[idx];
    1545              : }
    1546              : 
    1547              : template <typename realT, class inputSpectT, typename iosT>
    1548           13 : std::vector<realT> aoSystem<realT, inputSpectT, iosT>::Fbg()
    1549              : {
    1550           13 :     return m_Fbg;
    1551              : }
    1552              : 
    1553              : template <typename realT, class inputSpectT, typename iosT>
    1554            1 : void aoSystem<realT, inputSpectT, iosT>::minTauWFS( const std::vector<realT> &ntau )
    1555              : {
    1556            1 :     m_minTauWFS.resize( ntau.size() );
    1557            3 :     for( size_t n = 0; n < ntau.size(); ++n )
    1558              :     {
    1559            2 :         m_minTauWFS[n] = ntau[n];
    1560              :     }
    1561              : 
    1562            1 :     m_specsChanged = true;
    1563            1 :     m_dminChanged = true;
    1564            1 : }
    1565              : 
    1566              : template <typename realT, class inputSpectT, typename iosT>
    1567            0 : void aoSystem<realT, inputSpectT, iosT>::minTauWFS( size_t idx, realT ntau )
    1568              : {
    1569            0 :     if( m_minTauWFS.size() < idx + 1 )
    1570              :     {
    1571            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_ntau_wfs" ) );
    1572              :     }
    1573              : 
    1574            0 :     m_minTauWFS[idx] = ntau;
    1575              : 
    1576            0 :     m_specsChanged = true;
    1577            0 :     m_dminChanged = true;
    1578            0 : }
    1579              : 
    1580              : template <typename realT, class inputSpectT, typename iosT>
    1581            4 : realT aoSystem<realT, inputSpectT, iosT>::minTauWFS( size_t idx )
    1582              : {
    1583            4 :     if( m_minTauWFS.size() < idx + 1 )
    1584              :     {
    1585            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "idx larger than m_ntau_wfs" ) );
    1586              :     }
    1587              : 
    1588            4 :     return m_minTauWFS[idx];
    1589              : }
    1590              : 
    1591              : template <typename realT, class inputSpectT, typename iosT>
    1592           13 : std::vector<realT> aoSystem<realT, inputSpectT, iosT>::minTauWFS()
    1593              : {
    1594           13 :     return m_minTauWFS;
    1595              : }
    1596              : 
    1597              : template <typename realT, class inputSpectT, typename iosT>
    1598            1 : void aoSystem<realT, inputSpectT, iosT>::bin_npix( bool bnp )
    1599              : {
    1600            1 :     if( bnp != m_bin_npix )
    1601              :     {
    1602            1 :         m_bin_npix = bnp;
    1603            1 :         m_specsChanged = true;
    1604            1 :         m_dminChanged = true;
    1605              :     }
    1606            1 : }
    1607              : 
    1608              : template <typename realT, class inputSpectT, typename iosT>
    1609           12 : bool aoSystem<realT, inputSpectT, iosT>::bin_npix()
    1610              : {
    1611           12 :     return m_bin_npix;
    1612              : }
    1613              : 
    1614              : template <typename realT, class inputSpectT, typename iosT>
    1615            0 : int aoSystem<realT, inputSpectT, iosT>::bin_opt()
    1616              : {
    1617            0 :     d_opt();
    1618            0 :     return m_bin_opt;
    1619              : }
    1620              : 
    1621              : template <typename realT, class inputSpectT, typename iosT>
    1622            1 : void aoSystem<realT, inputSpectT, iosT>::tauWFS( realT ntau )
    1623              : {
    1624            1 :     m_tauWFS = ntau;
    1625            1 :     m_specsChanged = true;
    1626            1 :     m_dminChanged = true;
    1627            1 : }
    1628              : 
    1629              : template <typename realT, class inputSpectT, typename iosT>
    1630           13 : realT aoSystem<realT, inputSpectT, iosT>::tauWFS()
    1631              : {
    1632           13 :     return m_tauWFS;
    1633              : }
    1634              : 
    1635              : template <typename realT, class inputSpectT, typename iosT>
    1636            1 : void aoSystem<realT, inputSpectT, iosT>::deltaTau( realT ndel )
    1637              : {
    1638            1 :     m_deltaTau = ndel;
    1639            1 :     m_specsChanged = true;
    1640            1 :     m_dminChanged = true;
    1641            1 : }
    1642              : 
    1643              : template <typename realT, class inputSpectT, typename iosT>
    1644           13 : realT aoSystem<realT, inputSpectT, iosT>::deltaTau()
    1645              : {
    1646           13 :     return m_deltaTau;
    1647              : }
    1648              : 
    1649              : template <typename realT, class inputSpectT, typename iosT>
    1650            1 : void aoSystem<realT, inputSpectT, iosT>::optTau( bool ot )
    1651              : {
    1652            1 :     m_optTau = ot;
    1653            1 :     m_specsChanged = true;
    1654            1 :     m_dminChanged = true;
    1655            1 : }
    1656              : 
    1657              : template <typename realT, class inputSpectT, typename iosT>
    1658           12 : bool aoSystem<realT, inputSpectT, iosT>::optTau()
    1659              : {
    1660           12 :     return m_optTau;
    1661              : }
    1662              : 
    1663              : template <typename realT, class inputSpectT, typename iosT>
    1664           18 : void aoSystem<realT, inputSpectT, iosT>::lam_sci( realT nlam )
    1665              : {
    1666           18 :     m_lam_sci = nlam;
    1667           18 :     m_specsChanged = true;
    1668           18 :     m_dminChanged = true;
    1669           18 : }
    1670              : 
    1671              : template <typename realT, class inputSpectT, typename iosT>
    1672           20 : realT aoSystem<realT, inputSpectT, iosT>::lam_sci()
    1673              : {
    1674           20 :     return m_lam_sci;
    1675              : }
    1676              : 
    1677              : template <typename realT, class inputSpectT, typename iosT>
    1678            1 : void aoSystem<realT, inputSpectT, iosT>::zeta( realT nz )
    1679              : {
    1680            1 :     m_zeta = nz;
    1681            1 :     m_secZeta = 1 / cos( m_zeta );
    1682              : 
    1683            1 :     m_specsChanged = true;
    1684            1 :     m_dminChanged = true;
    1685            1 : }
    1686              : 
    1687              : template <typename realT, class inputSpectT, typename iosT>
    1688           13 : realT aoSystem<realT, inputSpectT, iosT>::zeta()
    1689              : {
    1690           13 :     return m_zeta;
    1691              : }
    1692              : 
    1693              : template <typename realT, class inputSpectT, typename iosT>
    1694        96452 : realT aoSystem<realT, inputSpectT, iosT>::secZeta()
    1695              : {
    1696        96452 :     return m_secZeta;
    1697              : }
    1698              : 
    1699              : template <typename realT, class inputSpectT, typename iosT>
    1700            1 : void aoSystem<realT, inputSpectT, iosT>::fit_mn_max( int mnm )
    1701              : {
    1702            1 :     if( mnm < 0 )
    1703            0 :         mnm = 0;
    1704            1 :     m_fit_mn_max = mnm;
    1705            1 : }
    1706              : 
    1707              : template <typename realT, class inputSpectT, typename iosT>
    1708           12 : int aoSystem<realT, inputSpectT, iosT>::fit_mn_max()
    1709              : {
    1710           12 :     return m_fit_mn_max;
    1711              : }
    1712              : 
    1713              : template <typename realT, class inputSpectT, typename iosT>
    1714            1 : void aoSystem<realT, inputSpectT, iosT>::circularLimit( bool cl )
    1715              : {
    1716            1 :     m_circularLimit = cl;
    1717            1 :     m_specsChanged = true;
    1718            1 :     m_dminChanged = true;
    1719            1 : }
    1720              : 
    1721              : template <typename realT, class inputSpectT, typename iosT>
    1722           12 : bool aoSystem<realT, inputSpectT, iosT>::circularLimit()
    1723              : {
    1724           12 :     return m_circularLimit;
    1725              : }
    1726              : 
    1727              : template <typename realT, class inputSpectT, typename iosT>
    1728            1 : void aoSystem<realT, inputSpectT, iosT>::spatialFilter_ku( realT kx )
    1729              : {
    1730            1 :     m_spatialFilter_ku = fabs( kx );
    1731            1 :     m_specsChanged = true; // not sure if needed
    1732            1 :     m_dminChanged = true;  // not sure if needed
    1733            1 : }
    1734              : 
    1735              : template <typename realT, class inputSpectT, typename iosT>
    1736           50 : realT aoSystem<realT, inputSpectT, iosT>::spatialFilter_ku()
    1737              : {
    1738           50 :     return m_spatialFilter_ku;
    1739              : }
    1740              : 
    1741              : template <typename realT, class inputSpectT, typename iosT>
    1742            1 : void aoSystem<realT, inputSpectT, iosT>::spatialFilter_kv( realT ky )
    1743              : {
    1744            1 :     m_spatialFilter_kv = fabs( ky );
    1745            1 :     m_specsChanged = true; // not sure if needed
    1746            1 :     m_dminChanged = true;  // not sure if needed
    1747            1 : }
    1748              : 
    1749              : template <typename realT, class inputSpectT, typename iosT>
    1750           50 : realT aoSystem<realT, inputSpectT, iosT>::spatialFilter_kv()
    1751              : {
    1752           50 :     return m_spatialFilter_kv;
    1753              : }
    1754              : 
    1755              : template <typename realT, class inputSpectT, typename iosT>
    1756            1 : void aoSystem<realT, inputSpectT, iosT>::ncp_wfe( realT nwfe )
    1757              : {
    1758            1 :     m_ncp_wfe = nwfe;
    1759            1 :     m_specsChanged = true;
    1760            1 :     m_dminChanged = true;
    1761            1 : }
    1762              : 
    1763              : template <typename realT, class inputSpectT, typename iosT>
    1764           12 : realT aoSystem<realT, inputSpectT, iosT>::ncp_wfe()
    1765              : {
    1766           12 :     return m_ncp_wfe;
    1767              : }
    1768              : 
    1769              : template <typename realT, class inputSpectT, typename iosT>
    1770            1 : void aoSystem<realT, inputSpectT, iosT>::ncp_alpha( realT alpha )
    1771              : {
    1772            1 :     m_ncp_alpha = alpha;
    1773            1 :     m_specsChanged = true;
    1774            1 :     m_dminChanged = true;
    1775            1 : }
    1776              : 
    1777              : template <typename realT, class inputSpectT, typename iosT>
    1778           12 : realT aoSystem<realT, inputSpectT, iosT>::ncp_alpha()
    1779              : {
    1780           12 :     return m_ncp_alpha;
    1781              : }
    1782              : 
    1783              : template <typename realT, class inputSpectT, typename iosT>
    1784            1 : void aoSystem<realT, inputSpectT, iosT>::F0( realT nF0 )
    1785              : {
    1786            1 :     m_F0 = nF0;
    1787            1 :     m_specsChanged = true;
    1788            1 :     m_dminChanged = true;
    1789            1 : }
    1790              : 
    1791              : template <typename realT, class inputSpectT, typename iosT>
    1792           13 : realT aoSystem<realT, inputSpectT, iosT>::F0()
    1793              : {
    1794           13 :     return m_F0;
    1795              : }
    1796              : 
    1797              : template <typename realT, class inputSpectT, typename iosT>
    1798            1 : void aoSystem<realT, inputSpectT, iosT>::starMag( realT nmag )
    1799              : {
    1800            1 :     m_starMag = nmag;
    1801            1 :     m_specsChanged = true;
    1802            1 :     m_dminChanged = true;
    1803            1 : }
    1804              : 
    1805              : template <typename realT, class inputSpectT, typename iosT>
    1806           13 : realT aoSystem<realT, inputSpectT, iosT>::starMag()
    1807              : {
    1808           13 :     return m_starMag;
    1809              : }
    1810              : 
    1811              : template <typename realT, class inputSpectT, typename iosT>
    1812            4 : realT aoSystem<realT, inputSpectT, iosT>::Fg( realT mag )
    1813              : {
    1814            4 :     return m_F0 * pow( 10.0, -0.4 * mag );
    1815              : }
    1816              : 
    1817              : template <typename realT, class inputSpectT, typename iosT>
    1818            2 : realT aoSystem<realT, inputSpectT, iosT>::Fg()
    1819              : {
    1820            2 :     return Fg( m_starMag );
    1821              : }
    1822              : 
    1823              : template <typename realT, class inputSpectT, typename iosT>
    1824            0 : const Eigen::Array<int, -1, -1> &aoSystem<realT, inputSpectT, iosT>::controlledModes()
    1825              : {
    1826            0 :     if( m_specsChanged || m_dminChanged )
    1827            0 :         calcStrehl();
    1828              : 
    1829            0 :     return m_controlledModes;
    1830              : }
    1831              : 
    1832              : template <typename realT, class inputSpectT, typename iosT>
    1833            0 : realT aoSystem<realT, inputSpectT, iosT>::signal2Noise2( realT &Nph, realT tau_wfs, realT d, int b )
    1834              : {
    1835            0 :     Nph = Fg() * tau_wfs;
    1836              : 
    1837            0 :     double binfact = 1.0;
    1838            0 :     int binidx = 0;
    1839            0 :     if( m_bin_npix )
    1840              :     {
    1841            0 :         if( m_npix_wfs.size() == 1 ) // Only if "true binning", otherwise the WFS mode vectors handle it.
    1842              :         {
    1843            0 :             binfact = 1. / pow( (realT)b + 1, 2 );
    1844              :         }
    1845              :         else
    1846            0 :             binidx = b;
    1847              :     }
    1848              : 
    1849            0 :     return pow( Nph, 2 ) / ( m_npix_wfs[binidx] * binfact * ( m_Fbg[binidx] * tau_wfs + pow( m_ron_wfs[binidx], 2 ) ) );
    1850              : }
    1851              : 
    1852              : template <typename realT, class inputSpectT, typename iosT>
    1853            0 : realT aoSystem<realT, inputSpectT, iosT>::measurementError( realT m, realT n, realT d, int b )
    1854              : {
    1855            0 :     if( m == 0 and n == 0 )
    1856            0 :         return 0;
    1857              : 
    1858              :     realT tau_wfs;
    1859              : 
    1860            0 :     if( m_optTau )
    1861            0 :         tau_wfs = optimumTauWFS( m, n, d, b );
    1862              :     else
    1863            0 :         tau_wfs = m_tauWFS;
    1864              : 
    1865            0 :     if( m_wfsBeta == 0 )
    1866              :     {
    1867            0 :         throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
    1868              :     }
    1869              : 
    1870            0 :     realT beta_p = m_wfsBeta->beta_p( m, n, m_D, d, atm.r_0( m_lam_wfs ) ) / sqrt( m_opticalGain );
    1871            0 :     realT beta_r = m_wfsBeta->beta_r( m, n, m_D, d, atm.r_0( m_lam_wfs ) ); // sqrt(m_opticalGain);
    1872            0 :     realT Nph = 0;
    1873            0 :     realT snr2 = signal2Noise2( Nph, tau_wfs, d, b );
    1874              : 
    1875            0 :     return ( pow( beta_r, 2 ) / snr2 + pow( beta_p, 2 ) / Nph ) * pow( m_lam_wfs / m_lam_sci, 2 );
    1876              : }
    1877              : 
    1878              : template <typename realT, class inputSpectT, typename iosT>
    1879            0 : realT aoSystem<realT, inputSpectT, iosT>::measurementErrorTotal()
    1880              : {
    1881            0 :     if( m_specsChanged || m_dminChanged )
    1882            0 :         calcStrehl();
    1883            0 :     return m_wfeMeasurement;
    1884              : }
    1885              : 
    1886              : template <typename realT, class inputSpectT, typename iosT>
    1887            0 : realT aoSystem<realT, inputSpectT, iosT>::timeDelayError( realT m, realT n, realT d, int b )
    1888              : {
    1889            0 :     if( m == 0 and n == 0 )
    1890            0 :         return 0;
    1891              : 
    1892            0 :     realT k = sqrt( m * m + n * n ) / m_D;
    1893              : 
    1894              :     realT tau_wfs;
    1895              : 
    1896            0 :     if( m_optTau )
    1897            0 :         tau_wfs = optimumTauWFS( m, n, d, b );
    1898              :     else
    1899            0 :         tau_wfs = m_tauWFS;
    1900              : 
    1901            0 :     realT tau = tau_wfs + m_deltaTau;
    1902              : 
    1903              :     ///\todo handle multiple layers
    1904            0 :     return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
    1905            0 :            sqrt( atm.X( k, m_lam_sci, m_secZeta ) ) * pow( math::two_pi<realT>() * atm.v_wind() * k, 2 ) *
    1906            0 :            pow( tau, 2 );
    1907              : }
    1908              : 
    1909              : template <typename realT, class inputSpectT, typename iosT>
    1910            0 : realT aoSystem<realT, inputSpectT, iosT>::timeDelayErrorTotal()
    1911              : {
    1912            0 :     if( m_specsChanged || m_dminChanged )
    1913            0 :         calcStrehl();
    1914            0 :     return m_wfeTimeDelay;
    1915              : }
    1916              : 
    1917              : template <typename realT, class inputSpectT, typename iosT>
    1918            0 : realT aoSystem<realT, inputSpectT, iosT>::fittingError( realT m, realT n )
    1919              : {
    1920            0 :     realT k = sqrt( m * m + n * n ) / m_D;
    1921              : 
    1922              :     ///\todo handle multiple layers
    1923            0 :     return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 );
    1924              : }
    1925              : 
    1926              : template <typename realT, class inputSpectT, typename iosT>
    1927            0 : realT aoSystem<realT, inputSpectT, iosT>::fittingErrorTotal()
    1928              : {
    1929            0 :     if( m_specsChanged || m_dminChanged )
    1930            0 :         calcStrehl();
    1931            0 :     return m_wfeFitting;
    1932              : }
    1933              : 
    1934              : template <typename realT, class inputSpectT, typename iosT>
    1935            0 : realT aoSystem<realT, inputSpectT, iosT>::chromScintOPDError( int m, int n )
    1936              : {
    1937            0 :     return C4var( m, n );
    1938              : }
    1939              : 
    1940              : template <typename realT, class inputSpectT, typename iosT>
    1941            0 : realT aoSystem<realT, inputSpectT, iosT>::chromScintOPDErrorTotal()
    1942              : {
    1943            0 :     if( m_specsChanged || m_dminChanged )
    1944            0 :         calcStrehl();
    1945            0 :     return m_wfeChromScintOPD;
    1946              : }
    1947              : 
    1948              : template <typename realT, class inputSpectT, typename iosT>
    1949            0 : realT aoSystem<realT, inputSpectT, iosT>::chromScintAmpError( int m, int n )
    1950              : {
    1951            0 :     return C5var( m, n );
    1952              : 
    1953              : #if 0
    1954              :    int mn_max = floor(0.5*m_D/d_opt());
    1955              : 
    1956              :    realT sum = 0;
    1957              : 
    1958              :    for(int m = -mn_max; m <= mn_max; ++m)
    1959              :    {
    1960              :       for(int n = -mn_max; n <= mn_max; ++n)
    1961              :       {
    1962              :          if(n == 0 && m == 0) continue;
    1963              : 
    1964              :          sum += C5var(m,n);
    1965              :       }
    1966              :    }
    1967              : 
    1968              :    return sum;
    1969              : #endif
    1970              : }
    1971              : 
    1972              : template <typename realT, class inputSpectT, typename iosT>
    1973            0 : realT aoSystem<realT, inputSpectT, iosT>::chromIndexError( int m, int n )
    1974              : {
    1975            0 :     return C6var( m, n );
    1976              : }
    1977              : 
    1978              : template <typename realT, class inputSpectT, typename iosT>
    1979            0 : realT aoSystem<realT, inputSpectT, iosT>::chromIndexErrorTotal()
    1980              : {
    1981            0 :     if( m_specsChanged || m_dminChanged )
    1982            0 :         calcStrehl();
    1983            0 :     return m_wfeChromIndex;
    1984              : }
    1985              : 
    1986              : template <typename realT, class inputSpectT, typename iosT>
    1987            0 : realT aoSystem<realT, inputSpectT, iosT>::dispAnisoOPDError( int m, int n )
    1988              : {
    1989            0 :     return C7var( m, n );
    1990              : }
    1991              : 
    1992              : template <typename realT, class inputSpectT, typename iosT>
    1993            0 : realT aoSystem<realT, inputSpectT, iosT>::dispAnisoOPDErrorTotal()
    1994              : {
    1995            0 :     if( m_specsChanged || m_dminChanged )
    1996            0 :         calcStrehl();
    1997            0 :     return m_wfeAnisoOPD;
    1998              : }
    1999              : 
    2000              : template <typename realT, class inputSpectT, typename iosT>
    2001            0 : realT aoSystem<realT, inputSpectT, iosT>::dispAnisoAmpError()
    2002              : {
    2003            0 :     return 0;
    2004              : 
    2005              : #if 0
    2006              :    int mn_max = floor(0.5*m_D/d_opt());
    2007              : 
    2008              :    realT sum = 0;
    2009              : 
    2010              :    for(int m = -mn_max; m <= mn_max; ++m)
    2011              :    {
    2012              :       for(int n = -mn_max; n <= mn_max; ++n)
    2013              :       {
    2014              :          if(n == 0 && m == 0) continue;
    2015              : 
    2016              :          if( m_circularLimit )
    2017              :          {
    2018              :             if( m*m + n*n > mn_max*mn_max ) continue;
    2019              :          }
    2020              : 
    2021              :          sum += C8var(m,n);
    2022              :       }
    2023              :    }
    2024              : 
    2025              :    return sum;
    2026              : #endif
    2027              : }
    2028              : 
    2029              : template <typename realT, class inputSpectT, typename iosT>
    2030            0 : realT aoSystem<realT, inputSpectT, iosT>::optimumTauWFS(
    2031              :     realT m,
    2032              :     realT n,
    2033              :     realT dact, // here called dact due to parameter collision with root-finding
    2034              :     int bbin )
    2035              : {
    2036            0 :     if( m_D == 0 )
    2037              :     {
    2038            0 :         internal::mxlib_error_report( error_t::paramnotset, "Diameter (D) not set." );
    2039            0 :         return -1;
    2040              :     }
    2041              : 
    2042            0 :     if( m_F0 == 0 )
    2043              :     {
    2044            0 :         internal::mxlib_error_report( error_t::paramnotset, "0-mag photon flux (F0) not set." );
    2045            0 :         return -1;
    2046              :     }
    2047              : 
    2048            0 :     double binfact = 1.0;
    2049            0 :     int binidx = 0; // index into WFS configurations
    2050            0 :     if( m_bin_npix )
    2051              :     {
    2052            0 :         if( m_npix_wfs.size() > 1 )
    2053              :         {
    2054            0 :             binidx = bbin;
    2055              :         }
    2056              :         else
    2057              :         {
    2058            0 :             binfact = 1.0 / pow( (realT)( bbin + 1 ), 2 );
    2059              :         }
    2060              :     }
    2061              : 
    2062            0 :     realT k = sqrt( m * m + n * n ) / m_D;
    2063              : 
    2064            0 :     realT F = Fg();
    2065              : 
    2066            0 :     if( m_wfsBeta == 0 )
    2067              :     {
    2068            0 :         throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
    2069              :     }
    2070              : 
    2071            0 :     realT beta_p = m_wfsBeta->beta_p( m, n, m_D, dact, atm.r_0( m_lam_wfs ) ) / sqrt( m_opticalGain );
    2072            0 :     realT beta_r = m_wfsBeta->beta_r( m, n, m_D, dact, atm.r_0( m_lam_wfs ) ) / sqrt( m_opticalGain );
    2073              : 
    2074              :     // Set up for root finding:
    2075              :     realT a, b, c, d, e;
    2076              : 
    2077              :     ///\todo handle multiple layers
    2078            0 :     realT Atmp = 2 * pow( atm.lam_0(), 2 ) * psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) *
    2079            0 :                  ( atm.X( k, m_lam_wfs, m_secZeta ) ) * pow( math::two_pi<realT>() * atm.v_wind() * k, 2 );
    2080              : 
    2081            0 :     a = Atmp;
    2082            0 :     b = Atmp * m_deltaTau;
    2083            0 :     c = 0;
    2084            0 :     d = -1 * ( pow( m_lam_wfs, 2 ) / F ) *
    2085            0 :         ( ( m_npix_wfs[binidx] * binfact * m_Fbg[binidx] / F ) * pow( beta_r, 2 ) + pow( beta_p, 2 ) );
    2086            0 :     e = -2 * pow( m_lam_wfs, 2 ) * ( m_npix_wfs[binidx] * binfact ) * pow( m_ron_wfs[binidx], 2 ) * pow( beta_r, 2 ) /
    2087            0 :         pow( F, 2 );
    2088              : 
    2089            0 :     std::vector<std::complex<realT>> x;
    2090              : 
    2091              :     // Get the roots
    2092            0 :     math::quarticRoots( x, a, b, c, d, e );
    2093              : 
    2094              :     // Now pick the largest positive real root
    2095            0 :     realT tauopt = 0.0;
    2096              : 
    2097            0 :     for( int i = 0; i < 4; i++ )
    2098              :     {
    2099            0 :         if( real( x[i] ) > 0 && imag( x[i] ) == 0 && real( x[i] ) > tauopt )
    2100            0 :             tauopt = real( x[i] );
    2101              :     }
    2102              : 
    2103            0 :     if( tauopt < m_minTauWFS[binidx] )
    2104            0 :         tauopt = m_minTauWFS[binidx];
    2105              : 
    2106            0 :     return tauopt;
    2107            0 : }
    2108              : 
    2109              : template <typename realT, class inputSpectT, typename iosT>
    2110            0 : realT aoSystem<realT, inputSpectT, iosT>::optimumTauWFS( realT m, realT n )
    2111              : {
    2112            0 :     d_opt(); // also gets m_bin_opt;
    2113            0 :     return optimumTauWFS( m, n, m_d_opt, m_bin_opt );
    2114              : }
    2115              : 
    2116              : template <typename realT, class inputSpectT, typename iosT>
    2117            1 : realT aoSystem<realT, inputSpectT, iosT>::d_opt()
    2118              : {
    2119            1 :     if( !m_dminChanged )
    2120            0 :         return m_d_opt;
    2121              : 
    2122              :     // Validate the modes
    2123              :     ///\todo this should be in a separate valideModes function
    2124            1 :     if( m_npix_wfs.size() < 1 )
    2125              :     {
    2126            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "npix_wfs must have at least one entry" ) );
    2127              :     }
    2128              : 
    2129            1 :     if( m_d_min.size() != m_npix_wfs.size() )
    2130              :     {
    2131            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "d_min must be the same size as npix_wfs" ) );
    2132              :     }
    2133              : 
    2134            1 :     if( m_ron_wfs.size() != m_npix_wfs.size() )
    2135              :     {
    2136            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "ron_wfs must be the same size as npix_wfs" ) );
    2137              :     }
    2138              : 
    2139            1 :     if( m_Fbg.size() != m_npix_wfs.size() )
    2140              :     {
    2141            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "F_bg must be the same size as npix_wfs" ) );
    2142              :     }
    2143              : 
    2144            1 :     if( m_minTauWFS.size() != m_npix_wfs.size() )
    2145              :     {
    2146            0 :         throw( mx::exception<verboseT>( error_t::sizeerr, "minTauWFS must be the same size as npix_wfs" ) );
    2147              :     }
    2148              : 
    2149              :     ///\todo investigate why we tolerate m_d_min = 0 here.  Is this just a config error?
    2150            1 :     if( m_d_min.size() == 1 && m_d_min[0] == 0 )
    2151              :     {
    2152            1 :         m_d_opt = 1e-50;
    2153            1 :         m_bin_opt = 0;
    2154            1 :         m_dminChanged = false;
    2155            1 :         return m_d_opt;
    2156              :     }
    2157              : 
    2158            0 :     if( !m_optd )
    2159              :     {
    2160            0 :         m_d_opt = m_d_min[0];
    2161            0 :         m_bin_opt = 0;
    2162            0 :         m_dminChanged = false;
    2163              : 
    2164            0 :         return m_d_opt;
    2165              :     }
    2166              : 
    2167            0 :     realT best_d = 0;
    2168              : 
    2169            0 :     if( m_bin_npix )
    2170              :     {
    2171            0 :         if( m_npix_wfs.size() > 1 ) // Optimize over the WFS modes
    2172              :         {
    2173            0 :             int best_idx = 0;
    2174            0 :             best_d = m_d_min[0];
    2175              : 
    2176            0 :             realT bestStrehlOverall = 0;
    2177              : 
    2178            0 :             for( int b = 0; b < m_npix_wfs.size(); ++b )
    2179              :             {
    2180            0 :                 realT d = m_d_min[b];
    2181            0 :                 realT s = strehl( d, b );
    2182            0 :                 realT bestStrehl = s;
    2183              : 
    2184              :                 // Find the actuator pitch at which AO error is less than fitting error at Nyquist
    2185            0 :                 while( d < m_D / 2 )
    2186              :                 {
    2187            0 :                     d += m_d_min[b] / m_optd_delta;
    2188            0 :                     s = strehl( d, b );
    2189              : 
    2190            0 :                     if( s < bestStrehl ) // Strehl got worse.  Can't be <= otherwise small m_optd_deltas will
    2191              :                                          // immediately out
    2192              :                     {
    2193            0 :                         d -= m_d_min[b] / m_optd_delta; // go back to previous d
    2194            0 :                         break;
    2195              :                     }
    2196              :                     else
    2197            0 :                         bestStrehl = s;
    2198              :                 }
    2199              : 
    2200              :                 // Check if this is optimum so far
    2201            0 :                 if( bestStrehl >= bestStrehlOverall ) // >= to favor fewer actuators
    2202              :                 {
    2203            0 :                     bestStrehlOverall = bestStrehl;
    2204            0 :                     best_idx = b;
    2205            0 :                     best_d = d;
    2206              :                 }
    2207              :             }
    2208              : 
    2209            0 :             m_bin_opt = best_idx;
    2210              :         }
    2211              :         else
    2212              :         {
    2213            0 :             realT d = m_d_min[0];
    2214            0 :             m_bin_opt = 0;
    2215              : 
    2216            0 :             realT s = strehl( d, m_bin_opt );
    2217            0 :             realT bestStrehl = s;
    2218              : 
    2219            0 :             while( d < m_D / 2 )
    2220              :             {
    2221            0 :                 d += m_d_min[0] / m_optd_delta;
    2222            0 :                 m_bin_opt = d / m_d_min[0] - 1;
    2223            0 :                 if( m_bin_opt < 0 )
    2224            0 :                     m_bin_opt = 0;
    2225              : 
    2226            0 :                 s = strehl( d, m_bin_opt );
    2227              : 
    2228            0 :                 if( s < bestStrehl ) // Strehl got worse.  Can't be <= otherwise small m_optd_deltas will immediately
    2229              :                                      // out
    2230              :                 {
    2231            0 :                     d -= m_d_min[0] / m_optd_delta; // go back to previous d
    2232            0 :                     m_bin_opt = d / m_d_min[0] - 1;
    2233            0 :                     if( m_bin_opt < 0 )
    2234            0 :                         m_bin_opt = 0;
    2235            0 :                     break;
    2236              :                 }
    2237              :                 else
    2238            0 :                     bestStrehl = s;
    2239              :             }
    2240            0 :             best_d = d;
    2241              :         }
    2242              :     }
    2243              :     else
    2244              :     {
    2245            0 :         realT d = m_d_min[0];
    2246            0 :         m_bin_opt = 0;
    2247              : 
    2248            0 :         realT s = strehl( d, m_bin_opt );
    2249            0 :         realT bestStrehl = s;
    2250              : 
    2251              :         // Find the actuator pitch which minimizes total error (AO error is less than fitting error at Nyquist)
    2252            0 :         while( d < m_D / 2 )
    2253              :         {
    2254            0 :             d += m_d_min[0] / m_optd_delta;
    2255              : 
    2256            0 :             s = strehl( d, m_bin_opt );
    2257            0 :             if( s < bestStrehl ) // Strehl got worse.  Can't be <= otherwise small m_optd_deltas will immediately out
    2258              :             {
    2259            0 :                 d -= m_d_min[0] / m_optd_delta; // go back to previous d
    2260            0 :                 break;
    2261              :             }
    2262              :             else
    2263            0 :                 bestStrehl = s;
    2264              :         }
    2265            0 :         best_d = d;
    2266              :     }
    2267              : 
    2268            0 :     m_d_opt = best_d;
    2269            0 :     m_dminChanged = false;
    2270              : 
    2271            0 :     return m_d_opt;
    2272              : }
    2273              : 
    2274              : template <typename realT, class inputSpectT, typename iosT>
    2275            0 : realT aoSystem<realT, inputSpectT, iosT>::ncpError( int m, int n )
    2276              : {
    2277            0 :     if( m == 0 and n == 0 )
    2278            0 :         return 0;
    2279              : 
    2280            0 :     realT k = sqrt( m * m + n * n ) / m_D;
    2281              : 
    2282            0 :     realT kmax = m_fit_mn_max / m_D;
    2283            0 :     realT kmin = 1. / m_D;
    2284              : 
    2285              :     realT beta;
    2286            0 :     if( m_ncp_alpha != 2 )
    2287              :     {
    2288            0 :         beta = ( m_ncp_alpha - 2 ) / ( math::two_pi<realT>() ) * 1. /
    2289            0 :                ( pow( kmin, -m_ncp_alpha + 2 ) - pow( kmax, -m_ncp_alpha + 2 ) );
    2290              :     }
    2291              :     else
    2292              :     {
    2293            0 :         beta = 1. / ( math::two_pi<realT>() * log( kmax / kmin ) );
    2294              :     }
    2295            0 :     return beta * ncpError() * pow( k, -m_ncp_alpha ) * pow( kmin, 2 );
    2296              : }
    2297              : 
    2298              : template <typename realT, class inputSpectT, typename iosT>
    2299            0 : realT aoSystem<realT, inputSpectT, iosT>::ncpError()
    2300              : {
    2301            0 :     return pow( m_ncp_wfe, 2 ) * pow( math::two_pi<realT>() / m_lam_sci, 2 );
    2302              : }
    2303              : 
    2304              : template <typename realT, class inputSpectT, typename iosT>
    2305            0 : realT aoSystem<realT, inputSpectT, iosT>::strehl( realT d, int b )
    2306              : {
    2307              : 
    2308            0 :     int mn_max = floor( 0.5 * m_D / d );
    2309              : 
    2310            0 :     realT wfeVar = 0;
    2311              : 
    2312              :     bool controlled;
    2313            0 :     for( int m = -m_fit_mn_max; m <= m_fit_mn_max; ++m )
    2314              :     {
    2315            0 :         for( int n = -m_fit_mn_max; n <= m_fit_mn_max; ++n )
    2316              :         {
    2317            0 :             controlled = false;
    2318            0 :             if( m_circularLimit )
    2319              :             {
    2320            0 :                 if( m * m + n * n <= mn_max * mn_max )
    2321            0 :                     controlled = true;
    2322              :             }
    2323              :             else
    2324              :             {
    2325            0 :                 if( abs( m ) <= mn_max && abs( n ) <= mn_max )
    2326            0 :                     controlled = true;
    2327              :             }
    2328              : 
    2329            0 :             if( controlled )
    2330              :             {
    2331            0 :                 realT wfeMeasurement = measurementError( m, n, d, b );
    2332            0 :                 realT wfeTimeDelay = timeDelayError( m, n, d, b );
    2333            0 :                 realT wfeChromScintOPD = chromScintOPDError( m, n );
    2334            0 :                 realT wfeChromIndex = chromIndexError( m, n );
    2335            0 :                 realT wfeAnisoOPD = dispAnisoOPDError( m, n );
    2336              : 
    2337            0 :                 realT wfeFitting = fittingError( m, n );
    2338              : 
    2339            0 :                 if( wfeFitting < wfeMeasurement + wfeTimeDelay + wfeChromScintOPD + wfeChromIndex + wfeAnisoOPD )
    2340              :                 {
    2341            0 :                     wfeVar += wfeFitting;
    2342              :                 }
    2343              :                 else
    2344              :                 {
    2345            0 :                     wfeVar += wfeMeasurement + wfeTimeDelay + wfeChromScintOPD + wfeChromIndex + wfeAnisoOPD;
    2346              :                 }
    2347              :             }
    2348              :             else
    2349              :             {
    2350            0 :                 wfeVar += fittingError( m, n );
    2351              :             }
    2352              :         }
    2353              :     }
    2354              : 
    2355            0 :     wfeVar += ncpError();
    2356            0 :     return exp( -1 * wfeVar );
    2357              : }
    2358              : 
    2359              : template <typename realT, class inputSpectT, typename iosT>
    2360            0 : void aoSystem<realT, inputSpectT, iosT>::calcStrehl()
    2361              : {
    2362            0 :     realT d = d_opt(); // have to run this to get m_bin_opt set.
    2363            0 :     int b = m_bin_opt;
    2364              : 
    2365            0 :     int mn_max = floor( 0.5 * m_D / d );
    2366              : 
    2367            0 :     m_wfeMeasurement = 0;
    2368            0 :     m_wfeTimeDelay = 0;
    2369            0 :     m_wfeChromScintOPD = 0;
    2370            0 :     m_wfeChromIndex = 0;
    2371            0 :     m_wfeAnisoOPD = 0;
    2372              : 
    2373            0 :     m_wfeFitting = 0;
    2374              : 
    2375            0 :     m_wfeNCP = 0;
    2376              : 
    2377            0 :     m_controlledModes.resize( 2 * m_fit_mn_max + 1, 2 * m_fit_mn_max + 1 );
    2378            0 :     m_controlledModes.setZero();
    2379              : 
    2380              :     bool controlled;
    2381            0 :     for( int m = -m_fit_mn_max; m <= m_fit_mn_max; ++m )
    2382              :     {
    2383            0 :         for( int n = -m_fit_mn_max; n <= m_fit_mn_max; ++n )
    2384              :         {
    2385            0 :             if( m == 0 && n == 0 )
    2386              :             {
    2387            0 :                 continue;
    2388              :             }
    2389              : 
    2390            0 :             controlled = false;
    2391            0 :             if( m_circularLimit )
    2392              :             {
    2393            0 :                 if( m * m + n * n <= mn_max * mn_max )
    2394            0 :                     controlled = true;
    2395              :             }
    2396              :             else
    2397              :             {
    2398            0 :                 if( abs( m ) <= mn_max && abs( n ) <= mn_max )
    2399            0 :                     controlled = true;
    2400              :             }
    2401              : 
    2402            0 :             if( controlled )
    2403              :             {
    2404            0 :                 realT wfeMeasurement = measurementError( m, n, d, b );
    2405            0 :                 realT wfeTimeDelay = timeDelayError( m, n, d, b );
    2406            0 :                 realT wfeChromScintOPD = chromScintOPDError( m, n );
    2407            0 :                 realT wfeChromIndex = chromIndexError( m, n );
    2408            0 :                 realT wfeAnisoOPD = dispAnisoOPDError( m, n );
    2409              : 
    2410            0 :                 realT wfeFitting = fittingError( m, n );
    2411              : 
    2412            0 :                 if( wfeFitting < wfeMeasurement + wfeTimeDelay + wfeChromScintOPD + wfeChromIndex + wfeAnisoOPD )
    2413              :                 {
    2414            0 :                     m_wfeFitting += wfeFitting;
    2415              :                 }
    2416              :                 else // it's worth controlling this mode.
    2417              :                 {
    2418            0 :                     m_wfeMeasurement += wfeMeasurement;
    2419            0 :                     m_wfeTimeDelay += wfeTimeDelay;
    2420            0 :                     m_wfeChromScintOPD += wfeChromScintOPD;
    2421            0 :                     m_wfeChromIndex += wfeChromIndex;
    2422            0 :                     m_wfeAnisoOPD += wfeAnisoOPD;
    2423            0 :                     m_controlledModes( m_fit_mn_max + m, m_fit_mn_max + n ) = 1;
    2424              :                 }
    2425              :             }
    2426              :             else
    2427              :             {
    2428            0 :                 m_wfeFitting += fittingError( m, n );
    2429              :             }
    2430              :         }
    2431              :     }
    2432              : 
    2433            0 :     m_wfeNCP = ncpError();
    2434              : 
    2435            0 :     m_wfeVar = m_wfeMeasurement + m_wfeTimeDelay + m_wfeFitting + m_wfeChromScintOPD + m_wfeChromIndex + m_wfeAnisoOPD +
    2436            0 :                m_wfeNCP;
    2437              : 
    2438            0 :     m_strehl = exp( -1 * m_wfeVar );
    2439              : 
    2440            0 :     m_specsChanged = false;
    2441            0 : }
    2442              : 
    2443              : template <typename realT, class inputSpectT, typename iosT>
    2444            0 : realT aoSystem<realT, inputSpectT, iosT>::wfeVar()
    2445              : {
    2446            0 :     if( m_specsChanged || m_dminChanged )
    2447            0 :         calcStrehl();
    2448              : 
    2449            0 :     return m_wfeVar;
    2450              : }
    2451              : 
    2452              : template <typename realT, class inputSpectT, typename iosT>
    2453            0 : realT aoSystem<realT, inputSpectT, iosT>::strehl()
    2454              : {
    2455            0 :     if( m_specsChanged || m_dminChanged )
    2456            0 :         calcStrehl();
    2457              : 
    2458            0 :     return m_strehl;
    2459              : }
    2460              : 
    2461              : template <typename realT, class inputSpectT, typename iosT>
    2462              : template <typename varFuncT>
    2463            0 : realT aoSystem<realT, inputSpectT, iosT>::C_( int m, int n, bool normStrehl, varFuncT varFunc, int doFittingError )
    2464              : {
    2465            0 :     if( m == 0 && n == 0 )
    2466            0 :         return 0;
    2467              : 
    2468              :     // we run this to optimize regardless of whether we use it
    2469              :     //  if already optimized then this is a minor hit
    2470            0 :     realT S = strehl();
    2471              : 
    2472            0 :     if( !normStrehl )
    2473            0 :         S = 1;
    2474              : 
    2475            0 :     if( doFittingError != FITTING_ERROR_NO )
    2476              :     {
    2477            0 :         int mn_max = m_D / ( 2. * d_opt() );
    2478              : 
    2479            0 :         if( m_controlledModes( m_fit_mn_max + m, m_fit_mn_max + n ) == false )
    2480              :         {
    2481            0 :             if( doFittingError == FITTING_ERROR_ZERO )
    2482            0 :                 return 0;
    2483              : 
    2484            0 :             realT fe = fittingError( m, n );
    2485              : 
    2486            0 :             realT k = sqrt( m * m + n * n ) / m_D;
    2487              : 
    2488            0 :             if( doFittingError == FITTING_ERROR_X )
    2489            0 :                 fe *= ( atm.X( k, m_lam_sci, m_secZeta ) );
    2490            0 :             else if( doFittingError == FITTING_ERROR_Y )
    2491            0 :                 fe *= ( atm.Y( k, m_lam_sci, m_secZeta ) );
    2492              :             else
    2493              :             {
    2494            0 :                 std::cerr << "Unknown doFittingError\n";
    2495            0 :                 exit( -1 );
    2496              :             }
    2497              : 
    2498            0 :             return fe / S;
    2499              :         }
    2500              :     }
    2501              :     // get if not doing fitting error or if inside control region:
    2502              : 
    2503            0 :     realT var = ( this->*varFunc )( m, n );
    2504              : 
    2505            0 :     return var / S;
    2506              : }
    2507              : 
    2508              : template <typename realT, class inputSpectT, typename iosT>
    2509              : template <typename imageT, typename CfuncT>
    2510              : void aoSystem<realT, inputSpectT, iosT>::C_Map( imageT &im, CfuncT Cfunc, bool normStrehl )
    2511              : {
    2512              :     int dim1 = im.rows();
    2513              :     int dim2 = im.cols();
    2514              : 
    2515              :     int mc = 0.5 * ( dim1 - 1 );
    2516              :     int nc = 0.5 * ( dim2 - 1 );
    2517              : 
    2518              :     for( int i = 0; i < dim1; ++i )
    2519              :     {
    2520              :         int m = i - mc;
    2521              : 
    2522              :         for( int j = 0; j < dim2; ++j )
    2523              :         {
    2524              :             int n = j - nc;
    2525              : 
    2526              :             im( i, j ) = ( this->*Cfunc )( m, n, normStrehl );
    2527              :         }
    2528              :     }
    2529              : }
    2530              : 
    2531              : template <typename realT, class inputSpectT, typename iosT>
    2532            0 : realT aoSystem<realT, inputSpectT, iosT>::C0var( realT m, realT n )
    2533              : {
    2534            0 :     realT k = sqrt( m * m + n * n ) / m_D;
    2535              :     ///\todo handle multiple layers
    2536            0 :     return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
    2537            0 :            ( atm.X( k, m_lam_sci, m_secZeta ) );
    2538              : }
    2539              : 
    2540              : template <typename realT, class inputSpectT, typename iosT>
    2541            0 : realT aoSystem<realT, inputSpectT, iosT>::C0( realT m, realT n, bool normStrehl )
    2542              : {
    2543              : 
    2544            0 :     return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C0var, FITTING_ERROR_NO );
    2545              : }
    2546              : 
    2547              : template <typename realT, class inputSpectT, typename iosT>
    2548              : template <typename imageT>
    2549              : void aoSystem<realT, inputSpectT, iosT>::C0Map( imageT &im, bool normStrehl )
    2550              : {
    2551              :     C_Map( im, &aoSystem<realT, inputSpectT, iosT>::C0, normStrehl );
    2552              : }
    2553              : 
    2554              : template <typename realT, class inputSpectT, typename iosT>
    2555            0 : realT aoSystem<realT, inputSpectT, iosT>::C1var( realT m, realT n )
    2556              : {
    2557            0 :     realT k = sqrt( m * m + n * n ) / m_D;
    2558              : 
    2559              :     ///\todo handle multiple layers
    2560            0 :     return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
    2561            0 :            ( atm.Y( k, m_lam_sci, m_secZeta ) );
    2562              : }
    2563              : 
    2564              : template <typename realT, class inputSpectT, typename iosT>
    2565            0 : realT aoSystem<realT, inputSpectT, iosT>::C1( realT m, realT n, bool normStrehl )
    2566              : {
    2567            0 :     return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C1var, FITTING_ERROR_NO );
    2568              : }
    2569              : 
    2570              : template <typename realT, class inputSpectT, typename iosT>
    2571              : template <typename imageT>
    2572              : void aoSystem<realT, inputSpectT, iosT>::C1Map( imageT &im, bool normStrehl )
    2573              : {
    2574              :     C_Map( im, &aoSystem<realT, inputSpectT, iosT>::C1, normStrehl );
    2575              : }
    2576              : 
    2577              : template <typename realT, class inputSpectT, typename iosT>
    2578            0 : realT aoSystem<realT, inputSpectT, iosT>::C2var( realT m, realT n )
    2579              : {
    2580            0 :     d_opt();
    2581            0 :     return measurementError( m, n, d_opt(), m_bin_opt ) + timeDelayError( m, n, d_opt(), m_bin_opt );
    2582              : }
    2583              : 
    2584              : template <typename realT, class inputSpectT, typename iosT>
    2585            0 : realT aoSystem<realT, inputSpectT, iosT>::C2( realT m, realT n, bool normStrehl )
    2586              : {
    2587            0 :     return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C2var, FITTING_ERROR_X );
    2588              : }
    2589              : 
    2590              : template <typename realT, class inputSpectT, typename iosT>
    2591              : template <typename imageT>
    2592              : void aoSystem<realT, inputSpectT, iosT>::C2Map( imageT &im, bool normStrehl )
    2593              : {
    2594              :     C_Map( im, &aoSystem<realT, inputSpectT, iosT>::C2, normStrehl );
    2595              : }
    2596              : 
    2597              : template <typename realT, class inputSpectT, typename iosT>
    2598            0 : realT aoSystem<realT, inputSpectT, iosT>::C3var( realT m, realT n )
    2599              : {
    2600            0 :     return 0; // measurementError(m, n) + timeDelayError(m,n);
    2601              : }
    2602              : 
    2603              : template <typename realT, class inputSpectT, typename iosT>
    2604            0 : realT aoSystem<realT, inputSpectT, iosT>::C3( realT m, realT n, bool normStrehl )
    2605              : {
    2606            0 :     return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C3var, FITTING_ERROR_ZERO ); // FITTING_ERROR_Y);
    2607              : }
    2608              : 
    2609              : template <typename realT, class inputSpectT, typename iosT>
    2610              : template <typename imageT>
    2611              : void aoSystem<realT, inputSpectT, iosT>::C3Map( imageT &im, bool normStrehl )
    2612              : {
    2613              :     C_Map( im, &aoSystem<realT, inputSpectT, iosT>::C3, normStrehl );
    2614              : }
    2615              : 
    2616              : template <typename realT, class inputSpectT, typename iosT>
    2617            0 : realT aoSystem<realT, inputSpectT, iosT>::C4var( realT m, realT n )
    2618              : {
    2619            0 :     realT k = sqrt( m * m + n * n ) / m_D;
    2620              : 
    2621              :     ///\todo handle multiple layers
    2622              :     // This does not need to be divided by X b/c we haven't multiplied by it, this is is C0/X.
    2623            0 :     return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
    2624            0 :            atm.dX( k, m_lam_sci, m_lam_wfs );
    2625              : }
    2626              : 
    2627              : template <typename realT, class inputSpectT, typename iosT>
    2628            0 : realT aoSystem<realT, inputSpectT, iosT>::C4( realT m, realT n, bool normStrehl )
    2629              : {
    2630            0 :     return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C4var, FITTING_ERROR_ZERO );
    2631              : }
    2632              : 
    2633              : template <typename realT, class inputSpectT, typename iosT>
    2634              : template <typename imageT>
    2635              : void aoSystem<realT, inputSpectT, iosT>::C4Map( imageT &im, bool normStrehl )
    2636              : {
    2637              :     C_Map( im, &aoSystem<realT, inputSpectT, iosT>::C4, normStrehl );
    2638              : }
    2639              : 
    2640              : template <typename realT, class inputSpectT, typename iosT>
    2641            0 : realT aoSystem<realT, inputSpectT, iosT>::C5var( realT m, realT n )
    2642              : {
    2643            0 :     realT k = sqrt( m * m + n * n ) / m_D;
    2644              : 
    2645              :     ///\todo handle multiple layers
    2646              :     // This does not need to be divided by Y b/c we haven't multiplied by it, this is is C1/Y.
    2647            0 :     return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
    2648            0 :            atm.dY( k, m_lam_sci, m_lam_wfs );
    2649              : }
    2650              : 
    2651              : template <typename realT, class inputSpectT, typename iosT>
    2652            0 : realT aoSystem<realT, inputSpectT, iosT>::C5( realT m, realT n, bool normStrehl )
    2653              : {
    2654            0 :     return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C5var, FITTING_ERROR_ZERO );
    2655              : }
    2656              : 
    2657              : template <typename realT, class inputSpectT, typename iosT>
    2658              : template <typename imageT>
    2659              : void aoSystem<realT, inputSpectT, iosT>::C5Map( imageT &im, bool normStrehl )
    2660              : {
    2661              :     C_Map( im, &aoSystem<realT, inputSpectT, iosT>::C5, normStrehl );
    2662              : }
    2663              : 
    2664              : template <typename realT, class inputSpectT, typename iosT>
    2665            0 : realT aoSystem<realT, inputSpectT, iosT>::C6var( realT m, realT n )
    2666              : {
    2667            0 :     realT ni = atm.n_air( m_lam_sci );
    2668            0 :     realT nw = atm.n_air( m_lam_wfs );
    2669              : 
    2670            0 :     return C0var( m, n ) * pow( ( ni - nw ) / ( ni - 1 ), 2 ); // Fixes error in Eqn 29
    2671              : }
    2672              : 
    2673              : template <typename realT, class inputSpectT, typename iosT>
    2674            0 : realT aoSystem<realT, inputSpectT, iosT>::C6( realT m, realT n, bool normStrehl )
    2675              : {
    2676            0 :     return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C6var, FITTING_ERROR_ZERO );
    2677              : }
    2678              : 
    2679              : template <typename realT, class inputSpectT, typename iosT>
    2680              : template <typename imageT>
    2681              : void aoSystem<realT, inputSpectT, iosT>::C6Map( imageT &im, bool normStrehl )
    2682              : {
    2683              :     C_Map( im, &aoSystem<realT, inputSpectT, iosT>::C6, normStrehl );
    2684              : }
    2685              : 
    2686              : template <typename realT, class inputSpectT, typename iosT>
    2687            0 : realT aoSystem<realT, inputSpectT, iosT>::C7var( realT m, realT n )
    2688              : {
    2689            0 :     realT k = sqrt( m * m + n * n ) / m_D;
    2690              : 
    2691              :     ///\todo this needs to handle multiple layers -- do we violate an assumption of L_0 isn't constant?
    2692            0 :     return psd( atm, 0, k, m_secZeta ) / pow( m_D, 2 ) * pow( atm.lam_0() / m_lam_sci, 2 ) *
    2693            0 :            atm.X_Z( k, m_lam_wfs, m_lam_sci, m_secZeta );
    2694              : }
    2695              : 
    2696              : template <typename realT, class inputSpectT, typename iosT>
    2697            0 : realT aoSystem<realT, inputSpectT, iosT>::C7( realT m, realT n, bool normStrehl )
    2698              : {
    2699            0 :     return C_( m, n, normStrehl, &aoSystem<realT, inputSpectT, iosT>::C7var, FITTING_ERROR_ZERO );
    2700              : }
    2701              : 
    2702              : template <typename realT, class inputSpectT, typename iosT>
    2703              : template <typename imageT>
    2704              : void aoSystem<realT, inputSpectT, iosT>::C7Map( imageT &im, bool normStrehl )
    2705              : {
    2706              :     C_Map( im, &aoSystem<realT, inputSpectT, iosT>::C7, normStrehl );
    2707              : }
    2708              : 
    2709              : template <typename realT, class inputSpectT, typename iosT>
    2710            1 : iosT &aoSystem<realT, inputSpectT, iosT>::dumpAOSystem( iosT &ios )
    2711              : {
    2712            1 :     ios << "# AO Params:\n";
    2713            1 :     ios << "#    D = " << D() << '\n';
    2714              : 
    2715            1 :     if( m_d_min.size() > 0 )
    2716              :     {
    2717            1 :         ios << "#    d_min = " << d_min( (size_t)0 );
    2718            1 :         for( size_t n = 1; n < m_d_min.size(); ++n )
    2719            0 :             ios << ',' << d_min( n );
    2720            1 :         ios << '\n';
    2721              :     }
    2722              :     else
    2723            0 :         ios << "#    d_min = null\n";
    2724              : 
    2725            1 :     ios << "#    optd = " << std::boolalpha << m_optd << '\n';
    2726            1 :     ios << "#    d_opt_delta = " << optd_delta() << '\n';
    2727            1 :     ios << "#    lam_sci = " << lam_sci() << '\n';
    2728            1 :     ios << "#    F0 = " << F0() << '\n';
    2729            1 :     ios << "#    starMag = " << starMag() << '\n';
    2730            1 :     ios << "#    lam_sci = " << lam_sci() << '\n';
    2731            1 :     ios << "#    zeta    = " << zeta() << '\n';
    2732            1 :     ios << "#    lam_wfs = " << lam_wfs() << '\n';
    2733              : 
    2734            1 :     if( npix_wfs().size() > 0 )
    2735              :     {
    2736            1 :         ios << "#    npix_wfs = " << npix_wfs( (size_t)0 );
    2737            1 :         for( size_t n = 1; n < npix_wfs().size(); ++n )
    2738            0 :             ios << ',' << npix_wfs( n );
    2739            1 :         ios << '\n';
    2740              :     }
    2741              :     else
    2742            0 :         ios << "#    npix_wfs = null\n";
    2743              : 
    2744            1 :     if( ron_wfs().size() > 0 )
    2745              :     {
    2746            1 :         ios << "#    ron_wfs = " << ron_wfs( (size_t)0 );
    2747            1 :         for( size_t n = 1; n < ron_wfs().size(); ++n )
    2748            0 :             ios << ',' << ron_wfs( n );
    2749            1 :         ios << '\n';
    2750              :     }
    2751              :     else
    2752            0 :         ios << "#    ron_wfs = null\n";
    2753              : 
    2754            1 :     if( Fbg().size() > 0 )
    2755              :     {
    2756            1 :         ios << "#    Fbg = " << Fbg( (size_t)0 );
    2757            1 :         for( size_t n = 1; n < Fbg().size(); ++n )
    2758            0 :             ios << ',' << Fbg( n );
    2759            1 :         ios << '\n';
    2760              :     }
    2761              :     else
    2762            0 :         ios << "#    Fbg = null\n";
    2763              : 
    2764            1 :     if( minTauWFS().size() > 0 )
    2765              :     {
    2766            1 :         ios << "#    minTauWFS = " << minTauWFS( (size_t)0 );
    2767            1 :         for( size_t n = 1; n < minTauWFS().size(); ++n )
    2768            0 :             ios << ',' << minTauWFS( n );
    2769            1 :         ios << '\n';
    2770              :     }
    2771              :     else
    2772            0 :         ios << "#    minTauWFS = null\n";
    2773              : 
    2774            1 :     ios << "#    bin_npix = " << std::boolalpha << m_bin_npix << '\n';
    2775            1 :     ios << "#    tauWFS = " << tauWFS() << '\n';
    2776            1 :     ios << "#    optTau = " << std::boolalpha << m_optTau << '\n';
    2777            1 :     ios << "#    deltaTau = " << deltaTau() << '\n';
    2778            1 :     ios << "#    fit_mn_max = " << m_fit_mn_max << '\n';
    2779            1 :     ios << "#    spatialFilter_ku = " << m_spatialFilter_ku << '\n';
    2780            1 :     ios << "#    spatialFilter_kv = " << m_spatialFilter_kv << '\n';
    2781            1 :     ios << "#    ncp_wfe = " << m_ncp_wfe << '\n';
    2782            1 :     ios << "#    ncp_alpha = " << m_ncp_alpha << '\n';
    2783              : 
    2784            1 :     if( m_wfsBeta == 0 )
    2785              :     {
    2786            0 :         throw( mx::exception<verboseT>( error_t::paramnotset, "The WFS is not assigned." ) );
    2787              :     }
    2788              : 
    2789            1 :     m_wfsBeta->dumpWFS( ios );
    2790            1 :     psd.dumpPSD( ios );
    2791            1 :     atm.dumpAtmosphere( ios );
    2792              : 
    2793            1 :     dumpGitStatus( ios );
    2794              : 
    2795            1 :     return ios;
    2796              : }
    2797              : 
    2798              : template <typename realT, class inputSpectT, typename iosT>
    2799           10 : void aoSystem<realT, inputSpectT, iosT>::setupConfig( app::appConfigurator &config )
    2800              : {
    2801              :     using namespace mx::app;
    2802              : 
    2803              :     // AO System configuration
    2804           10 :     config.add( "aosys.wfs",
    2805           20 :                 "",
    2806           20 :                 "aosys.wfs",
    2807              :                 argType::Required,
    2808           20 :                 "aosys",
    2809           20 :                 "wfs",
    2810              :                 false,
    2811           20 :                 "string",
    2812           30 :                 "The WFS type: idealWFS, unmodPyWFS, asympModPyWFS, shwfs, calculatedWFS" );
    2813           10 :     config.add( "aosys.wfs_beta_p",
    2814           20 :                 "",
    2815           20 :                 "aosys.wfs_beta_p",
    2816              :                 argType::Required,
    2817           20 :                 "aosys",
    2818           20 :                 "wfs_beta_p",
    2819              :                 false,
    2820           20 :                 "string",
    2821           30 :                 "The beta_p file path for calcualtedWFS" );
    2822           10 :     config.add( "aosys.wfs_beta_r",
    2823           20 :                 "",
    2824           20 :                 "aosys.wfs_beta_r",
    2825              :                 argType::Required,
    2826           20 :                 "aosys",
    2827           20 :                 "wfs_beta_r",
    2828              :                 false,
    2829           20 :                 "string",
    2830           30 :                 "The beta_r file path for calcualtedWFS" );
    2831           10 :     config.add( "aosys.wfs_sensitivity",
    2832           20 :                 "",
    2833           20 :                 "aosys.wfs_sensitivity",
    2834              :                 argType::Required,
    2835           20 :                 "aosys",
    2836           20 :                 "wfs_sensitivity",
    2837              :                 false,
    2838           20 :                 "bool",
    2839           30 :                 "Flag indicating that beta_p/beta_r are sensitivities (inverse) [default false]" );
    2840              :     config
    2841          140 :         .add( "aosys.D", "", "aosys.D", argType::Required, "aosys", "D", false, "real", "The telescope diameter [m]" );
    2842           10 :     config.add( "aosys.d_min",
    2843           20 :                 "",
    2844           20 :                 "aosys.d_min",
    2845              :                 argType::Required,
    2846           20 :                 "aosys",
    2847           20 :                 "d_min",
    2848              :                 false,
    2849           20 :                 "real",
    2850           30 :                 "The minimum actuator spacing [m]" );
    2851           10 :     config.add( "aosys.optd",
    2852           20 :                 "",
    2853           20 :                 "aosys.optd",
    2854              :                 argType::Optional,
    2855           20 :                 "aosys",
    2856           20 :                 "optd",
    2857              :                 false,
    2858           20 :                 "bool",
    2859           30 :                 "Whether or not the actuator spacing is optimized" );
    2860           10 :     config.add( "aosys.optd_delta",
    2861           20 :                 "",
    2862           20 :                 "aosys.optd_delta",
    2863              :                 argType::Required,
    2864           20 :                 "aosys",
    2865           20 :                 "optd_delta",
    2866              :                 false,
    2867           20 :                 "bool",
    2868           30 :                 "The fractional change from d_min used in optimization.  Set to 1 (default) for integer binnings, > 1 "
    2869              :                 "for finer sampling." );
    2870           10 :     config.add( "aosys.F0",
    2871           20 :                 "",
    2872           20 :                 "aosys.F0",
    2873              :                 argType::Required,
    2874           20 :                 "aosys",
    2875           20 :                 "F0",
    2876              :                 false,
    2877           20 :                 "real",
    2878           30 :                 "Zero-mag photon flux, [photons/sec]" );
    2879           10 :     config.add( "aosys.lam_wfs",
    2880           20 :                 "",
    2881           20 :                 "aosys.lam_wfs",
    2882              :                 argType::Required,
    2883           20 :                 "aosys",
    2884           20 :                 "lam_wfs",
    2885              :                 false,
    2886           20 :                 "real",
    2887           30 :                 "WFS wavelength [m]" );
    2888           10 :     config.add( "aosys.npix_wfs",
    2889           20 :                 "",
    2890           20 :                 "aosys.npix_wfs",
    2891              :                 argType::Required,
    2892           20 :                 "aosys",
    2893           20 :                 "npix_wfs",
    2894              :                 false,
    2895           20 :                 "vector<real>",
    2896           30 :                 "The number of pixels in the WFS" );
    2897           10 :     config.add( "aosys.ron_wfs",
    2898           20 :                 "",
    2899           20 :                 "aosys.ron_wfs",
    2900              :                 argType::Required,
    2901           20 :                 "aosys",
    2902           20 :                 "ron_wfs",
    2903              :                 false,
    2904           20 :                 "vector<real>",
    2905           30 :                 "WFS readout noise [photons/read]" );
    2906           10 :     config.add( "aosys.bin_npix",
    2907           20 :                 "",
    2908           20 :                 "aosys.bin_npix",
    2909              :                 argType::Required,
    2910           20 :                 "aosys",
    2911           20 :                 "bin_npix",
    2912              :                 false,
    2913           20 :                 "bool",
    2914           30 :                 "Whether or not WFS pixels are re-binned along with actuator spacing optimization" );
    2915           10 :     config.add( "aosys.Fbg",
    2916           20 :                 "",
    2917           20 :                 "aosys.Fbg",
    2918              :                 argType::Required,
    2919           20 :                 "aosys",
    2920           20 :                 "Fbg",
    2921              :                 false,
    2922           20 :                 "vector<real>",
    2923           30 :                 "Background counts, [counts/pix/sec]" );
    2924           10 :     config.add( "aosys.tauWFS",
    2925           20 :                 "",
    2926           20 :                 "aosys.tauWFS",
    2927              :                 argType::Required,
    2928           20 :                 "aosys",
    2929           20 :                 "tauWFS",
    2930              :                 false,
    2931           20 :                 "real",
    2932           30 :                 "WFS integration time [s]" );
    2933           10 :     config.add( "aosys.minTauWFS",
    2934           20 :                 "",
    2935           20 :                 "aosys.minTauWFS",
    2936              :                 argType::Required,
    2937           20 :                 "aosys",
    2938           20 :                 "minTauWFS",
    2939              :                 false,
    2940           20 :                 "vector<real>",
    2941           30 :                 "Minimum WFS integration time [s]" );
    2942           10 :     config.add( "aosys.deltaTau",
    2943           20 :                 "",
    2944           20 :                 "aosys.deltaTau",
    2945              :                 argType::Required,
    2946           20 :                 "aosys",
    2947           20 :                 "deltaTau",
    2948              :                 false,
    2949           20 :                 "real",
    2950           30 :                 "Loop delay [s]" );
    2951           10 :     config.add( "aosys.optTau",
    2952           20 :                 "",
    2953           20 :                 "aosys.optTau",
    2954              :                 argType::Optional,
    2955           20 :                 "aosys",
    2956           20 :                 "optTau",
    2957              :                 false,
    2958           20 :                 "bool",
    2959           30 :                 "Whether or not the integration time is optimized" );
    2960           10 :     config.add( "aosys.lam_sci",
    2961           20 :                 "",
    2962           20 :                 "aosys.lam_sci",
    2963              :                 argType::Required,
    2964           20 :                 "aosys",
    2965           20 :                 "lam_sci",
    2966              :                 false,
    2967           20 :                 "real",
    2968           30 :                 "Science wavelength [m]" );
    2969           10 :     config.add( "aosys.zeta",
    2970           20 :                 "",
    2971           20 :                 "aosys.zeta",
    2972              :                 argType::Required,
    2973           20 :                 "aosys",
    2974           20 :                 "zeta",
    2975              :                 false,
    2976           20 :                 "real",
    2977           30 :                 "Zenith distance [rad]" );
    2978           10 :     config.add( "aosys.fit_mn_max",
    2979           20 :                 "",
    2980           20 :                 "aosys.fit_mn_max",
    2981              :                 argType::Required,
    2982           20 :                 "aosys",
    2983           20 :                 "fit_mn_max",
    2984              :                 false,
    2985           20 :                 "real",
    2986           30 :                 "Maximum spatial frequency index to use for analysis" );
    2987           10 :     config.add( "aosys.circularLimit",
    2988           20 :                 "",
    2989           20 :                 "aosys.circularLimit",
    2990              :                 argType::Optional,
    2991           20 :                 "aosys",
    2992           20 :                 "circularLimit",
    2993              :                 false,
    2994           20 :                 "bool",
    2995           30 :                 " Flag to indicate that the spatial frequency limit is circular, not square." );
    2996           10 :     config.add( "aosys.spatialFilter_ku",
    2997           20 :                 "",
    2998           20 :                 "aosys.spatialFilter_ku",
    2999              :                 argType::Required,
    3000           20 :                 "aosys",
    3001           20 :                 "spatialFilter_ku",
    3002              :                 false,
    3003           20 :                 "real",
    3004           30 :                 "Spatial filter cutoff frequency in u [m^-1]" );
    3005           10 :     config.add( "aosys.spatialFilter_kv",
    3006           20 :                 "",
    3007           20 :                 "aosys.spatialFilter_kv",
    3008              :                 argType::Required,
    3009           20 :                 "aosys",
    3010           20 :                 "spatialFilter_kv",
    3011              :                 false,
    3012           20 :                 "real",
    3013           30 :                 "Spatial filter cutoff frequency in v [m^-1]" );
    3014           10 :     config.add( "aosys.ncp_wfe",
    3015           20 :                 "",
    3016           20 :                 "aosys.ncp_wfe",
    3017              :                 argType::Required,
    3018           20 :                 "aosys",
    3019           20 :                 "ncp_wfe",
    3020              :                 false,
    3021           20 :                 "real",
    3022           30 :                 "NCP WFE between 1 lambda/D and fit_mn_max [rad^2]" );
    3023           10 :     config.add( "aosys.ncp_alpha",
    3024           20 :                 "",
    3025           20 :                 "aosys.ncp_alpha",
    3026              :                 argType::Required,
    3027           20 :                 "aosys",
    3028           20 :                 "ncp_alpha",
    3029              :                 false,
    3030           20 :                 "real",
    3031           30 :                 "PSD index for NCP WFE" );
    3032           10 :     config.add( "aosys.starMag",
    3033           20 :                 "",
    3034           20 :                 "aosys.starMag",
    3035              :                 argType::Required,
    3036           20 :                 "aosys",
    3037           20 :                 "starMag",
    3038              :                 false,
    3039           20 :                 "real",
    3040           30 :                 "Star magnitude" );
    3041           10 :     config.add( "aosys.starMags",
    3042           20 :                 "",
    3043           20 :                 "aosys.starMags",
    3044              :                 argType::Required,
    3045           20 :                 "aosys",
    3046           20 :                 "starMags",
    3047              :                 false,
    3048           20 :                 "real vector",
    3049           20 :                 "A vector of star magnitudes" );
    3050              : 
    3051           10 :     atm.setupConfig( config );
    3052           10 :     psd.setupConfig( config );
    3053           10 : }
    3054              : 
    3055              : template <typename realT, class inputSpectT, typename iosT>
    3056           10 : error_t aoSystem<realT, inputSpectT, iosT>::loadConfig( app::appConfigurator &config )
    3057              : {
    3058              :     // WFS
    3059           20 :     if( config.isSet( "aosys.wfs" ) )
    3060              :     {
    3061            4 :         std::string wfsStr;
    3062            4 :         config( wfsStr, "aosys.wfs" );
    3063              : 
    3064            4 :         if( wfsStr == "ideal" )
    3065              :         {
    3066            1 :             wfsBeta<wfs<realT, iosT>>( nullptr );
    3067              :         }
    3068            3 :         else if( wfsStr == "unmodPyWFS" )
    3069              :         {
    3070            1 :             wfsBeta<pywfsUnmod<realT, iosT>>( nullptr );
    3071              :         }
    3072            2 :         else if( wfsStr == "asympModPyWFS" )
    3073              :         {
    3074            1 :             wfsBeta<pywfsModAsymptotic<realT>>( nullptr );
    3075              :         }
    3076            1 :         else if( wfsStr == "SHWFS" )
    3077              :         {
    3078            1 :             wfsBeta<shwfs<realT, iosT>>( nullptr );
    3079              :         }
    3080            0 :         else if( wfsStr == "calculatedWFS" )
    3081              :         {
    3082            0 :             wfsBeta<calculatedWFS<realT, iosT>>( nullptr );
    3083              : 
    3084            0 :             calculatedWFS<realT, iosT> *cwfs = static_cast<calculatedWFS<realT, iosT> *>( m_wfsBeta );
    3085            0 :             config( cwfs->m_beta_p_file, "aosys.wfs_beta_p" );
    3086            0 :             config( cwfs->m_beta_r_file, "aosys.wfs_beta_r" );
    3087            0 :             bool sens = cwfs->m_sensitivity;
    3088            0 :             config( sens, "aosys.wfs_sensitivity" );
    3089            0 :             if( config.isSet( "aosys.wfs_sensitivity" ) )
    3090              :             {
    3091            0 :                 cwfs->m_sensitivity = sens;
    3092              :             }
    3093              :         }
    3094              :         else
    3095              :         {
    3096            0 :             throw( mx::exception<verboseT>( error_t::invalidarg, "unknown WFS " + wfsStr + " specified" ) );
    3097              :         }
    3098            4 :     }
    3099              : 
    3100              :     // We load the default value, get the config value (which will be the default if not set), and
    3101              :     // then if it was set, call the setter function so any side effects are captured.
    3102              : 
    3103              :     // diameter
    3104           10 :     realT nD = D();
    3105           20 :     config( nD, "aosys.D" );
    3106           20 :     if( config.isSet( "aosys.D" ) )
    3107            1 :         D( nD );
    3108              : 
    3109              :     // d_min
    3110           10 :     std::vector<realT> nd_min = d_min();
    3111           20 :     config( nd_min, "aosys.d_min" );
    3112           20 :     if( config.isSet( "aosys.d_min" ) )
    3113            1 :         d_min( nd_min );
    3114              : 
    3115           10 :     bool noptd = optd();
    3116           20 :     config( noptd, "aosys.optd" );
    3117           20 :     if( config.isSet( "aosys.optd" ) )
    3118            1 :         optd( noptd );
    3119              : 
    3120           10 :     realT noptd_delta = optd_delta();
    3121           20 :     config( noptd_delta, "aosys.optd_delta" );
    3122           20 :     if( config.isSet( "aosys.optd_delta" ) )
    3123            1 :         optd_delta( noptd_delta );
    3124              : 
    3125           10 :     realT nlam_wfs = lam_wfs();
    3126           20 :     config( nlam_wfs, "aosys.lam_wfs" );
    3127           20 :     if( config.isSet( "aosys.lam_wfs" ) )
    3128            1 :         lam_wfs( nlam_wfs );
    3129              : 
    3130              :     // npix_wfs
    3131           10 :     std::vector<realT> nnpix_wfs = npix_wfs();
    3132           20 :     config( nnpix_wfs, "aosys.npix_wfs" );
    3133           20 :     if( config.isSet( "aosys.npix_wfs" ) )
    3134            1 :         npix_wfs( nnpix_wfs );
    3135              : 
    3136              :     // ron_wfs
    3137           10 :     std::vector<realT> nron_wfs = ron_wfs();
    3138           20 :     config( nron_wfs, "aosys.ron_wfs" );
    3139           20 :     if( config.isSet( "aosys.ron_wfs" ) )
    3140            1 :         ron_wfs( nron_wfs );
    3141              : 
    3142              :     // Fbg
    3143           10 :     std::vector<realT> nFbg = Fbg();
    3144           20 :     config( nFbg, "aosys.Fbg" );
    3145           20 :     if( config.isSet( "aosys.Fbg" ) )
    3146            1 :         Fbg( nFbg );
    3147              : 
    3148              :     // minTauWFS
    3149           10 :     std::vector<realT> nminTauWFS = minTauWFS();
    3150           20 :     config( nminTauWFS, "aosys.minTauWFS" );
    3151           20 :     if( config.isSet( "aosys.minTauWFS" ) )
    3152            1 :         minTauWFS( nminTauWFS );
    3153              : 
    3154              :     // bin_npix
    3155           10 :     bool nbin_npix = bin_npix();
    3156           20 :     config( nbin_npix, "aosys.bin_npix" );
    3157           20 :     if( config.isSet( "aosys.bin_npix" ) )
    3158            1 :         bin_npix( nbin_npix );
    3159              : 
    3160              :     // tauWFS
    3161           10 :     realT ntauWFS = tauWFS();
    3162           20 :     config( ntauWFS, "aosys.tauWFS" );
    3163           20 :     if( config.isSet( "aosys.tauWFS" ) )
    3164            1 :         tauWFS( ntauWFS );
    3165              : 
    3166              :     // deltaTau
    3167           10 :     realT ndeltaTau = deltaTau();
    3168           20 :     config( ndeltaTau, "aosys.deltaTau" );
    3169           20 :     if( config.isSet( "aosys.deltaTau" ) )
    3170            1 :         deltaTau( ndeltaTau );
    3171              : 
    3172              :     // optTau
    3173           10 :     bool noptTau = optTau();
    3174           20 :     config( noptTau, "aosys.optTau" );
    3175           20 :     if( config.isSet( "aosys.optTau" ) )
    3176            1 :         optTau( noptTau );
    3177              : 
    3178              :     // lam_sci
    3179           10 :     realT nlam_sci = lam_sci();
    3180           20 :     config( nlam_sci, "aosys.lam_sci" );
    3181           20 :     if( config.isSet( "aosys.lam_sci" ) )
    3182            1 :         lam_sci( nlam_sci );
    3183              : 
    3184              :     // zeta
    3185           10 :     realT nzeta = zeta();
    3186           20 :     config( nzeta, "aosys.zeta" );
    3187           20 :     if( config.isSet( "aosys.zeta" ) )
    3188            1 :         zeta( nzeta );
    3189              : 
    3190              :     // fit_mn_max
    3191           10 :     realT fmnm = fit_mn_max();
    3192           20 :     config( fmnm, "aosys.fit_mn_max" );
    3193           20 :     if( config.isSet( "aosys.fit_mn_max" ) )
    3194            1 :         fit_mn_max( fmnm );
    3195              : 
    3196              :     // circularLimit
    3197           10 :     bool cl = circularLimit();
    3198           20 :     config( cl, "aosys.circularLimit" );
    3199           20 :     if( config.isSet( "aosys.circularLimit" ) )
    3200            1 :         circularLimit( cl );
    3201              : 
    3202              :     // spatialFilter_ku
    3203           10 :     realT ku = spatialFilter_ku();
    3204           20 :     config( ku, "aosys.spatialFilter_ku" );
    3205           20 :     if( config.isSet( "aosys.spatialFilter_ku" ) )
    3206            1 :         spatialFilter_ku( ku );
    3207              : 
    3208              :     // spatialFilter_kv
    3209           10 :     realT kv = spatialFilter_kv();
    3210           20 :     config( kv, "aosys.spatialFilter_kv" );
    3211           20 :     if( config.isSet( "aosys.spatialFilter_kv" ) )
    3212            1 :         spatialFilter_kv( kv );
    3213              : 
    3214              :     // ncp_wfe
    3215           10 :     realT nwfe = ncp_wfe();
    3216           20 :     config( nwfe, "aosys.ncp_wfe" );
    3217           20 :     if( config.isSet( "aosys.ncp_wfe" ) )
    3218            1 :         ncp_wfe( nwfe );
    3219              : 
    3220              :     // ncp_alpha
    3221           10 :     realT na = ncp_alpha();
    3222           20 :     config( na, "aosys.ncp_alpha" );
    3223           20 :     if( config.isSet( "aosys.ncp_alpha" ) )
    3224            1 :         ncp_alpha( na );
    3225              : 
    3226              :     // F0
    3227           10 :     realT nF0 = F0();
    3228           20 :     config( nF0, "aosys.F0" );
    3229           20 :     if( config.isSet( "aosys.F0" ) )
    3230            1 :         F0( nF0 );
    3231              : 
    3232              :     // star_mag
    3233           10 :     realT smag = starMag();
    3234           20 :     config( smag, "aosys.starMag" );
    3235           20 :     if( config.isSet( "aosys.starMag" ) )
    3236            1 :         starMag( smag );
    3237              : 
    3238           10 :     const error_t atmosphereStatus = atm.loadConfig( config );
    3239           10 :     psd.loadConfig( config );
    3240              : 
    3241           10 :     return atmosphereStatus;
    3242           10 : }
    3243              : 
    3244              : extern template class aoSystem<float, vonKarmanSpectrum<float>, std::ostream>;
    3245              : 
    3246              : extern template class aoSystem<double, vonKarmanSpectrum<double>, std::ostream>;
    3247              : 
    3248              : extern template class aoSystem<long double, vonKarmanSpectrum<long double>, std::ostream>;
    3249              : 
    3250              : #ifdef HASQUAD
    3251              : extern template class aoSystem<__float128, vonKarmanSpectrum<__float128>, std::ostream>;
    3252              : #endif
    3253              : 
    3254              : } // namespace analysis
    3255              : } // namespace AO
    3256              : } // namespace mx
    3257              : 
    3258              : #endif // aoSystem_hpp
        

Generated by: LCOV version 2.0-1