LCOV - code coverage report
Current view: top level - include/wfp - fraunhoferPropagator.hpp (source / functions) Coverage Total Hit
Test: mxlib Lines: 89.2 % 65 58
Test Date: 2026-08-30 22:23:37 Functions: 90.0 % 10 9

            Line data    Source code
       1              : /** \file fraunhoferPropagator.hpp
       2              :  * \brief Declares and defines a class for Fraunhofer propagation of optical wavefronts
       3              :  * \ingroup imaging
       4              :  * \author Jared R. Males (jaredmales@gmail.com)
       5              :  *
       6              :  */
       7              : 
       8              : //***********************************************************************//
       9              : // Copyright 2015-2020 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 wfp_fraunhoferPropagator_hpp
      28              : #define wfp_fraunhoferPropagator_hpp
      29              : 
      30              : #include "../mxlib.hpp"
      31              : 
      32              : #include "../math/constants.hpp"
      33              : #include "imagingArray.hpp"
      34              : #include "imagingUtils.hpp"
      35              : 
      36              : #include "../math/ft/fftT.hpp"
      37              : 
      38              : #ifdef MXLIB_CUDA
      39              : #include "../math/cuda/templateCuda.hpp"
      40              : #include "../math/cuda/cudaPtr.hpp"
      41              : #include "../math/cuda/templateCufft.hpp"
      42              : #include "../math/cuda/templateCublas.hpp"
      43              : #endif
      44              : 
      45              : #ifdef DEBUG
      46              : #define BREAD_CRUMB std::cout << "DEBUG: " << __FILE__ << " " << __LINE__ << "\n";
      47              : #else
      48              : #define BREAD_CRUMB
      49              : #endif
      50              : 
      51              : namespace mx
      52              : {
      53              : 
      54              : namespace wfp
      55              : {
      56              : 
      57              : template <typename wavefrontT, int cudaGPU>
      58              : struct fraunhoferPropagatorArrayT;
      59              : 
      60              : template <typename wavefrontT>
      61              : struct fraunhoferPropagatorArrayT<wavefrontT, 0>
      62              : {
      63              :     typedef wavefrontT arrayT;
      64              : };
      65              : 
      66              : #ifdef MXLIB_CUDA
      67              : template <typename wavefrontT>
      68              : struct fraunhoferPropagatorArrayT<wavefrontT, 1>
      69              : {
      70              :     typedef mx::cuda::cudaPtr<typename wavefrontT::Scalar> arrayT;
      71              : };
      72              : #endif
      73              : 
      74              : /// Class to perform Fraunhofer propagation between pupil and focal planes
      75              : /** This class uses the FFT to propagate between planes, and normalizes so that flux
      76              :  * is conserved.  For propagation from pupil to focal plane, the pupil wavefront is tilted so that
      77              :  * the focal-plane image is centered at the geometric center of the array.  After propagation from
      78              :  * focal plane to pupil plane, the pupil plane wavefront is un-tilted to restore the
      79              :  * pupil to its original position.
      80              :  *
      81              :  * \tparam _wavefrontT is an Eigen::Array-like type, with std::complex values.
      82              :  *
      83              :  * \todo check if we should reverse the FFT orders to be proper
      84              :  *
      85              :  * \ingroup imaging
      86              :  */
      87              : template <typename _wavefrontT, int _cudaGPU = 0>
      88              : class fraunhoferPropagator
      89              : {
      90              : 
      91              :   public:
      92              :     /// The wavefront data type
      93              :     typedef _wavefrontT wavefrontT;
      94              : 
      95              :     static constexpr int cudaGPU = _cudaGPU;
      96              : 
      97              :     /// The complex data type
      98              :     typedef typename wavefrontT::Scalar complexT;
      99              : 
     100              :     /// The real data type
     101              :     typedef typename wavefrontT::Scalar::value_type realT;
     102              : 
     103              :     /// The array data type
     104              :     typedef typename fraunhoferPropagatorArrayT<wavefrontT, cudaGPU>::arrayT arrayT;
     105              : 
     106              :     typedef verbose::d verboseT;
     107              : 
     108              :   protected:
     109              :     /// The size of the wavefront in pixels
     110              :     int m_wavefrontSizePixels{ 0 };
     111              : 
     112              :     realT m_xcen{ 0 }; ///< x-coordinate of focal plane center, in pixels
     113              :     realT m_ycen{ 0 }; ///< x-coordinate of focal plane center, in pixels
     114              : 
     115              :     /// Determines how the image is centered.
     116              :     /** If 0 (default) it is at 0.5*(wfSz-1), if true it is shifted by 0.5*m_wholePixel in each axis.
     117              :      */
     118              :     realT m_wholePixel{ 0 };
     119              : 
     120              :     /// Phase screen for tilting the pupil plane so that the focal plane image is centered.
     121              :     fraunhoferPropagatorArrayT<wavefrontT, cudaGPU>::arrayT m_centerFocal;
     122              : 
     123              :     /// Phase screen for un-tilting the pupil plane after propagating from a centered focal plane.
     124              :     fraunhoferPropagatorArrayT<wavefrontT, cudaGPU>::arrayT m_centerPupil;
     125              : 
     126              :     /// FFT object for forward FFTs
     127              :     math::ft::fftT<complexT, complexT, 2, cudaGPU> m_fft_fwd;
     128              : 
     129              :     /// FFT object for backward FFTs
     130              :     math::ft::fftT<complexT, complexT, 2, cudaGPU> m_fft_back;
     131              : 
     132              :   public:
     133              :     /// Constructor
     134              :     fraunhoferPropagator();
     135              : 
     136              :     /// Destructor
     137              :     ~fraunhoferPropagator();
     138              : 
     139              :     /// Get the value of the wholePixel parameter
     140              :     /** The wholePixel parameter determines how the image is centered.  If 0 (default) it is at 0.5*(wfSz-1), if true it
     141              :      * is shifted by 0.5*m_wholePixel in each axis.
     142              :      *
     143              :      * \returns the value of m_wholePixel
     144              :      */
     145              :     int wholePixel();
     146              : 
     147              :     /// Set the value of the wholePixel parameter
     148              :     /** The wholePixel parameter determines how the image is centered.  If 0 (default) it is at 0.5*(wfSz-1), if true it
     149              :      * is shifted by 0.5*m_wholePixel in each axis.
     150              :      */
     151              :     void wholePixel( realT wp /**< [in] the new wholePixel value */ );
     152              : 
     153              :     /// Apply the shift to a pupil wavefront  and apply the normalization.
     154              :     /** This will center the resultant focal plane image.
     155              :      * You must have allocated the shift screens first, by calling propagatePupilToFocal, propagateFocalToPupil, or
     156              :      * setWavefrontSizePixels.
     157              :      */
     158              :     template <int ccudaGPU = cudaGPU>
     159              :     error_t shiftPupil( arrayT &complexPupil, /**< [in.out] the complex pupil plane wavefront to shift*/
     160              :                         typename std::enable_if<ccudaGPU == 0>::type * = 0 );
     161              : 
     162              :     /// Apply the shift to a pupil wavefront  and apply the normalization.
     163              :     /** This will center the resultant focal plane image.
     164              :      * You must have allocated the shift screens first, by calling propagatePupilToFocal, propagateFocalToPupil, or
     165              :      * setWavefrontSizePixels.
     166              :      */
     167              :     template <int ccudaGPU = cudaGPU>
     168              :     error_t shiftPupil( arrayT &complexPupil, /**< [in.out] the complex pupil plane wavefront to shift*/
     169              :                         typename std::enable_if<ccudaGPU == 1>::type * = 0 );
     170              : 
     171              :     /// Apply the shift to a pupil wavefront which will restore it to a centered pupil image, with correct flux.
     172              :     /** You must have allocated the shift screens first, by calling propagatePupilToFocal, propagateFocalToPupil, or
     173              :      * setWavefrontSizePixels.
     174              :      */
     175              :     template <int ccudaGPU = cudaGPU>
     176              :     error_t unshiftPupil( arrayT &complexPupil, /**< [in.out] the complex pupil plane wavefront to shift*/
     177              :                        typename std::enable_if<ccudaGPU == 0>::type * = 0 );
     178              : 
     179              :     /// Apply the shift to a pupil wavefront which will restore it to a centered pupil image, with correct flux.
     180              :     /** You must have allocated the shift screens first, by calling propagatePupilToFocal, propagateFocalToPupil, or
     181              :      * setWavefrontSizePixels.
     182              :      */
     183              :     template <int ccudaGPU = cudaGPU>
     184              :     error_t unshiftPupil( arrayT &complexPupil, /**< [in.out] the complex pupil plane wavefront to shift*/
     185              :                        typename std::enable_if<ccudaGPU == 1>::type * = 0 );
     186              : 
     187              :   public:
     188              :     /// Propagate the wavefront from the pupil plane to the focal plane
     189              :     /** The pupil plane wavefront (complexPupil) is multiplied by a tilt to place the
     190              :      * image in the geometric center of the focal plane.  This can be prevented by
     191              :      * setting doCenter to false.
     192              :      *
     193              :      */
     194              :     error_t propagatePupilToFocal( arrayT &complexFocal, /**< [out] the focal plane wavefront.  Must be
     195              :                                                                      pre-allocated to same size as complexPupil.*/
     196              :                                 arrayT &complexPupil, /**< [in] the pupil plane wavefront. Modified due
     197              :                                                                     to application of centering tilt.*/
     198              :                                 bool doCenter = true  /**< [in] [optional] set to false to not apply
     199              :                                                                            the centering shift*/
     200              :     );
     201              : 
     202              :     /// Propagate the wavefront from Focal plane to Pupil plane
     203              :     /** After the fourier transform, the output pupil plane wavefront is de-tilted, restoring it
     204              :      * to the state prior to calling \ref propagatePupilToFocal.  This can be prevented by
     205              :      * setting doCenter to false.
     206              :      *
     207              :      */
     208              :     error_t propagateFocalToPupil( arrayT &complexPupil, /**< [out] the pupil plane wavefront. Must be
     209              :                                                                      pre-allocated to same size as complexFocal.*/
     210              :                                 arrayT &complexFocal, /**< [in] the focal plane wavefront. */
     211              :                                 bool doCenter = true  /**< [in] [optional] set to false to not apply
     212              :                                                                            the centering shift*/
     213              :     );
     214              : 
     215              :     /// Set the size of the wavefront, in pixels
     216              :     /** Checks if the size changes, does nothing if no change.  Otherwise,  calls
     217              :      * \ref makeShiftPhase to pre-calculate the tilt arrays and plans the FFTs.
     218              :      *
     219              :      */
     220              :     void setWavefrontSizePixels( int wfsPix /**< [in] the desired new size of the wavefront */ );
     221              : 
     222              :   protected:
     223              :     template <int ccudaGPU = cudaGPU>
     224              :     void
     225              :     setShiftPhase( complexT *shiftFocal, complexT *shiftPupil, typename std::enable_if<ccudaGPU == 0>::type * = 0 );
     226              : 
     227              :     template <int ccudaGPU = cudaGPU>
     228              :     void
     229              :     setShiftPhase( complexT *shiftFocal, complexT *shiftPupil, typename std::enable_if<ccudaGPU == 1>::type * = 0 );
     230              : 
     231              :     /// Calculate the complex tilt arrays for centering and normalizing the wavefronts
     232              :     /**
     233              :      */
     234              :     void makeShiftPhase();
     235              : 
     236              : }; // class fraunhoferPropagator
     237              : 
     238              : template <typename wavefrontT, int cudaGPU>
     239            1 : fraunhoferPropagator<wavefrontT, cudaGPU>::fraunhoferPropagator()
     240              : {
     241            1 : }
     242              : 
     243              : template <typename wavefrontT, int cudaGPU>
     244            1 : fraunhoferPropagator<wavefrontT, cudaGPU>::~fraunhoferPropagator()
     245              : {
     246            1 : }
     247              : 
     248              : template <typename wavefrontT, int cudaGPU>
     249              : int fraunhoferPropagator<wavefrontT, cudaGPU>::wholePixel()
     250              : {
     251              :     return m_wholePixel;
     252              : }
     253              : 
     254              : template <typename wavefrontT, int cudaGPU>
     255            0 : void fraunhoferPropagator<wavefrontT, cudaGPU>::wholePixel( realT wp )
     256              : {
     257            0 :     m_wholePixel = wp;
     258              : 
     259              :     // Re-make the shift phase if size already set.
     260            0 :     if( m_wavefrontSizePixels > 0 )
     261              :     {
     262            0 :         makeShiftPhase();
     263              :     }
     264            0 : }
     265              : 
     266              : template <typename wavefrontT, int cudaGPU>
     267              : template <int ccudaGPU>
     268            1 : error_t fraunhoferPropagator<wavefrontT, cudaGPU>::shiftPupil( arrayT &complexPupil,
     269              :                                                                    typename std::enable_if<ccudaGPU == 0>::type * )
     270              : {
     271            1 :     complexPupil *= m_centerFocal;
     272              : 
     273            1 :     return error_t::noerror;
     274              : }
     275              : 
     276              : template <typename wavefrontT, int cudaGPU>
     277              : template <int ccudaGPU>
     278              : error_t fraunhoferPropagator<wavefrontT, cudaGPU>::shiftPupil( arrayT &complexPupil,
     279              :                                                                    typename std::enable_if<ccudaGPU == 1>::type * )
     280              : {
     281              :     BREAD_CRUMB;
     282              : #ifdef MXLIB_CUDA
     283              : 
     284              :     cudaError_t ce = mx::cuda::elementwiseXxY( reinterpret_cast<cuComplex *>( complexPupil.m_devicePtr ),
     285              :                                                reinterpret_cast<cuComplex *>( m_centerFocal.m_devicePtr ),
     286              :                                                pow( m_wavefrontSizePixels, 2 ) );
     287              : 
     288              :     if( ce != cudaSuccess )
     289              :     {
     290              :         return internal::mxlib_error_report<verboseT>( cudaError2error_t( ce ), "from mx::cuda::elementwiseXxY" );
     291              :     }
     292              : 
     293              : #endif
     294              : 
     295              :     BREAD_CRUMB;
     296              : 
     297              :     return error_t::noerror;
     298              : }
     299              : 
     300              : template <typename wavefrontT, int cudaGPU>
     301              : template <int ccudaGPU>
     302            1 : error_t fraunhoferPropagator<wavefrontT, cudaGPU>::unshiftPupil( arrayT &complexPupil,
     303              :                                                                  typename std::enable_if<ccudaGPU == 0>::type * )
     304              : {
     305            1 :     complexPupil *= m_centerPupil;
     306              : 
     307            1 :     return error_t::noerror;
     308              : }
     309              : 
     310              : template <typename wavefrontT, int cudaGPU>
     311              : template <int ccudaGPU>
     312              : error_t fraunhoferPropagator<wavefrontT, cudaGPU>::unshiftPupil( arrayT &complexPupil,
     313              :                                                                  typename std::enable_if<ccudaGPU == 1>::type * )
     314              : {
     315              : #ifdef MXLIB_CUDA
     316              :     cudaError_t ce = mx::cuda::elementwiseXxY( reinterpret_cast<cuComplex *>( complexPupil.m_devicePtr ),
     317              :                               reinterpret_cast<cuComplex *>( m_centerPupil.m_devicePtr ),
     318              :                               pow( m_wavefrontSizePixels, 2 ) );
     319              : 
     320              :     if( ce != cudaSuccess )
     321              :     {
     322              :         return internal::mxlib_error_report<verboseT>( cudaError2error_t( ce ), "from mx::cuda::elementwiseXxY" );
     323              :     }
     324              : #endif
     325              : 
     326              :     return error_t::noerror;
     327              : }
     328              : 
     329              : template <typename wavefrontT, int cudaGPU>
     330            1 : error_t fraunhoferPropagator<wavefrontT, cudaGPU>::propagatePupilToFocal( arrayT &complexFocal,
     331              :                                                                        arrayT &complexPupil,
     332              :                                                                        bool doCenter /*default = true*/
     333              : )
     334              : {
     335              : 
     336              :     BREAD_CRUMB;
     337              : 
     338              :     // First setup the tilt screens (does nothing if there's no change in size)
     339            1 :     setWavefrontSizePixels( complexPupil.rows() );
     340              : 
     341              :     BREAD_CRUMB;
     342              : 
     343              :     // Apply the centering shift -- this adjusts by 0.5 pixels and normalizes
     344            1 :     if( doCenter )
     345              :     {
     346            1 :         error_t ec = shiftPupil( complexPupil );
     347              : 
     348            1 :         if(ec != error_t::noerror)
     349              :         {
     350            0 :             return internal::mxlib_error_report<verboseT>(ec, "from shiftPupil");
     351              :         }
     352              :     }
     353              : 
     354              :     BREAD_CRUMB;
     355              : 
     356              :     // fft_fwd.fft(complexPupil.data(), complexFocal.data() );
     357            1 :     m_fft_fwd( complexFocal.data(), complexPupil.data() );
     358              : 
     359              :     BREAD_CRUMB;
     360              : 
     361            1 :     return error_t::noerror;
     362              : }
     363              : 
     364              : template <typename wavefrontT, int cudaGPU>
     365            1 : error_t fraunhoferPropagator<wavefrontT, cudaGPU>::propagateFocalToPupil( arrayT &complexPupil,
     366              :                                                                        arrayT &complexFocal,
     367              :                                                                        bool doCenter /*default = true*/
     368              : )
     369              : {
     370              :     BREAD_CRUMB;
     371              : 
     372              :     // First setup the tilt screens (does nothing if there's no change in size)
     373            1 :     setWavefrontSizePixels( complexPupil.rows() );
     374              : 
     375              :     BREAD_CRUMB;
     376              :     // fft_back.fft( complexFocal.data(), complexPupil.data());
     377            1 :     m_fft_back( complexPupil.data(), complexFocal.data() );
     378              : 
     379              :     BREAD_CRUMB;
     380              : 
     381              :     // Unshift the wavefront and normalize
     382            1 :     if( doCenter )
     383              :     {
     384            1 :         error_t ec = unshiftPupil( complexPupil );
     385              : 
     386            1 :         if(ec != error_t::noerror)
     387              :         {
     388            0 :             return internal::mxlib_error_report<verboseT>(ec, "from unshiftPupil");
     389              :         }
     390              : 
     391              :     }
     392              : 
     393              :     BREAD_CRUMB;
     394              : 
     395            1 :     return error_t::noerror;
     396              : }
     397              : 
     398              : template <typename wavefrontT, int cudaGPU>
     399            2 : void fraunhoferPropagator<wavefrontT, cudaGPU>::setWavefrontSizePixels( int wfsPix )
     400              : {
     401              :     BREAD_CRUMB;
     402              : 
     403              :     // If no change in size, do nothing
     404            2 :     if( wfsPix == m_centerFocal.rows() )
     405              :     {
     406            1 :         return;
     407              :     }
     408              : 
     409              :     BREAD_CRUMB;
     410              : 
     411            1 :     m_wavefrontSizePixels = wfsPix;
     412              : 
     413              :     BREAD_CRUMB;
     414              : 
     415            1 :     m_xcen = 0.5 * ( wfsPix - 1.0 );
     416            1 :     m_ycen = 0.5 * ( wfsPix - 1.0 );
     417              : 
     418              :     BREAD_CRUMB;
     419              : 
     420            1 :     makeShiftPhase();
     421              : 
     422              :     BREAD_CRUMB;
     423              : 
     424            1 :     m_fft_fwd.plan( wfsPix, wfsPix );
     425              : 
     426              :     BREAD_CRUMB;
     427              : 
     428            1 :     m_fft_back.plan( wfsPix, wfsPix, math::ft::dir::backward );
     429              : 
     430              :     BREAD_CRUMB;
     431              : }
     432              : 
     433              : template <typename wavefrontT, int cudaGPU>
     434              : template <int ccudaGPU>
     435            1 : void fraunhoferPropagator<wavefrontT, cudaGPU>::setShiftPhase( complexT *centerFocal,
     436              :                                                                complexT *centerPupil,
     437              :                                                                typename std::enable_if<ccudaGPU == 0>::type * )
     438              : {
     439            1 :     m_centerFocal.resize( m_wavefrontSizePixels, m_wavefrontSizePixels );
     440            1 :     m_centerPupil.resize( m_wavefrontSizePixels, m_wavefrontSizePixels );
     441              : 
     442              :     // Have to do this manually b/c "Eigen-like" might not support copy from a map
     443          513 :     for( int cc = 0; cc < m_wavefrontSizePixels; ++cc )
     444              :     {
     445       262656 :         for( int rr = 0; rr < m_wavefrontSizePixels; ++rr )
     446              :         {
     447       262144 :             m_centerFocal( rr, cc ) = centerFocal[cc * m_wavefrontSizePixels + rr];
     448       262144 :             m_centerPupil( rr, cc ) = centerPupil[cc * m_wavefrontSizePixels + rr];
     449              :         }
     450              :     }
     451            1 : }
     452              : 
     453              : template <typename wavefrontT, int cudaGPU>
     454              : template <int ccudaGPU>
     455              : void fraunhoferPropagator<wavefrontT, cudaGPU>::setShiftPhase( complexT *centerFocal,
     456              :                                                                complexT *centerPupil,
     457              :                                                                typename std::enable_if<ccudaGPU == 1>::type * )
     458              : {
     459              : #ifdef MXLIB_CUDA
     460              : 
     461              :     BREAD_CRUMB;
     462              : 
     463              :     m_centerFocal.upload( centerFocal, m_wavefrontSizePixels, m_wavefrontSizePixels );
     464              : 
     465              :     BREAD_CRUMB;
     466              : 
     467              :     m_centerPupil.upload( centerPupil, m_wavefrontSizePixels, m_wavefrontSizePixels );
     468              : 
     469              :     BREAD_CRUMB;
     470              : 
     471              : #endif
     472              : }
     473              : 
     474              : template <typename wavefrontT, int cudaGPU>
     475            1 : void fraunhoferPropagator<wavefrontT, cudaGPU>::makeShiftPhase()
     476              : {
     477            1 :     constexpr realT pi = math::pi<realT>();
     478              : 
     479              :     // The normalization is included in the tilt.
     480            1 :     realT norm = 1. / ( m_wavefrontSizePixels * sqrt( 2 ) );
     481            1 :     complexT cnorm = complexT( norm, norm );
     482              : 
     483              :     BREAD_CRUMB;
     484              : 
     485              :     /// Host memory to build the shift screens
     486       262145 :     complexT *centerFocal = new complexT[m_wavefrontSizePixels * m_wavefrontSizePixels];
     487              : 
     488       262145 :     complexT *centerPupil = new complexT[m_wavefrontSizePixels * m_wavefrontSizePixels];
     489              : 
     490              :     // Shift by 0.5 pixels
     491            1 :     realT arg = -2.0 * pi * 0.5 * ( m_wavefrontSizePixels - m_wholePixel ) / ( m_wavefrontSizePixels - 1 );
     492              : 
     493          513 :     for( int cc = 0; cc < m_wavefrontSizePixels; ++cc )
     494              :     {
     495       262656 :         for( int rr = 0; rr < m_wavefrontSizePixels; ++rr )
     496              :         {
     497       262144 :             centerFocal[cc * m_wavefrontSizePixels + rr] =
     498       262144 :                 cnorm * exp( complexT( 0., arg * ( ( rr - m_xcen ) + ( cc - m_ycen ) ) ) );
     499       262144 :             centerPupil[cc * m_wavefrontSizePixels + rr] =
     500       262144 :                 cnorm * exp( complexT( 0., 0.5 * pi - arg * ( ( rr - m_xcen ) + ( cc - m_ycen ) ) ) );
     501              :         }
     502              :     }
     503              : 
     504              :     BREAD_CRUMB;
     505              : 
     506            1 :     setShiftPhase( centerFocal, centerPupil );
     507              : 
     508              :     BREAD_CRUMB;
     509              : 
     510            1 :     delete[] centerFocal;
     511            1 :     delete[] centerPupil;
     512            1 : }
     513              : 
     514              : } // namespace wfp
     515              : } // namespace mx
     516              : 
     517              : #endif // wfp_fraunhoferPropagator_hpp
        

Generated by: LCOV version 2.0-1