Esempio n. 1
0
def adaptive_sampling_angular_spectrum(field,k,distance,dx,wavelength):
    """
    A definition to calculate adaptive sampling angular spectrum based beam propagation. For more Zhang, Wenhui, Hao Zhang, and Guofan Jin. "Adaptive-sampling angular spectrum method with full utilization of space-bandwidth product." Optics Letters 45.16 (2020): 4416-4419.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    raise Exception("Adaptive sampling angular spectrum method is not yet stable. See issue https://github.com/kunguz/odak/issues/13")
    iflag = -1
    eps   = 10**(-12)
    nu,nv = field.shape
    l     = nu*dx
    x     = np.linspace(-l/2,l/2,nu)
    y     = np.linspace(-l/2,l/2,nv)
    X,Y   = np.meshgrid(x,y)
    fx    = np.linspace(-1./2./dx,1./2./dx,nu)
    fy    = np.linspace(-1./2./dx,1./2./dx,nv)
    FX,FY = np.meshgrid(fx,fy)
    forig = 1./2./dx
    fc2   = 1./2*(nu/wavelength/np.abs(distance))**0.5
    ss    = np.abs(fc2)/forig
    zc    = nu*dx**2/wavelength
    K     = nu/2/np.amax(np.abs(fx))
    if np.abs(distance) <= zc*2:
        nnu2  = nu
        nnv2  = nv
        fxn   = np.linspace(-1./2./dx,1./2./dx,nnu2)
        fyn   = np.linspace(-1./2./dx,1./2./dx,nnv2)
    else:
        nnu2  = nu
        nnv2  = nv
        fxn   = np.linspace(-fc2,fc2,nnu2)
        fyn   = np.linspace(-fc2,fc2,nnv2)
    FXN,FYN   = np.meshgrid(fxn,fxn)
    Hn        = np.exp(1j*k*distance*(1-(FXN*wavelength)**2-(FYN*wavelength)**2)**0.5)
    FX        = FX/np.amax(FX)*np.pi
    FY        = FY/np.amax(FY)*np.pi
    t_2       = nufft2(field,FX*ss,FY*ss,size=[nnu2,nnv2],sign=iflag,eps=eps)
    FX        = FXN/np.amax(FXN)*np.pi
    FY        = FYN/np.amax(FYN)*np.pi
    result    = nufft2(Hn*t_2,FX*ss,FY*ss,size=[nu,nv],sign=-iflag,eps=eps)
    return result
Esempio n. 2
0
def band_extended_angular_spectrum(field,k,distance,dx,wavelength):
    """
    A definition to calculate bandextended angular spectrum based beam propagation. For more Zhang, Wenhui, Hao Zhang, and Guofan Jin. "Band-extended angular spectrum method for accurate diffraction calculation in a wide propagation range." Optics Letters 45.6 (2020): 1543-1546.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    iflag = -1
    eps   = 10**(-12)
    nv,nu = field.shape
    l     = nu*dx
    x     = np.linspace(-l/2,l/2,nu)
    y     = np.linspace(-l/2,l/2,nv)
    X,Y   = np.meshgrid(x,y)
    Z     = X**2+Y**2
    fx    = np.linspace(-1./2./dx,1./2./dx,nu)
    fy    = np.linspace(-1./2./dx,1./2./dx,nv)
    FX,FY = np.meshgrid(fx,fy)
    K     = nu/2/np.amax(fx)
    fcn   = 1./2*(nu/wavelength/np.abs(distance))**0.5
    ss    = np.abs(fcn)/np.amax(np.abs(fx))
    zc    = nu*dx**2/wavelength
    if np.abs(distance) < zc:
        fxn = fx
        fyn = fy
    else:
        fxn = fx*ss
        fyn = fy*ss
    FXN,FYN   = np.meshgrid(fxn,fyn)
    Hn        = np.exp(1j*k*distance*(1-(FXN*wavelength)**2-(FYN*wavelength)**2)**0.5)
    X         = X/np.amax(X)*np.pi
    Y         = Y/np.amax(Y)*np.pi
    t_asmNUFT = nufft2(field,X*ss,Y*ss,sign=iflag,eps=eps)
    result    = nuifft2(Hn*t_asmNUFT,X*ss,Y*ss,sign=-iflag,eps=eps)
    return result
Esempio n. 3
0
def transfer_function_fresnel(field,k,distance,dx,wavelength):
    """
    A definition to calculate convolution based Fresnel approximation for beam propagation.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).

    """
    nv,nu  = field.shape
    fx     = np.linspace(-1./2./dx,1./2./dx,nu)
    fy     = np.linspace(-1./2./dx,1./2./dx,nv)
    FX,FY  = np.meshgrid(fx,fy)
    H      = np.exp(1j*k*distance*(1-(FX*wavelength)**2-(FY*wavelength)**2)**0.5)
    U1     = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(field)))
    U2     = H*U1
    result = np.fft.ifftshift(np.fft.ifft2(np.fft.ifftshift(U2)))
    return result
Esempio n. 4
0
def impulse_response_fresnel(field,k,distance,dx,wavelength):
    """
    A definition to calculate impulse response based Fresnel approximation for beam propagation.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).

    """
    nv,nu  = field.shape
    x      = np.linspace(-nu/2*dx,nu/2*dx,nu)
    y      = np.linspace(-nv/2*dx,nv/2*dx,nv)
    X,Y    = np.meshgrid(x,y)
    Z      = X**2+Y**2
    h      = np.exp(1j*k*distance)/(1j*wavelength*distance)*np.exp(1j*k/2/distance*Z)
    h      = np.fft.fft2(np.fft.fftshift(h))*dx**2
    U1     = np.fft.fft2(np.fft.fftshift(field))
    U2     = h*U1
    result = np.fft.ifftshift(np.fft.ifft2(U2))
    return result
Esempio n. 5
0
def fraunhofer_inverse(field,k,distance,dx,wavelength):
    """
    A definition to calculate Inverse Fraunhofer based beam propagation.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    distance = np.abs(distance)
    nv,nu    = field.shape
    l        = nu*dx
    l2       = wavelength*distance/dx
    dx2      = wavelength*distance/l
    fx       = np.linspace(-l2/2.,l2/2.,nu)
    fy       = np.linspace(-l2/2.,l2/2.,nv)
    FX,FY    = np.meshgrid(fx,fy)
    FZ       = FX**2+FY**2
    c        = np.exp(1j*k*distance)/(1j*wavelength*distance)*np.exp(1j*k/(2*distance)*FZ)
    result   = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift(field/dx**2/c )))
    return result
Esempio n. 6
0
def freeform(nx, ny, k, distances, dx=0.001):
    """
    A definition to generate a freeform field pattern from a depth map.

    Parameters
    ----------
    nx         : int
                 Size of the output along X.
    ny         : int
                 Size of the output along Y.
    k          : odak.wave.wavenumber
                 See odak.wave.wavenumber for more.
    distances  : ndarray
                 Depth map.
    dx         : float
                 Pixel pitch.

    Returns
    ---------
    field      : ndarray
                 Generated pattern.
    """
    size = [ny, nx]
    x = np.linspace(-size[0] * dx / 2, size[0] * dx / 2, size[0])
    y = np.linspace(-size[1] * dx / 2, size[1] * dx / 2, size[1])
    X, Y = np.meshgrid(x, y)
    Z = X**2 + Y**2
    field = np.exp(1j * k * 0.5 * np.sin(Z / distances))
    return field
Esempio n. 7
0
def prism_phase_function(nx, ny, k, angle, dx=0.001, axis='x'):
    """
    A definition to generate 2D phase function that represents a prism. See Goodman's Introduction to Fourier Optics book for more.

    Parameters
    ----------
    nx         : int
                 Size of the output along X.
    ny         : int
                 Size of the output along Y.
    k          : odak.wave.wavenumber
                 See odak.wave.wavenumber for more.
    angle      : float
                 Tilt angle of the prism in degrees.
    dx         : float
                 Pixel pitch.
    axis       : str
                 Axis of the prism.

    Returns
    ----------
    prism      : ndarray
                 Generated phase function for a prism.
    """
    angle = np.radians(angle)
    size = [ny, nx]
    x = np.linspace(-size[0] * dx / 2, size[0] * dx / 2, size[0])
    y = np.linspace(-size[1] * dx / 2, size[1] * dx / 2, size[1])
    X, Y = np.meshgrid(x, y)
    if axis == 'y':
        prism = np.exp(-1j * k * np.sin(angle) * Y)
    elif axis == 'x':
        prism = np.exp(-1j * k * np.sin(angle) * X)
    return prism
Esempio n. 8
0
def quadratic_phase_function(nx, ny, k, focal=0.4, dx=0.001):
    """ 
    A definition to generate 2D quadratic phase function, which is typically use to represent lenses.

    Parameters
    ----------
    nx         : int
                 Size of the output along X.
    ny         : int
                 Size of the output along Y.
    k          : odak.wave.wavenumber
                 See odak.wave.wavenumber for more.
    focal      : float
                 Focal length of the quadratic phase function.
    dx         : float
                 Pixel pitch.

    Returns
    ---------
    function   : ndarray
                 Generated quadratic phase function.
    """
    size = [ny, nx]
    x = np.linspace(-size[0] * dx / 2, size[0] * dx / 2, size[0])
    y = np.linspace(-size[1] * dx / 2, size[1] * dx / 2, size[1])
    X, Y = np.meshgrid(x, y)
    Z = X**2 + Y**2
    qwf = np.exp(1j * k * 0.5 * np.sin(Z / focal))
    return qwf
Esempio n. 9
0
def angular_spectrum(field,k,distance,dx,wavelength):
    """
    A definition to calculate angular spectrum based beam propagation.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    nu,nv  = field.shape
    x      = np.linspace(-nu/2*dx,nu/2*dx,nu)
    y      = np.linspace(-nv/2*dx,nv/2*dx,nv)
    X,Y    = np.meshgrid(x,y)
    Z      = X**2+Y**2
    h      = 1./(1j*wavelength*distance)*np.exp(1j*k*(distance+Z/2/distance))
    h      = np.fft.fft2(np.fft.fftshift(h))*dx**2
    U1     = np.fft.fft2(np.fft.fftshift(field))
    U2     = h*U1
    result = np.fft.ifftshift(np.fft.ifft2(U2))
    return result
Esempio n. 10
0
def band_limited_angular_spectrum(field,k,distance,dx,wavelength):
    """
    A definition to calculate bandlimited angular spectrum based beam propagation. For more Matsushima, Kyoji, and Tomoyoshi Shimobaba. "Band-limited angular spectrum method for numerical simulation of free-space propagation in far and near fields." Optics express 17.22 (2009): 19662-19673.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    nu,nv  = field.shape
    l      = nu*dx
    l2     = wavelength*distance/dx
    dx2    = wavelength*distance/l
    fx     = np.linspace(-l2/2.,l2/2.,nu)
    fy     = np.linspace(-l2/2.,l2/2.,nv)
    FX,FY  = np.meshgrid(fx,fy)
    FZ     = FX**2+FY**2
    c      = 1./(1j*wavelength*distance)*np.exp(1j*k*(2./distance)*FZ)
    c      = np.exp(1j*k*distance)/(1j*wavelength*distance)*np.exp(1j*k/(2*distance)*FZ)
    result = c*np.fft.ifftshift(np.fft.fft2(np.fft.fftshift(field)))*dx**2
    return result
Esempio n. 11
0
def propagate_beam(field,
                   k,
                   distance,
                   dx,
                   wavelength,
                   propagation_type='IR Fresnel'):
    """
    Definitions for Fresnel impulse respone (IR), Fresnel Transfer Function (TF), Fraunhofer diffraction in accordence with "Computational Fourier Optics" by David Vuelz.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.
    propagation_type : str
                       Type of the propagation (IR Fresnel, TR Fresnel, Fraunhofer).

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    nu, nv = field.shape
    x = np.linspace(-nv * dx, nv * dx, nv)
    y = np.linspace(-nu * dx, nu * dx, nu)
    X, Y = np.meshgrid(x, y)
    Z = X**2 + Y**2
    if propagation_type == 'IR Fresnel':
        h = 1. / (1j * wavelength * distance) * np.exp(
            1j * k * 0.5 / distance * Z)
        h = np.fft.fft2(np.fft.fftshift(h)) * pow(dx, 2)
        U1 = np.fft.fft2(np.fft.fftshift(field))
        U2 = h * U1
        result = np.fft.ifftshift(np.fft.ifft2(U2))
    elif propagation_type == 'TR Fresnel':
        h = np.exp(1j * k * distance) * np.exp(
            -1j * np.pi * wavelength * distance * Z)
        h = np.fft.fftshift(h)
        U1 = np.fft.fft2(np.fft.fftshift(field))
        U2 = h * U1
        result = np.fft.ifftshift(np.fft.ifft2(U2))
    elif propagation_type == 'Fraunhofer':
        c = 1. / (1j * wavelength * distance) * np.exp(
            1j * k * 0.5 / distance * Z)
        result = c * np.fft.ifftshift(np.fft.fft2(
            np.fft.fftshift(field))) * pow(dx, 2)
    return result
Esempio n. 12
0
def plane_tilt(nx, ny, k, focals, dx=0.001, axis='x'):
    """
    A definition to tilt a complex field.

    Parameters
    ----------
    nx         : int
                 Size of the output along X.
    ny         : int
                 Size of the output along Y.
    k          : odak.wave.wavenumber
                 See odak.wave.wavenumber for more.
    focals     : list
                 Focus ranges, two for X and two for Y, in total four numbers. Make sure to pass nonzero numbers.
    dx         : float
                 Pixel pitch.
    axis       : str
                 Which state to tilt, x, y or xy.

    Returns
    ----------
    field      : ndarray
                 Field to tilt a plane.
    """
    size = [ny, nx]
    x = np.linspace(-size[0] * dx / 2, size[0] * dx / 2, size[0])
    y = np.linspace(-size[1] * dx / 2, size[1] * dx / 2, size[1])
    X, Y = np.meshgrid(x, y)
    Z = X**2 + Y**2
    if np.all((focals == 0)):
        raise Exception("Focals must be non zero.")
    focal_x = np.geomspace(focals[0], focals[1], size[0])
    focal_y = np.geomspace(focals[2], focals[3], size[1])
    FX, FY = np.meshgrid(focal_x, focal_y)
    field = np.ones((nx, ny), dtype=np.complex64)
    if axis == 'x' or axis == 'xy':
        field *= np.exp(1j * k * 0.5 * np.sin(Z / FX))
    if axis == 'y' or axis == 'xy':
        field *= np.exp(1j * k * 0.5 * np.sin(Z / FY))
    return field
Esempio n. 13
0
def band_limited_angular_spectrum(field,k,distance,dx,wavelength):
    """
    A definition to calculate bandlimited angular spectrum based beam propagation. For more Matsushima, Kyoji, and Tomoyoshi Shimobaba. "Band-limited angular spectrum method for numerical simulation of free-space propagation in far and near fields." Optics express 17.22 (2009): 19662-19673.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    nv,nu  = field.shape
    x      = np.linspace(-nu/2*dx,nu/2*dx,nu)
    y      = np.linspace(-nv/2*dx,nv/2*dx,nv)
    X,Y    = np.meshgrid(x,y)
    Z      = X**2+Y**2
    h      = 1./(1j*wavelength*distance)*np.exp(1j*k*(distance+Z/2/distance))
    h      = np.fft.fft2(np.fft.fftshift(h))*dx**2
    flimx  = np.ceil(1/(((2*distance*(1./(nu)))**2+1)**0.5*wavelength))
    flimy  = np.ceil(1/(((2*distance*(1./(nv)))**2+1)**0.5*wavelength))
    mask   = np.zeros((nu,nv),dtype=np.complex64)
    mask   = (np.abs(X)<flimx) & (np.abs(Y)<flimy)
    mask   = set_amplitude(h,mask)
    U1     = np.fft.fft2(np.fft.fftshift(field))
    U2     = mask*U1
    result = np.fft.ifftshift(np.fft.ifft2(U2))
    return result
Esempio n. 14
0
def rayleigh_sommerfeld(field,k,distance,dx,wavelength):
    """
    Definition to compute beam propagation using Rayleigh-Sommerfeld's diffraction formula (Huygens-Fresnel Principle). For more see Section 3.5.2 in Goodman, Joseph W. Introduction to Fourier optics. Roberts and Company Publishers, 2005.

    Parameters
    ----------
    field            : np.complex
                       Complex field (MxN).
    k                : odak.wave.wavenumber
                       Wave number of a wave, see odak.wave.wavenumber for more.
    distance         : float
                       Propagation distance.
    dx               : float
                       Size of one single pixel in the field grid (in meters).
    wavelength       : float
                       Wavelength of the electric field.

    Returns
    =======
    result           : np.complex
                       Final complex field (MxN).
    """
    nv,nu     = field.shape
    x         = np.linspace(-nv*dx/2,nv*dx/2,nv)
    y         = np.linspace(-nu*dx/2,nu*dx/2,nu)
    X,Y       = np.meshgrid(x,y)
    Z         = X**2+Y**2
    result    = np.zeros(field.shape,dtype=np.complex64)
    direction = int(distance/np.abs(distance))
    for i in range(nu):
        for j in range(nv):
            if field[i,j] != 0:
                r01      = np.sqrt(distance**2+(X-X[i,j])**2+(Y-Y[i,j])**2)*direction
                cosnr01  = np.cos(distance/r01)
                result  += field[i,j]*np.exp(1j*k*r01)/r01*cosnr01
    result *= 1./(1j*wavelength)
    return result