Open IvanOstr opened 3 years ago
Dear Ivan,
I think you are on the right track. I'll try to comment each point:
Fresnel
/Forvard
seems fine. For wavelength=632.8nm
and w0=0.3mm
the Rayleigh range is z_R=446.81mm
according to an online Gauss beam calculator, so for your 30cm-200cm propagation indeed becomes significant.Steps
result indeed looks weird after a longer propagation of ~1m. However I believe this is due to boundary effects, so this should be solveable by using a larger grid size (requiring more grid points if you want to keep the resolution, see comment below).Lens
should work as expected. In general I recommend reading the manual and playing around with the examples to learn about the pitfalls of the calculations (e.g. edge effects or too hard focusing/ too short focal length compared to beam size)GaussBeam
, GaussHermite
and GaussLaguerre
commands should be sufficient for most cases, as they provide the correct formula at beam waist (z=0
). So for e.g. z=1mm
, you can just use a propagation with Fresnel
instead of using the analytical formula. Nevertheless, if you think it could be useful, please feel free to play around and submit a Pull Request if you're happy with your implementation.I have added some printouts to your code and a final plot showing the weird intensity distribution after the final Steps
propagation.
Fresnel
and Steps
, confirming the expected Rayleigh range.Step
shows some "square" behaviour, always an indicator you might be running into edge effectssize=8*mm
and N to N=800
while keeping the beam size fixed, you can see the much better result after the final Step
.Let us know if this helps. All the best, Lenny
from LightPipes import *
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
wavelength = 632.8*nm
size = 2*mm
N = 200
N2 = int(N/2)
R = 0.3*mm # if R means w0 -> at 633nm, z_Rayleigh is 446.67mm ~ 44.7cm
dz = 10*mm
f = 15*cm
n = (1.0 + 0.1j)*np.ones((N,N))
Z_len = 200
Icross = np.zeros((Z_len,N))
X = range(N)
Z = range(Z_len)
X, Z = np.meshgrid(X, Z)
F = Begin(size, wavelength, N)
F = GaussBeam(R, F)
############### Forvad / Fresnel ################
I=Intensity(F, 0)
print('Imax at z=0.0cm', I.max())
plt.figure()
plt.imshow(I)
plt.show()
# raise Exception('eof')
#F=Lens(F,f)
zs = np.linspace(0, 44.7, 10)
for z in zs:
F_propogated = Fresnel(F, z*cm)
# F_propogated = Forvard(F, z*cm)
I=Intensity(F_propogated, 0)
print(f'Imax at z={z:.1f}cm (Fresnel)', I.max())
plt.figure()
plt.plot(Intensity(F_propogated, 0)[N2, :])
# plt.imshow(I)
plt.show()
############### Steps ################
z_steps = 0.0
for i in range(0, Z_len):
F=Steps(F, dz, 1, n)
z_steps += dz
I=Intensity(F, 0)
print(f'Imax at z={z_steps/cm:.1f}cm (Steps)', I.max())
Icross[i][:N] = I[N2][:N]
# plt.figure();plt.plot(Icross[i][:N]);plt.show()
fig = plt.figure()
plt.imshow(I)
plt.title('Intensity after final Steps() call')
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Z,
Icross,
rstride=1,
cstride=1,
cmap='rainbow',
linewidth=0.0,
)
plt.axis('off')
plt.title('Lineout of intensity on axis along Steps()')
plt.show()
Dear Lenny, Thank you very much for the comprehensive response!
I'll update about successful propagation through lens with the same setup in the few days, if you don't close the issue by then.
I have found out some of the effects you mentioned regarding too short or too hard focusing.
1) A strange behavior starts when the beam parameters are w0 = 10mm, wavelength = 800nm, focal length = 150cm, grid_dim = 1000 and continuous along all the propagation length. For example:
This is taken from the code below.
The situation gets worse with shorter wavelengths. However, I found out that increasing the grid dimension can help that a lot. What is the reason for that? I tried to investigate it and found out that by making the lens smaller - the strange effects seem to vanish more. For example, by manually setting the phase to be the same phase as before but only on a smaller square on the middle.
Suppose I want to simulate a real life experiment with focal length = 15 cm, what would you recommend doing?
2) It seems that Steps does not agree with Fresnel when I change focal length to 250:
The left picture is Fresnel and the right one is the same length in z, but with the Steps function.
I guess it is a drawback of the method somehow? Increasing the grid size didn't work so well here.
The steps code I used (written straight after the forvard code) is attached below.
3) Suppose I try to simulate a really small lens - say, 10 to 100 times smaller than the gaussian beam. Do you know of any limitations of the simulations when trying to simulate diffraction and refraction from really small objects? My method is currently to cut a small square of phase and apply it on the field.
Code for (2):
Z_len = 30# mm
Icross = np.zeros((Z_len, grid_dimension))
X = range(grid_dimension)
Z = range(Z_len)
X, Z = np.meshgrid(X, Z)
dz = 100*mm
n = (1.0 + 0j)*np.ones((grid_dimension, grid_dimension))
for i in range(0, Z_len):
print(i)
F=Steps(F, dz, 1, n)
I=Intensity(F, 0)
Icross[i][:grid_dimension] = I[N2][:grid_dimension]
# plt.figure();plt.plot(Icross[i][:N]);plt.show()
plt.figure();plt.plot(x_arr,Icross[25][:grid_dimension]);plt.show()
Code for (1):
#Focus of a lens.
from LightPipes import*
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
def gaussian(x, a, b, c):
return a*np.exp(- (( (x-b)/c )**2) / 2)
wavelength = 800 * nm
k = 2 * np.pi / wavelength
grid_dimension = 1000
N2 = int(grid_dimension / 2)
waist_in_mm = 10 # waist at z = 0 in mm
W = waist_in_mm * mm
size = W * 5 # mm
x_arr = np.linspace(0, waist_in_mm*5, grid_dimension) # mm
F = Begin(size, wavelength, grid_dimension)
F = GaussBeam(W, F)
focal_length = 150
f = focal_length * cm
F = Lens(F, f)
############### Forvad / Fresnel ################
I = Intensity(F, 0)
plt.figure()
plt.imshow(I)
plt.title('Intial Distribution')
plt.show()
zs = np.linspace(0, focal_length, 5)
for z in zs:
F_propogated = Fresnel(F, z*cm)
#F_propogated = Forvard(F, z*cm)
I=Intensity(F_propogated, 0)
plt.figure()
plt.title('Propagation After ' + str(z) +'cm')
plt.plot(x_arr, I[N2, :])
# p0 = (1, 25, 1)
# popt, pcov = curve_fit(gaussian, x_arr, I[N2, :], p0)
# plt.plot(x_arr, gaussian(x_arr, *popt), 'r-')
# plt.title('Propagation after ' + str(z) +'cm with waist = ' + str(2*popt[2]))
# plt.imshow(I)
plt.show()
Dear Ivan, I finally found the time to test your updated examples (by the way thanks for supplying a fully working code/ MWE, it helps a lot to investigate).
PhaseUnwrap
or Phase(F, unwrap=True)
take a while unfortunately). If you observe the wrapped phase (unwrap=False
) you will notice the step from one grid point to the next will be delta_phi<< 2pi
close to the center, but rapidly varying at the edges. Using unwrap=True
the routine will try to "guess" the correct offset of 2pi
to make the phase look smoother. This starts to fail at the edges (the square shape is an artefact of the unwrap routine).
-> In my experience, once the phase delta to neighbouring grid points becomes larger than 2pi
(in regions where the field has non-negligible intensity), things start to get weird. In your case, Forvard
seems to cope a little better than Fresnel
(f=250cm vs. f=550cm before weird effects show up, resp.). However try both, in other cases it might be the other way around.
As you can see, you have very few grid points to sample the focus, so changes in the shape are hard to interpret. Very roughly, the focus spot size will be r0 = lambda*f/D
. For your lens with f=150cm
, D=2cm
(est. from Gauss waist) and lambda=0.8um
the focus spot radius will be ~60um
, but your grid spacing is 50mm/1000=50um
. To solve this and the above question about the real life experiment I recommend the LightPipes commands LensFresnel
/LensForvard
. Check the documentation for a simple example, I can elaborate on the f1/f2
trick used if necessary (I have been meaning to improve the documentation here anyway at some point).
https://opticspy.github.io/lightpipes/manual.html#spherical-coordinates
This effectively allows you to have a different grid spacing in the focus than at the lens. It also allows to use much shorter focal lengths compared to the beam diameter, however I have never investigated what the new limit is yet (I have joined LightPipes later in the game, the original implementation seems exactly as old as I am...)
Note that Steps
only works in normal coordinates, not spherical ones.
I'm not sure I follow your last question. Concretely, how large is the beam, the lens, and the focal length? If the beam is much larger than the lens, what do you do with the parts that lie outside, do you apply an aperture? Very generally, the underlying physics/maths use various methods to solve the diffractions integrals on a grid, so your grid spacing must be chosen to sufficiently account for the objects size/shape and potential phase shifts (see above). If you want to be sure the result makes sense you should start with a finer spacing and then decrease the sampling. If the result does not change much, you are fine. I would start by resolving your tiny objects by at least 10 grid points, e.g. a circular aperture should not be a cross of 3x3pixels but rather a smooth circle with 10x10pixels/gridpoints outer diameter.
Code for (1):
from LightPipes import *
import numpy as np
import matplotlib.pyplot as plt
wavelength = 800 * nm
grid_dimension = 1000
N2 = int(grid_dimension / 2)
waist_in_mm = 10 # waist at z = 0 in mm
W = waist_in_mm * mm
size = W * 5 # mm
x_arr = np.linspace(0, waist_in_mm*5, grid_dimension) # mm
F = Begin(size, wavelength, grid_dimension)
F = GaussBeam(W, F)
# focal_length = 200 # effects start to show in Forvard
# focal_length = 250 # no effects in Forvard
# focal_length = 450 # effects start to show in Fresnel
focal_length = 550 # (almost) no effects in Fresnel
f = focal_length * cm
F = Lens(F, f)
############### Forvard / Fresnel ################
I = Intensity(F, 0)
do_unwrap = False
Ph = Phase(F, unwrap=do_unwrap)
PhLineout = Ph[N2, :]
fig, [axl, axm, axr] = plt.subplots(1, 3)
axl.imshow(I)
axm.imshow(Ph)
if do_unwrap:
axr.plot(PhLineout)
else:
axr.plot(PhLineout, 'x-') #crosses make jumps more visible
axl.set_title('Intensity at z=0')
axm.set_title('Phase at z=0')
axr.set_title('Phase lineout at z=0')
plt.show()
zs = np.linspace(0, focal_length, 5)
for z in zs:
F_propogated = Fresnel(F, z*cm)
# F_propogated = Forvard(F, z*cm)
I=Intensity(F_propogated, 0)
plt.figure()
plt.title('Propagation After ' + str(z) +'cm')
plt.plot(x_arr, I[N2, :])
plt.show()
I agree that support for a general Gaussian beam would be useful. Should not be difficult to implement: just add parameters and compute the right intensity/phase functions. In the very least different waist sizes should be possible.
I propose new commands for propagating Gauss beams using analytical ABCD theory: user defined ABCD matrix, free space propagation and a lens command. I have add them to a new branch "develop_Fred" in this repo. I called them GaussABCD, GaussForvard and GaussLens, but I think that another name is better: maybe GBForvard and GBlens or GForvard and GLens. Comments are welcome! Please look at the test scripts in this branch.
Dear Leonard: Thank you very much for the answer again. I hope the questions I ask are not too much :)
Dear Fredvan: Good idea! The problem i'll have with that is that the aperture (lens) I use may be much smaller then the beam, but the functions will be really helpful otherwise.
Dear Leonard,
Greetings..
Currently I am trying to realize a 4f-system, with LightPipes, and seem to have run into similar issues as discussed in this thread.
The modified code: (While running this piece I encounter the following error, which seems to be related to the values one assigns to f1 "LensFresnel: Behind focus")
from LightPipes import *
import numpy as np
import matplotlib.pyplot as plt
#(1) BASICS
N = 3001
labda=800*nm
size=50*cm
u=100*cm #Object Distance from the Lens
v=100*cm #Image Distance from the Lens
f=50*cm #Focal Length
f1=10*m # The tricks..
f2=f1*f/(f1-f)
frac=f/f1
newsize=frac*size
w=6*cm
#(2) OPTICAL SYSTEM
F=Begin(size,labda,N);
F=CircAperture(w,0,0,F);
ph_in=Phase(F);ph_in=PhaseUnwrap(ph_in)
I_in=Intensity(F,0)
F=Fresnel(u,F) #Propogate Object to Lens
F=Lens(f1,0,0,F) #ApplyLens
F=LensFresnel(f2,v,F)
F=Convert(F)
ph_out=Phase(F);ph_out=PhaseUnwrap(ph_out)
I_out=Intensity(F,0)
x=[]
for i in range(N):
x.append((-newsize/2+i*newsize/N)/cm)
#(3) THE PLOTS
fig = plt.figure(figsize=(20,18))
ax1 = plt.subplot(311)
ax2 = plt.subplot(312)
ax1.plot(x,ph_in[int(N/2)],'r--',label='At Object')
ax1.plot(x,ph_out[int(N/2)],'b',label='At Image');
#ax1.set_xlim(-newsize/2/cm,newsize/2/cm)
#ax1.set_ylim(-2,4)
ax1.set_xlabel('x [cm]');
ax1.set_ylabel('phase [rad]');
ax1.set_title('Phase')
legend = ax1.legend(loc='upper center', shadow=True)
ax2.plot(x,I_in[int(N/2)],'r--',label='At Object')
ax2.plot(x,I_out[int(N/2)], 'b',label='At Image');
#ax2.set_xlim(-newsize/2/cm,newsize/2/cm)
#ax2.set_ylim(0,1000)
ax2.set_xlabel('x [cm]');
ax2.set_ylabel('Intensity [a.u.]');
ax2.set_title('Intensity')
legend = ax2.legend(loc='upper center', shadow=True)
plt.show()
Could you also comment upon the limitations with these methods (gridsize, propagation distances, etc..) in relation to the parameters in my task; my beam width is large (~12cm), and I am placing an obstacle ~_1cm_wide along its path. The task is to magnify the object by 10x or 100x (which is also large).
Am I correct in thinking it would be a mistake to not leave sufficient padding (of zeros) between my object and the simulation boundaries (i.e. size) , hence I probably cannot magnify arbitrarily.
Lastly, is there a simpler way to work this if I can ignore the distribution focal plane (after the lens).
I am new to LightPipes, and hope that it will be possible to find some guidance here :)
Regards -Hari
Hey there, 1) I'm trying to propagate a gaussian beam in free space using the code below. I've tried two method - once with Forvard/Fresnel and once with Steps. In both cases the gaussian beam expands, unlike in a laser system where the gaussian beam stays almost constant because of collimation and a really large Rayleigh length. I want to simulate the case with a really large gaussian beam, and I did not find in your code the full gaussian beam as in Saleh or other references:
Should I implement it by myself and put it in the field matrix?
Or does the beam expansion is just a result of the FFT approach?
2) I think the both methods does not agree completely. What are the reasons, and is there a guide line when to use each? for example, in steps we see multiple peaks at some large z:![image](https://user-images.githubusercontent.com/39938309/106487275-2b1e4c80-64bb-11eb-94b9-75775a5c5524.png)
3) When I propagate through lens with this methods, I'm afraid i'm not getting the right results as of the reason stated in 1.
Thanks in advance, Ivan
The code: