opticspy / lightpipes

LightPipes for Python, "Pure Python version"
https://opticspy.github.io/lightpipes/
BSD 3-Clause "New" or "Revised" License
227 stars 52 forks source link

Step by Step Propagation With a Short Focal Length Lens #64

Open IvanOstr opened 2 years ago

IvanOstr commented 2 years ago

Hey there again! This time i'm trying to propagate a gaussian beam through a f = 15cm lens, but at small increments - ideally by 250micron. In order to do it with Forvard (which by the way has less problems then Fresnel) without strange issues (seen in the image below) I need 10000 points in my grid and even more. I'll give here in example: This is how propagation looks with a N = 2000 grid as compared to N = 5000: image

Also, I'm quite sure the strange phenomenon we see is due to grid spacing and not boundry effects. The code for this is (1) below.

1) I've tried the solution with the spherical coordinates - although I didn't understand the physics of it - exactly as you did in the example in the website, but i'm not sure if I did correct considering I want a propagation with steps. Could you please elaborate the trick with the f1/f2 and give me a hint if that can work with steps? Code in (2) 2) The main problem with 10000 points is that it takes a lot of time for me to propagate 600 points from 0 to 15cm. Is there a way to use more computational power in the Lightpipes simulation? 3) Is there a way to get rid of the smaller ocillations occuring all around the beam?

Code for (1): from LightPipes import* import numpy as np import matplotlib.pyplot as plt import LightPipes import time

start_time = time.time()

wavelength = 800*nm
k = 2*np.pi/wavelength
size = 40*mm
N = 5000
R = 12*mm # beam radius
center = round(N/2)

F = Begin(size, wavelength, N)
F = GaussBeam(R, F)

yy, xx = F.mgrid_cartesian

########## propagation with steps ##############
f1 = 15*cm
fi = -k * (xx ** 2 + yy ** 2) / (2 * f1)
F_with_steps = LightPipes.Field.copy(F)
F_with_steps.field *= np.exp(1j * fi)

d = f1 + 2*cm
step_size = 5*cm
len = 0
while len <= d:
    len += step_size
    F_with_steps = Forvard(F_with_steps, step_size)

    I_with_steps = Intensity(F_with_steps, 0)[center, :]
    plt.figure()
    plt.title('Prop. Len: ' + str(len))
    plt.plot(I_with_steps)

time_took = time.time() - start_time

Code for (2): from LightPipes import* import numpy as np import matplotlib.pyplot as plt import matplotlib import LightPipes import time

start_time = time.time()

wavelength = 800*nm
k = 2*np.pi/wavelength
size = 40*mm
N = 2000
R = 12*mm # beam radius
center = round(N/2)

F = Begin(size, wavelength, N)
F = GaussBeam(R, F)

yy, xx = F.mgrid_cartesian

########## propagation with steps ##############
f1 = 10*m
f = 15*cm
f2 = f1*f/(f1-f)

len = 0
d = 5*cm
step_size = 1*cm
F_with_steps = LightPipes.Field.copy(F)
F_with_steps = Lens(F_with_steps, f1)
while len <= d:
    len += step_size

    F_with_steps = LensForvard(F_with_steps, f2, len)
    F_with_steps = Convert(F_with_steps)

    I_with_steps = Intensity(F_with_steps, 0)[center, :]
    plt.figure()
    plt.title('Prop Len' + str(len))
    plt.plot(I_with_steps)

time_took = time.time() - start_time

Thanks, Ivan

FredvanGoor commented 2 years ago

In general, and especially with lenses, you have to be careful not to violate the Nyquist criterium. The maximum spatial frequency of your problem (= max(derivative of the phase with respect to x and y coordinate) ) should be smaller than the maximum spatial frequency given by the grid-spacing (= N/2/size)

FELman2011 commented 2 years ago

When you propagate from a source plane (here optical field after the thin lens) to a target plane at various distances, it is best practice to preserve the source and repeatedly propagate from source plane to new target plane. This avoids buildup of errors during the propagation. For example, when you want to propagate the optical field Field at the source plane to target planes that are in the range z_start to z_end away from the source plane, you could use:

Nz=50                                    # number of steps in range z_start to z_end
z0 = z_start
dz = (z_end -z_start)/Nz

I1=np.zeros((Nz,GridDimension))
I2=np.zeros((Nz,GridDimension))

for i in range(Nz):
     F = Forvard(Field,z0)
     I = np.array(Intensity(F))
     I1[i,:] = I[int(GridDimension/2),:]
     I2[i,:] = I[:,int(GridDimension/2)]
     z0 += dz
IvanOstr commented 2 years ago

FELman2011: Unfortunately I'm simulating an effect that has to be propagated step by step (if you are more insterested - self foucing via kerr effect), so I can't run from this problem. In a matter of fact, the errors of propagation with steps and without them are quite small so my problem is mainly the inefficient running time.

FredvanGoor: Thanks, thats a good tip.