structured-optics 0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: structured_optics
3
+ Version: 0.2
4
+ Summary: Package for Structured Light simulation
5
+ Author: Altilano C. Barbosa
6
+ Requires-Python: >=3.9
7
+ Requires-Dist: numpy
8
+ Requires-Dist: scipy
9
+ Requires-Dist: PyQt6
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "structured_optics"
7
+ version = "0.2"
8
+ description = "Package for Structured Light simulation"
9
+ requires-python = ">=3.9"
10
+ authors = [
11
+ { name = "Altilano C. Barbosa" }
12
+ ]
13
+
14
+ dependencies = [
15
+ "numpy",
16
+ "scipy",
17
+ "PyQt6"
18
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name='structured_optics',
5
+ version='0.2',
6
+ packages=find_packages()
7
+
8
+ )
@@ -0,0 +1,3 @@
1
+ from .struct_opt import *
2
+
3
+ __all__ = struct_opt.__all__ if "core" in globals() else []
@@ -0,0 +1,90 @@
1
+ from structured_optics.utils import overlap
2
+ import numpy as np
3
+
4
+
5
+
6
+ def hg_proj(Beam,N, completeness = False):
7
+ overlaps = np.zeros((N+1, N+1), dtype='complex')
8
+ auxiliary_beam = Beam.copy_clean()
9
+ for n in range(N+1):
10
+ for m in range(N+1):
11
+ auxiliary_beam.hg(n,m)
12
+ overlaps[n,m] = overlap(Beam, auxiliary_beam)
13
+ n, m = np.meshgrid(np.arange(0, N+1, 1), np.arange(0, N+1, 1))
14
+ comp = np.sum(np.abs(overlaps)**2)
15
+ if completeness == True:
16
+ return n, m, overlaps, comp
17
+ else:
18
+ return n, m, overlaps
19
+
20
+
21
+ def lg_proj(Beam, N, completeness = False):
22
+ overlaps = np.zeros((int(2*N)+1, int(N/2)+1), dtype='complex')
23
+ auxiliary_beam = Beam.copy_clean()
24
+ for l in np.arange(-N, N+1, 1):
25
+ for p in range(int(N/2)+1):
26
+ auxiliary_beam.lg(l,p)
27
+ overlaps[l+N,p] = overlap(Beam, auxiliary_beam)
28
+ p, l = np.meshgrid(np.arange(0, int(N/2)+1, 1), np.arange(-N, N+1, 1))
29
+ comp = np.sum(np.abs(overlaps)**2)
30
+ if completeness == True:
31
+ return l, p, overlaps, comp
32
+ else:
33
+ return l, p, overlaps
34
+
35
+ def hg_basis(Beam, N, waist = None, norm1 = False):
36
+ aux = Beam.copy_clean()
37
+ if waist != None:
38
+ aux.waist = waist
39
+ basis = np.empty((N+1, N+1, Beam.Dy, Beam.Dx), dtype = 'complex')
40
+ for n in range(N+1):
41
+ for m in range(N+1):
42
+ aux.hg(n, m)
43
+ basis[n, m] = aux.field
44
+ if norm1 == True:
45
+ basis = basis/np.max(basis)
46
+ return basis
47
+
48
+ def lg_basis(Beam, N, waist=None, norm1=False):
49
+ aux = Beam.copy_clean()
50
+ if waist != None:
51
+ aux.waist = waist
52
+ basis = np.empty((N+1, N+1, Beam.Dy, Beam.Dx), dtype = 'complex')
53
+ for n in range(N+1):
54
+ for m in range(N+1):
55
+ l = m-n
56
+ p = min(n,m)
57
+ aux.lg(l, p)
58
+ basis[n, m] = aux.field
59
+ if norm1 == True:
60
+ basis = basis/np.max(basis)
61
+ return basis
62
+
63
+ def bessel_basis(Beam, Nmax, waist=None, norm1=False):
64
+ aux = Beam.copy_clean()
65
+ if waist is not None:
66
+ aux.waist = waist
67
+ # basis[n] = Bessel beam of order n
68
+ basis = np.empty((2*Nmax+1, aux.Dy, aux.Dx), dtype='complex')
69
+ for n in range(2*Nmax+1):
70
+ aux.bessel(n-Nmax) # or aux.bessel(n, theta=...)
71
+ basis[n] = aux.field
72
+ if norm1:
73
+ basis = basis / np.max(np.abs(basis))
74
+ return basis
75
+
76
+ def build_from_coefs_and_basis(Beam, coefs, basis):
77
+ N = len(coefs)
78
+ aux = Beam.copy_clean()
79
+ for i in range(N):
80
+ for j in range(N):
81
+ aux.field = aux.field + coefs[i,j] * basis[i,j]
82
+ Beam.field = aux.field
83
+ return Beam
84
+
85
+ def astigmatic_mode_converter(Beam, N, theta):
86
+ n, m, overlaps = Beam.hg_projector(N)
87
+ converter = np.exp(1j*(m-n)*theta)
88
+ overlaps = overlaps*converter
89
+ Beam.build_from_coefs_and_basis(overlaps, Beam.hg_basis(N))
90
+ return Beam
@@ -0,0 +1,70 @@
1
+ from structured_optics.struct_opt import *
2
+ import numpy as np
3
+ from structured_optics.utils import *
4
+ import time
5
+
6
+
7
+ def slm_hologram(Beam, x_grating, y_grating, method, input_beam, eps, max_range):
8
+ #generate a hologram for a slm
9
+ if Beam.pol_dim != 1:
10
+ print('Beam must be scalar (pol_dim=1) to generate holograms')
11
+ return None
12
+
13
+ dx = (Beam.x[0,1] - Beam.x[0,0])
14
+ dy = (Beam.y[1,0] - Beam.y[0,0])
15
+ if input_beam is None:
16
+ input_field = np.exp(-((Beam.x**2 + Beam.y**2)/(2*np.max(Beam.x))**2))
17
+ else:
18
+ input_field = input_beam.field
19
+ desired = Beam.field/np.max(np.abs(Beam.field))
20
+ lamb_x = dx*x_grating
21
+ lamb_y = dy*y_grating
22
+ Ain = np.abs(input_field)
23
+ Ain = Ain / Ain.max()
24
+ Ades = np.abs(desired)
25
+ s = np.min(Ain / (Ades + eps))
26
+ Ades = s * Ades
27
+ A_rel = Ades / Ain
28
+ phi_g = np.mod(2*np.pi*(Beam.x/lamb_x + Beam.y/lamb_y), 2*np.pi)
29
+ phi_relg = np.angle(desired) - np.angle(input_field) + phi_g
30
+ if method == 'simple':
31
+ H = A_rel*phi_relg
32
+ elif method == 'g_holo':
33
+ edg = desired*np.exp(1j*phi_g)
34
+ N = np.min(np.abs(input_field)/(np.abs(desired) + 1e-9))
35
+ H = np.angle(N*edg + input_field)
36
+ elif method == 'davis':
37
+ H = (1-inv_sinc(A_rel)/np.pi)*phi_relg
38
+ elif method == 'bolduc':
39
+ M = 1+inv_sinc(A_rel)/np.pi
40
+ H = M*(phi_relg - np.pi*M)
41
+ elif method == 'bessel0':
42
+ H = phi_relg + inv_J0(A_rel)*np.sin(phi_relg)
43
+ elif method == 'bessel1':
44
+ H = inv_J1(A_rel)*np.sin(phi_relg)
45
+ else:
46
+ print('Error! method must be simple, g_holo, davis, bolduc, bessel0 or bessel1.')
47
+ H = max_range * (H - H.min()) / (H.max() - H.min())
48
+ return H.astype(np.uint8)
49
+
50
+ def dmd_hologram(Beam, cx, cy, sign):
51
+ #generates a hologram for a dmd
52
+ if Beam.pol_dim != 1:
53
+ print('Beam must be scalar (pol_dim=1) to generate holograms')
54
+ return None
55
+ U = np.abs(Beam.field)
56
+ U = U/np.amax(U)
57
+ phi = Beam.phase()
58
+ A = np.arcsin(U)
59
+
60
+ fx = cx*Beam.Dx/(2*Beam.nix)
61
+ fy = cy*Beam.Dy/(2*Beam.niy)
62
+ g = [fx, fy]
63
+ G = g[0]*Beam.x + g[1]*Beam.y
64
+
65
+ H = 0.5 + sign*0.5*np.sign(np.cos(phi + 2*np.pi*G) - np.cos(A))
66
+ return H
67
+
68
+
69
+
70
+
@@ -0,0 +1,393 @@
1
+ from structured_optics.utils import *
2
+ import numpy as np
3
+ from scipy import special
4
+
5
+
6
+ #modes implementations
7
+
8
+ def hg_mode(Beam, N, M, z, pol_index=None):
9
+ #get a HG mode at distance z of order N+M
10
+ zr = Beam.zr()
11
+ q0 = 1j*zr
12
+ q = -z + 1j*zr
13
+ k = 2*np.pi/Beam.lamb
14
+ w = Beam.waist*np.sqrt(1 + (z/zr)**2)
15
+ Cn = np.sqrt(np.sqrt(2/np.pi)*q0/(2**N * special.factorial(N) * q * Beam.waist))
16
+ un = Cn*(-np.conjugate(q)/q)**(N/2)*hermite(np.sqrt(2)*(Beam.x-Beam.x0)/w, N)*np.exp(-1j*k*(Beam.x-Beam.x0)**2/(2*q))
17
+ Cm = np.sqrt(np.sqrt(2/np.pi)*q0/(2**M * special.factorial(M) * q * Beam.waist))
18
+ um = Cm*(-np.conjugate(q)/q)**(M/2)*hermite(np.sqrt(2)*(Beam.y-Beam.y0)/w, M)*np.exp(-1j*k*(Beam.y-Beam.y0)**2/(2*q))
19
+ F = un*um*np.exp(1j*k*z)
20
+ if pol_index == None:
21
+ if Beam.pol_dim == 1:
22
+ Beam.field = F
23
+ elif Beam.pol_dim == 2:
24
+ Beam.field = np.array([F, F])/np.sqrt(2)
25
+ elif Beam.pol_dim == 3:
26
+ Beam.field = np.array([F, F, F])/np.sqrt(3)
27
+ else:
28
+ Beam.field[pol_index] = F
29
+ return Beam
30
+
31
+ def hg_astigmatic_mode(Beam, N, M, wx, wy, z=0, pol_index=None):
32
+ #get a astigmatic HG mode at distance z of order N+M
33
+ zrx = np.pi*wx**2/Beam.lamb
34
+ q0x = 1j*zrx
35
+ qx = -z + q0x
36
+ k = 2*np.pi/Beam.lamb
37
+ wxz = wx*np.sqrt(1 + (z/zrx)**2)
38
+ Cn = np.sqrt(np.sqrt(2/np.pi)*q0x/(2**N * special.factorial(N) * qx * wx))
39
+ un = Cn*(-np.conjugate(qx)/qx)**(N/2)*hermite(np.sqrt(2)*(Beam.x-Beam.x0)/wxz, N)*np.exp(-1j*k*(Beam.x-Beam.x0)**2/(2*qx))
40
+
41
+ zry = np.pi*wy**2/Beam.lamb
42
+ q0y = 1j*zry
43
+ qy = -z + q0y
44
+ wyz = wy*np.sqrt(1 + (z/zry)**2)
45
+ Cm = np.sqrt(np.sqrt(2/np.pi)*q0y/(2**M * special.factorial(M) * qy * wy))
46
+ um = Cm*(-np.conjugate(qy)/qy)**(M/2)*hermite(np.sqrt(2)*(Beam.y-Beam.y0)/wyz, M)*np.exp(-1j*k*(Beam.y-Beam.y0)**2/(2*qy))
47
+ F = un*um*np.exp(1j*k*z)
48
+
49
+ if pol_index == None:
50
+ if Beam.pol_dim == 1:
51
+ Beam.field = F
52
+ elif Beam.pol_dim == 2:
53
+ Beam.field = np.array([F, F])/np.sqrt(2)
54
+ elif Beam.pol_dim == 3:
55
+ Beam.field = np.array([F, F, F])/np.sqrt(3)
56
+ else:
57
+ Beam.field[pol_index] = F
58
+ return Beam
59
+
60
+
61
+ def lg_mode(Beam,l,p, z, pol_index=None):
62
+ #get a LG mode at distance z of order abs(N) + 2M
63
+ zr = Beam.zr()
64
+ k = 2*np.pi/Beam.lamb
65
+ w = Beam.waist*np.sqrt(1 + (z/zr)**2)
66
+ R = (z**2 + zr**2)
67
+ gouy = (np.abs(l) + 2*p + 1)*np.arctan(z/zr)
68
+ r = np.sqrt((Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2)
69
+ Cte = np.sqrt(2*special.factorial(p) / (np.pi*special.factorial(p+np.abs(l))))
70
+ F = Cte*(1/w)*(np.sqrt(2)*r/w)**(np.abs(l)) * np.exp(-(r/w)**2) * laguerre(2*(r/w)**2, np.abs(l), p) *\
71
+ np.exp(1j*(k*z + k*z*(r**2)/(2*R) + l*np.arctan2(Beam.y-Beam.y0,Beam.x-Beam.x0) - gouy))
72
+ if pol_index == None:
73
+ if Beam.pol_dim == 1:
74
+ Beam.field = F
75
+ elif Beam.pol_dim == 2:
76
+ Beam.field = np.array([F, F])/np.sqrt(2)
77
+ elif Beam.pol_dim == 3:
78
+ Beam.field = np.array([F, F, F])/np.sqrt(3)
79
+ else:
80
+ Beam.field[pol_index] = F
81
+ return Beam
82
+
83
+ def bessel_mode(Beam, N, z, pol_index=None):
84
+ #get a Bessel mode of order N at distance z
85
+ k = 2*np.pi / Beam.lamb # wavenumber
86
+ j01 = special.jn_zeros(abs(N), 1)[0] # first zero of J_N
87
+ # enforce the first zero at r = Beam.waist
88
+ kr = j01 / Beam.waist
89
+ kz = np.sqrt(k**2 - kr**2)
90
+ r = np.sqrt((Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2)
91
+ phi = np.arctan2(Beam.y-Beam.y0, Beam.x-Beam.x0)
92
+ F = (np.exp(1j*kz*z) * special.jv(abs(N), kr*r) * np.exp(-1j*N*phi))
93
+ if pol_index == None:
94
+ if Beam.pol_dim == 1:
95
+ Beam.field = F
96
+ elif Beam.pol_dim == 2:
97
+ Beam.field = np.array([F, F])
98
+ elif Beam.pol_dim == 3:
99
+ Beam.field = np.array([F, F, F])
100
+ else:
101
+ Beam.field[pol_index] = F
102
+ Beam.norm_beam()
103
+ return Beam
104
+
105
+ def gbessel_mode(Beam, N, r0, pol_index):
106
+ #get a gaussian bessel beam of order N at z=0
107
+ rad = 2*np.pi*Beam.waist**2/r0
108
+ r = np.sqrt((Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2)
109
+ F = special.jv(np.abs(N),rad*r/Beam.waist**2)*\
110
+ np.exp(-1j*N*np.arctan2(Beam.y-Beam.y0, Beam.x-Beam.x0))*np.exp(-(r/Beam.waist)**2)
111
+ if pol_index == None:
112
+ if Beam.pol_dim == 1:
113
+ Beam.field = F
114
+ elif Beam.pol_dim == 2:
115
+ Beam.field = np.array([F, F])
116
+ elif Beam.pol_dim == 3:
117
+ Beam.field = np.array([F, F, F])
118
+ else:
119
+ Beam.field[pol_index] = F
120
+ Beam.norm_beam()
121
+ return Beam
122
+
123
+ def lg_prod_mode(Beam, N, ls, centers, pol_index):
124
+ if centers is None:
125
+ centers = np.zeros((N, 2))
126
+ centers = np.asarray(centers)
127
+ if ls is None:
128
+ ls = np.ones(N, dtype=int)
129
+ ls = np.asarray(ls)
130
+
131
+ F = np.ones((Beam.Dy, Beam.Dx), dtype='complex128')
132
+ for i in range(N):
133
+ x0 = centers[i, 0]
134
+ y0 = centers[i, 1]
135
+ l = ls[i]
136
+ sign = -1 if l < 0 else 1
137
+ exp_pow = int(np.abs(l))
138
+ arg = (Beam.x - x0) + 1j * sign * (Beam.y - y0)
139
+ r2 = (Beam.x - x0)**2 + (Beam.y - y0)**2
140
+ lg = arg**exp_pow * np.exp(-r2/N / ((Beam.waist)**2))
141
+ F *= lg
142
+ if pol_index == None:
143
+ if Beam.pol_dim == 1:
144
+ Beam.field = F
145
+ elif Beam.pol_dim == 2:
146
+ Beam.field = np.array([F, F])
147
+ elif Beam.pol_dim == 3:
148
+ Beam.field = np.array([F, F, F])
149
+ else:
150
+ Beam.field[pol_index] = F
151
+ Beam.norm_beam()
152
+ return Beam
153
+
154
+ def frac_oam_mode(Beam, Ma, n_modes, beta, theta_0, z, pol_index):
155
+ #get a fractional OAM beam, with OAM Ma (!= integer), by the method of LG supperpositions.
156
+ mu = Ma%1
157
+ m = Ma-mu
158
+ if mu >=0.5:
159
+ n_min = np.ceil(Ma - n_modes / 2)
160
+ else:
161
+ n_min = np.floor(Ma - n_modes / 2)
162
+ n_max = n_min + n_modes - 1
163
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex128')
164
+ for l in np.arange(int(n_min), int(n_max)+1):
165
+ coef = np.exp(-1j*mu*beta)*1j*np.exp(1j*(Ma-l)*theta_0)/(2*np.pi*(Ma-l))*np.exp(1j*(m-l)*beta)*(1-np.exp(1j*mu*2*np.pi))
166
+ if Beam.pol_dim == 1:
167
+ F += coef*(Beam.lg(l, 0, z = z).field)
168
+ else:
169
+ F += coef*(Beam.lg(l, 0, z = z, pol_index=0).field[0])
170
+ if pol_index == None:
171
+ if Beam.pol_dim == 1:
172
+ Beam.field = F
173
+ elif Beam.pol_dim == 2:
174
+ Beam.field = np.array([F, F])
175
+ elif Beam.pol_dim == 3:
176
+ Beam.field = np.array([F, F, F])
177
+ else:
178
+ Beam.field[pol_index] = F
179
+ Beam.norm_beam()
180
+ return Beam
181
+
182
+ def frac_oam_qs_mode(Beam, Ma, n_modes, beta, theta_0, z, pol_index):
183
+ #get a quasi-stable fractional oam mode.
184
+ n_min = np.round(Ma-n_modes/2)
185
+ n_max = n_min + n_modes - 1
186
+ mu = Ma%1
187
+ m = Ma-mu
188
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex128')
189
+ for l in np.arange(int(n_min), int(n_max)+1):
190
+ coef = np.exp(-1j*mu*beta)*1j*np.exp(1j*(Ma-l)*theta_0)/(2*np.pi*(Ma-l))*np.exp(1j*(m-l)*beta)*(1-np.exp(1j*mu*2*np.pi))
191
+ p = np.floor((np.abs(Ma) + n_modes/2 -np.abs(l))/2)
192
+ if Beam.pol_dim == 1:
193
+ F += coef*(Beam.lg(l, p, z = z).field)
194
+ else:
195
+ F += coef*(Beam.lg(l, p, z = z, pol_index=0).field[0])
196
+ if pol_index == None:
197
+ if Beam.pol_dim == 1:
198
+ Beam.field = F
199
+ elif Beam.pol_dim == 2:
200
+ Beam.field = np.array([F, F])
201
+ elif Beam.pol_dim == 3:
202
+ Beam.field = np.array([F, F, F])
203
+ else:
204
+ Beam.field[pol_index] = F
205
+ Beam.norm_beam()
206
+ return Beam
207
+
208
+
209
+
210
+ def IG_even_mode(Beam, p, m, q, z, pol_index):
211
+ #Even Ince-Gaussian mode IG_p,m^e(x,y)
212
+
213
+ xi, eta = cartesian_to_elliptic(Beam.x-Beam.x0, Beam.y-Beam.y0, q, Beam.waist, z, Beam.lamb)
214
+
215
+ Ce_xi = C_ince(1j*xi, p, m, q)
216
+ Ce_eta = C_ince(eta, p, m, q)
217
+
218
+ zr = np.pi * Beam.waist**2 / Beam.lamb
219
+ k = 2*np.pi/Beam.lamb
220
+ wz = Beam.waist * np.sqrt(1 + (z * Beam.lamb / (np.pi * Beam.waist**2))**2)
221
+ Rz = (z**2 + zr**2)
222
+ r2 = (Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2
223
+ gouy = (p+1)*np.arctan(z / zr)
224
+ F = Ce_xi * Ce_eta * np.exp(-r2 / (wz**2))*np.exp(1j*(k*z + k*z*(r2)/(2*Rz) - gouy))
225
+ if pol_index == None:
226
+ if Beam.pol_dim == 1:
227
+ Beam.field = F
228
+ elif Beam.pol_dim == 2:
229
+ Beam.field = np.array([F, F])
230
+ elif Beam.pol_dim == 3:
231
+ Beam.field = np.array([F, F, F])
232
+ else:
233
+ Beam.field[pol_index] = F
234
+ Beam.norm_beam()
235
+ return Beam
236
+
237
+ def IG_odd_mode(Beam, p, m, q, z, pol_index):
238
+ #Odd Ince-Gaussian mode IG_p,m^o(x,y)
239
+
240
+ xi, eta = cartesian_to_elliptic(Beam.x-Beam.x0, Beam.y-Beam.y0, q, Beam.waist, z, Beam.lamb)
241
+ So_xi = S_ince(1j*xi, p, m, q)
242
+ So_eta = S_ince(eta, p, m, q)
243
+
244
+ zr = np.pi * Beam.waist**2 / Beam.lamb
245
+ k = 2*np.pi/Beam.lamb
246
+ wz = Beam.waist * np.sqrt(1 + (z * Beam.lamb / (np.pi * Beam.waist**2))**2)
247
+ Rz = (z**2 + zr**2)
248
+ r2 = (Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2
249
+ gouy = (p+1)*np.arctan(z / zr)
250
+ F = So_xi * So_eta * np.exp(-r2 / (wz**2))*np.exp(1j*(k*z + k*z*(r2)/(2*Rz) - gouy))
251
+ if pol_index == None:
252
+ if Beam.pol_dim == 1:
253
+ Beam.field = F
254
+ elif Beam.pol_dim == 2:
255
+ Beam.field = np.array([F, F])
256
+ elif Beam.pol_dim == 3:
257
+ Beam.field = np.array([F, F, F])
258
+ else:
259
+ Beam.field[pol_index] = F
260
+ Beam.norm_beam()
261
+ return Beam
262
+
263
+ def HInceG_mode(Beam, p, m, q, z, helicity, pol_index):
264
+ #Hermite-Ince-Gaussian mode HIG_p,m^e(x,y), combining even and odd Ince-Gaussian modes with given helicity.
265
+ #For some reason IG_odd has a 3*pi/2 phase shift wrt IG_even, so the helicity sign is inverted here.
266
+ xi, eta = cartesian_to_elliptic(Beam.x-Beam.x0, Beam.y-Beam.y0, q, Beam.waist, z, Beam.lamb)
267
+
268
+ Ce_xi = C_ince(1j*xi, p, m, q)
269
+ Ce_eta = C_ince(eta, p, m, q)
270
+
271
+ So_xi = S_ince(1j*xi, p, m, q)
272
+ So_eta = S_ince(eta, p, m, q)
273
+
274
+ zr = np.pi * Beam.waist**2 / Beam.lamb
275
+ k = 2*np.pi/Beam.lamb
276
+ wz = Beam.waist * np.sqrt(1 + (z * Beam.lamb / (np.pi * Beam.waist**2))**2)
277
+ Rz = (z**2 + zr**2)
278
+ r2 = (Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2
279
+ gouy = (p+1)*np.arctan(z / zr)
280
+ F = (Ce_xi * Ce_eta - helicity*So_xi * So_eta) * np.exp(-r2 / (wz**2))*np.exp(1j*(k*z + k*z*(r2)/(2*Rz) - gouy))
281
+ if pol_index == None:
282
+ if Beam.pol_dim == 1:
283
+ Beam.field = F
284
+ elif Beam.pol_dim == 2:
285
+ Beam.field = np.array([F, F])
286
+ elif Beam.pol_dim == 3:
287
+ Beam.field = np.array([F, F, F])
288
+ else:
289
+ Beam.field[pol_index] = F
290
+ Beam.norm_beam()
291
+ return Beam
292
+
293
+ def circle_mode(Beam, center, radius, pol_index):
294
+ if radius == None:
295
+ radius = Beam.waist
296
+ #Create a circular mode
297
+ center_x, center_y = center
298
+ # Calculate distance from center for all points
299
+ distances = np.sqrt((Beam.x - center_x)**2 + (Beam.y - center_y)**2)
300
+ # Set points within radius to 1
301
+ mask = distances <= radius
302
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex')
303
+ F[mask] = 1
304
+ if pol_index == None:
305
+ if Beam.pol_dim == 1:
306
+ Beam.field = F
307
+ elif Beam.pol_dim == 2:
308
+ Beam.field = np.array([F, F])
309
+ elif Beam.pol_dim == 3:
310
+ Beam.field = np.array([F, F, F])
311
+ else:
312
+ Beam.field[pol_index] = F
313
+ Beam.norm_beam()
314
+ return Beam
315
+
316
+
317
+ def square_mode(Beam, center, side_length, pol_index):
318
+ if side_length == None:
319
+ side_length = Beam.waist
320
+ center_x, center_y = center
321
+
322
+ # Calculate square boundaries
323
+ half_side = side_length / 2.0
324
+
325
+ # Create mask for points inside square
326
+ mask = (np.abs(Beam.x - center_x) <= half_side) & (np.abs(Beam.y - center_y) <= half_side)
327
+
328
+ # Set points inside square to 1
329
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex')
330
+ F[mask] = 1
331
+ if pol_index == None:
332
+ if Beam.pol_dim == 1:
333
+ Beam.field = F
334
+ elif Beam.pol_dim == 2:
335
+ Beam.field = np.array([F, F])
336
+ elif Beam.pol_dim == 3:
337
+ Beam.field = np.array([F, F, F])
338
+ else:
339
+ Beam.field[pol_index] = F
340
+ Beam.norm_beam()
341
+ return Beam
342
+
343
+
344
+ def triangle_mode(Beam, center, side_length, pol_index):
345
+ if side_length == None:
346
+ side_length = Beam.waist
347
+ center_x, center_y = center
348
+
349
+ # Height of equilateral triangle
350
+ height = side_length * np.sqrt(3) / 2
351
+
352
+ # Vertices for upward-pointing equilateral triangle
353
+ # Top vertex
354
+ v0_x = center_x
355
+ v0_y = center_y + height * 2/3
356
+
357
+ # Bottom left vertex
358
+ v1_x = center_x - side_length / 2
359
+ v1_y = center_y - height * 1/3
360
+
361
+ # Bottom right vertex
362
+ v2_x = center_x + side_length / 2
363
+ v2_y = center_y - height * 1/3
364
+
365
+ # Use barycentric coordinates to determine if point is inside triangle
366
+ def sign(px, py, v1_x, v1_y, v2_x, v2_y):
367
+ return (px - v2_x) * (v1_y - v2_y) - (v1_x - v2_x) * (py - v2_y)
368
+
369
+ # Compute signs for all points
370
+ d1 = sign(Beam.x, Beam.y, v0_x, v0_y, v1_x, v1_y)
371
+ d2 = sign(Beam.x, Beam.y, v1_x, v1_y, v2_x, v2_y)
372
+ d3 = sign(Beam.x, Beam.y, v2_x, v2_y, v0_x, v0_y)
373
+
374
+ # Point is inside if all signs are same
375
+ has_neg = (d1 < 0) | (d2 < 0) | (d3 < 0)
376
+ has_pos = (d1 > 0) | (d2 > 0) | (d3 > 0)
377
+
378
+ mask = ~(has_neg & has_pos)
379
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex')
380
+ F[mask] = 1
381
+ if pol_index == None:
382
+ if Beam.pol_dim == 1:
383
+ Beam.field = F
384
+ elif Beam.pol_dim == 2:
385
+ Beam.field = np.array([F, F])
386
+ elif Beam.pol_dim == 3:
387
+ Beam.field = np.array([F, F, F])
388
+ else:
389
+ Beam.field[pol_index] = F
390
+ Beam.norm_beam()
391
+ return Beam
392
+
393
+
@@ -0,0 +1,46 @@
1
+ import numpy as np
2
+ from scipy import fft
3
+
4
+
5
+ def propagate_fresnel(Beam, z):
6
+ #Propagate the field by a distance z using fresnel integral. convolution method
7
+ k = 2*np.pi/Beam.lamb
8
+ prop = np.exp(-1j * z * (Beam.kx ** 2 + Beam.ky ** 2) / (2 * k) + 1j*k*z)
9
+ Beam.fourier_field = fft.fft2(Beam.field)
10
+ Beam.field = fft.ifft2(prop*Beam.fourier_field)
11
+ return Beam
12
+
13
+ def propagate_incoherent(Beam, z):
14
+ #Propagates the INTENSITY by a distance z using incoherent propagation. Loses phase information.
15
+ #without pupil function doesn't seems to work
16
+ k = 2*np.pi/Beam.lamb
17
+ if z != 0:
18
+ h2 = np.ones_like(Beam.field)/(Beam.lamb*z)**2
19
+ else:
20
+ h2 = np.ones_like(Beam.field)
21
+ h2_fourier = fft.fft2(h2)
22
+ I_fourier = fft.fft2(Beam.int_profile())
23
+ Beam.field = np.sqrt(fft.ifft2(h2_fourier*I_fourier))
24
+ return Beam
25
+
26
+
27
+ def propagate_fraunhofer(Beam, z):
28
+ #Propagate the field by a distance z using fraunhofer integral. Changes XY grid.
29
+
30
+ if z < 2/Beam.lamb*Beam.waist**2:
31
+ print("z small. Fraunhofer is probably not a good aproximation.")
32
+ if z != 0:
33
+ k = 2*np.pi/Beam.lamb
34
+ dx = Beam.x[0,1] - Beam.x[0,0]
35
+ dy = Beam.y[1,0] - Beam.y[0,0]
36
+ fx = np.fft.fftshift(np.fft.fftfreq(Beam.Dx, d=dx))
37
+ fy = np.fft.fftshift(np.fft.fftfreq(Beam.Dy, d=dy))
38
+ FX, FY = np.meshgrid(fx, fy, indexing="xy")
39
+ Beam.x = Beam.lamb * z * FX
40
+ Beam.y = Beam.lamb * z * FY
41
+ Beam.nix = np.max(Beam.x)
42
+ Beam.niy = np.max(Beam.y)
43
+ C = np.exp(1j*k*z)/(1j*Beam.lamb*z)*np.exp(1j*k*(Beam.x**2 + Beam.y**2)/(2*z))
44
+ Beam.fourier_field = fft.fft2(Beam.field)
45
+ Beam.field = C*fft.fftshift(Beam.fourier_field)*dx*dy
46
+ return Beam