"""
FresnelZoomXY2D — 2-D zoomed Fresnel propagator with independent zoom factors in x and y.
"""
import numpy
import scipy.constants as codata
from wofry.propagator.wavefront2D.generic_wavefront import GenericWavefront2D
from wofry.propagator.propagator import Propagator2D
[docs]
class FresnelZoomXY2D(Propagator2D):
HANDLER_NAME = "FRESNEL_ZOOM_XY_2D"
[docs]
def get_handler_name(self):
return self.HANDLER_NAME
[docs]
def do_specific_progation_after(self, wavefront, propagation_distance, parameters, element_index=None):
return self.do_specific_progation(wavefront, propagation_distance, parameters, element_index=element_index)
[docs]
def do_specific_progation_before(self, wavefront, propagation_distance, parameters, element_index=None):
return self.do_specific_progation( wavefront, propagation_distance, parameters, element_index=element_index)
[docs]
def do_specific_progation(self, wavefront1, propagation_distance, parameters, element_index=None):
"""
Propagate a 2-D wavefront using the zoomed Fresnel method with independent x/y magnifications.
Parameters
----------
wavefront1 : GenericWavefront2D
Input wavefront.
propagation_distance : float
Propagation distance [m].
parameters : PropagationParameters
Propagation parameter container (may include ``shift_half_pixel``,
``magnification_x``, ``magnification_y``).
element_index : int, optional
Index of the beamline element being propagated through.
Returns
-------
GenericWavefront2D
Propagated wavefront on the zoomed output grid.
"""
shift_half_pixel = self.get_additional_parameter("shift_half_pixel",False,parameters,element_index=element_index)
m_x = self.get_additional_parameter("magnification_x",1.0,parameters,element_index=element_index)
m_y = self.get_additional_parameter("magnification_y",1.0,parameters,element_index=element_index)
return self.propagate_wavefront(wavefront1,propagation_distance, magnification_x=m_x, magnification_y=m_y,shift_half_pixel=shift_half_pixel)
[docs]
@classmethod
def propagate_wavefront(cls,wavefront1,propagation_distance,magnification_x=1.0,magnification_y=1.0,shift_half_pixel=False):
wavefront = wavefront1.duplicate()
wavelength = wavefront.get_wavelength()
wavenumber = wavefront.get_wavenumber()
shape = wavefront.size()
delta = wavefront.delta()
# frequency for axis 1
pixelsize = delta[0]
npixels = shape[0]
freq_nyquist = 0.5 / pixelsize
freq_n = numpy.linspace(-1.0, 1.0, npixels)
freq_x0 = freq_n * freq_nyquist
freq_x1 = numpy.fft.fftfreq(npixels, pixelsize)
freq_x1 = numpy.fft.ifftshift(freq_x1)
# frequency for axis 2
pixelsize = delta[1]
npixels = shape[1]
freq_nyquist = 0.5 / pixelsize
freq_n = numpy.linspace(-1.0, 1.0, npixels)
freq_y0 = freq_n * freq_nyquist
freq_y1 = numpy.fft.fftfreq(npixels, pixelsize)
freq_y1 = numpy.fft.ifftshift(freq_y1)
# It happens that with method=0 (old) the propagation of a centro-symmetric beam
# is not longer center but shifted.
# This is due to "shifted" storage of the frequencies that is dependent on the
# even or odd number of pixels.
# It seems that with the new method (method=1) the beam is center.
# Note: The new method ignores the shift_half_pixel keyword
# See also doscussion with V. Favre-Nicolin email to srio on 2018-05-02
method = 1 # 0-old, 1-new
if method == 0:
freq_x = freq_x0
freq_y = freq_y0
if shift_half_pixel:
freq_x = freq_x - 0.5 * numpy.abs(freq_x[1] - freq_x[0])
freq_y = freq_y - 0.5 * numpy.abs(freq_y[1] - freq_y[0])
else:
freq_x = freq_x1
freq_y = freq_y1
f_x, f_y = numpy.meshgrid(freq_x, freq_y, indexing='ij')
fsq = numpy.fft.fftshift(f_x ** 2 / magnification_x + f_y ** 2 / magnification_y)
x = wavefront.get_mesh_x()
y = wavefront.get_mesh_y()
x_rescaling = wavefront.get_mesh_x() * magnification_x
y_rescaling = wavefront.get_mesh_y() * magnification_y
r1sq = x ** 2 * (1 - magnification_x) + y ** 2 * (1 - magnification_y)
r2sq = x_rescaling ** 2 * ((magnification_x - 1) / magnification_x) + y_rescaling ** 2 * ((magnification_y - 1) / magnification_y)
Q1 = wavenumber / 2 / propagation_distance * r1sq
Q2 = numpy.exp(-1.0j * numpy.pi * wavelength * propagation_distance * fsq)
Q3 = numpy.exp(1.0j * wavenumber / 2 / propagation_distance * r2sq)
wavefront.add_phase_shift(Q1)
fft = numpy.fft.fft2(wavefront.get_complex_amplitude())
ifft = numpy.fft.ifft2(fft * Q2) * Q3 / numpy.sqrt(magnification_x * magnification_y)
wf_propagated = GenericWavefront2D.initialize_wavefront_from_arrays(x_array=wavefront.get_coordinate_x()*magnification_x,
y_array=wavefront.get_coordinate_y()*magnification_y,
z_array=ifft,
wavelength=wavelength)
return wf_propagated