0

I am trying to write a code which propagates a beam a certain distance through Fourier propagation. Here is the relevant excerpt of the code:

import numpy as np
import scipy
from matplotlib import pyplot as plt
from numpy.fft import fft, ifft, fft2, ifft2, fftfreq, fftshift, ifftshift

exp=np.exp
pi=np.pi
sqrt=np.sqrt
tg=np.tan
arctg=np.arctan2
angle=np.angle
array=np.array
plt.rcParams["figure.autolayout"] = True

class beam:
  def __init__(self, z_0: float, compOnda: float):
    self.z_0 = z_0 #distância de Rayleigh
    self.compOnda = compOnda #comprimento de onda

    self.k = 2*pi / self.compOnda #número de onda
    self.W_0 = sqrt(self.compOnda * self.z_0 / pi)
    self.teta_0 = self.W_0 / self.z_0
    self.A_0 = sqrt(2 / (pi * self.W_0)) #amplitude em 0; definimos ela de forma a normalizar o pulso (gaussiano - preciso verificar se normaliza o laguerre gaussiano também)
    self.I_0 = self.A_0 * self.A_0.conjugate() #intensidade em 0

  def W(self,z: np.array):
    return self.W_0 * sqrt(1 + (z / self.z_0)**2)

  def R(self,z: np.array):
    z_=np.where(z==0.0,1e-20,z) #evitando divisão por zero
    R=z_ * (1 + (self.z_0 / z_)**2)
    R=np.where(z==0.0,np.inf,R)
    return R

  def zeta(self,z: np.array):
    return arctg(z, self.z_0)

class GaussianBeam(beam):
  def __init__(self, z_0: float, compOnda: float):
    super().__init__(z_0,compOnda)

  def Amplitude(self,z: array = 0.0, rho: array = 0.0, phi: array = 0.0) -> array:
    z=np.asarray(z,dtype=float) 

    z_0=self.z_0
    A_0=self.A_0
    k=self.k
    W_0=self.W_0

    W=self.W(z)
    R=self.R(z)
    zeta=self.zeta(z)

    U=A_0*(W_0/W)*exp(-rho**2/W**2 + (-k*z-k*(rho**2)/(2*R)+zeta)*1.0j)
    return U

wavelength=800*(10**-9) #nm
z_0=pi*(10**(-3))/2 #m ->approx. 0.0015. W_0=20.10⁻6
dz=0.006 #propagation distance (m)

def Propag(U,dz,gr,d):
  #frequency coordinates
  fx = fftfreq(gr, d=d)
  fy = fftfreq(gr, d=d)
  fx, fy = np.meshgrid(fx, fy)
  kx = 2*pi*fx
  ky = 2*pi*fy
  kz = sqrt(k**2 - kx**2 - ky**2 + 0j) #propagation constant in z

  #radial complex amplitude
  Fourier_U = fft2(U) #angular spectrum
  Propag_FU = Fourier_U * exp(1j * kz * dz)
  U_propag = ifft2(Propag_FU)
  return U_propag

fx=GaussianBeam(z_0,wavelength)
W_0=fx.W_0
gr=1000
k=fx.k
x = np.linspace(-4*W_0,4*W_0,gr) #grid size
y = np.linspace(-4*W_0,4*W_0,gr)
d=8*W_0/(gr-1)
X,Y = np.meshgrid(x,y)
rho=sqrt(X**2+Y**2)
phi=arctg(Y,X)
U=fx.Amplitude(z_0,rho,phi) #this is a bidimensional Gaussian

U_propag=Propag(U,dz,gr,d)

def plot(u):
  modulo = abs(u)
  fase = angle(u)

  # Gráfico
  plt.figure(figsize=(12, 5))

  plt.subplot(1, 2, 1)
  plt.pcolor(X, Y, modulo, shading='auto', cmap='viridis')
  plt.title('Módulo')
  plt.xlabel('x')
  plt.ylabel('y')
  plt.colorbar()

  plt.subplot(1, 2, 2)
  plt.pcolor(X, Y, fase, shading='auto', cmap='twilight')
  plt.title('Fase')
  plt.xlabel('x')
  plt.ylabel('y')
  plt.colorbar()

  plt.tight_layout()
  plt.show()
  
plot(U_propag)

The code works, but it propagates the beam backwards - which can be seen by the shrunkage of the profile when dz=z_0, meaning that the beam is propagating towards its waist. I want to fix this and make the beam propagate forward. I read that the Fourier transform in optics has a positive sign, but swapping fft and ifft produced the same results. I then tried to give exp(1j * kz * dz) a negative sign, but that gave me an error:

/tmp/ipython-input-17-2768534464.py:14: RuntimeWarning: overflow encountered in exp
  Propag_FU = Fourier_U * exp(-1j * kz * dz)
/usr/local/lib/python3.11/dist-packages/numpy/fft/_pocketfft.py:94: RuntimeWarning: invalid value encountered in fft
  return ufunc(a, fct, axes=[(axis,), (), (axis,)], out=out)

Why is my beam being propagated backwards, and how can I propagate it forward?

7
  • 2
    What is k in the formula for kz? I don't see a definition for that. And what is z_0? If it's the initial position for the calculation, where do you expect the beam waist to be with respect to z_0? What if you increase dz, does the beam continue to expand with distance the way you'd expect? Commented Jul 1 at 6:29
  • 1
    Please show complete code. Your error message contains a very important sign difference from your actual code as presented. If kz had a positive imaginary part then the code in your error message would blow up. We need to know how big k is compared to kx and ky. Commented Jul 1 at 7:23
  • added full code, including definitions for k and z_0. @PaulCornelius yes, once dz is greater than 2*z_0 the beam starts expanding exactly as I'd expect. Commented Jul 1 at 13:57
  • @lastchance the error occurs when I change exp(1j * kz * dz) to exp(-1j * kz * dz). Otherwise no error occurs except for the beam propagating backwards. Commented Jul 1 at 13:58
  • 1
    Can you explain how the output of your code is showing whatever problem you have? Commented Jul 1 at 19:46

1 Answer 1

0

If you want to take the complex conjugate of a numpy array, the function .conj() will do that. If I remember my Gaussian optics correctly (a big if, at this point), that should change the direction of the beam.

If I use .conj() instead of changing 1j to -1j, the calculation works without error. The beam appears to be propagating away from its waist, but I didn't spend a whole lot of time on this. I think you have more of a physics problem here than a programming problem.

In other words, replace this line:

Propag_FU = Fourier_U * exp(1j * kz * dz)

with:

Propag_FU = Fourier_U * exp(1j * kz * dz).conj()

Sorting out the various factors of 1j, -1j, and which version of the fft to use here is a bit outside my current pay grade.

Sign up to request clarification or add additional context in comments.

1 Comment

It worked, thanks! I think the 'why' question is a bit beyond the scope of this forum, so I will mark your answer as the solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.