structured-optics 0.2__tar.gz → 0.3__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.
Files changed (21) hide show
  1. {structured_optics-0.2 → structured_optics-0.3}/PKG-INFO +1 -1
  2. {structured_optics-0.2 → structured_optics-0.3}/pyproject.toml +1 -1
  3. {structured_optics-0.2 → structured_optics-0.3}/setup.py +1 -1
  4. structured_optics-0.3/structured_optics/__init__.py +4 -0
  5. {structured_optics-0.2 → structured_optics-0.3}/structured_optics/algebra_utils.py +10 -24
  6. {structured_optics-0.2 → structured_optics-0.3}/structured_optics/hologram.py +3 -9
  7. structured_optics-0.3/structured_optics/modes.py +299 -0
  8. structured_optics-0.3/structured_optics/polarization.py +25 -0
  9. {structured_optics-0.2 → structured_optics-0.3}/structured_optics/prop_methods.py +3 -3
  10. structured_optics-0.3/structured_optics/struct_opt.py +580 -0
  11. {structured_optics-0.2 → structured_optics-0.3}/structured_optics/utils.py +211 -69
  12. {structured_optics-0.2 → structured_optics-0.3}/structured_optics.egg-info/PKG-INFO +1 -1
  13. {structured_optics-0.2 → structured_optics-0.3}/structured_optics.egg-info/SOURCES.txt +1 -0
  14. structured_optics-0.2/structured_optics/__init__.py +0 -3
  15. structured_optics-0.2/structured_optics/modes.py +0 -393
  16. structured_optics-0.2/structured_optics/struct_opt.py +0 -392
  17. {structured_optics-0.2 → structured_optics-0.3}/setup.cfg +0 -0
  18. {structured_optics-0.2 → structured_optics-0.3}/structured_optics/slmdisplay.py +0 -0
  19. {structured_optics-0.2 → structured_optics-0.3}/structured_optics.egg-info/dependency_links.txt +0 -0
  20. {structured_optics-0.2 → structured_optics-0.3}/structured_optics.egg-info/requires.txt +0 -0
  21. {structured_optics-0.2 → structured_optics-0.3}/structured_optics.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: structured_optics
3
- Version: 0.2
3
+ Version: 0.3
4
4
  Summary: Package for Structured Light simulation
5
5
  Author: Altilano C. Barbosa
6
6
  Requires-Python: >=3.9
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "structured_optics"
7
- version = "0.2"
7
+ version = "0.3"
8
8
  description = "Package for Structured Light simulation"
9
9
  requires-python = ">=3.9"
10
10
  authors = [
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='structured_optics',
5
- version='0.2',
5
+ version='0.3',
6
6
  packages=find_packages()
7
7
 
8
8
  )
@@ -0,0 +1,4 @@
1
+ from .struct_opt import *
2
+ from .modes import *
3
+
4
+
@@ -3,7 +3,7 @@ import numpy as np
3
3
 
4
4
 
5
5
 
6
- def hg_proj(Beam,N, completeness = False):
6
+ def hg_proj(Beam,N):
7
7
  overlaps = np.zeros((N+1, N+1), dtype='complex')
8
8
  auxiliary_beam = Beam.copy_clean()
9
9
  for n in range(N+1):
@@ -11,14 +11,10 @@ def hg_proj(Beam,N, completeness = False):
11
11
  auxiliary_beam.hg(n,m)
12
12
  overlaps[n,m] = overlap(Beam, auxiliary_beam)
13
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
14
+ return n, m, overlaps
19
15
 
20
16
 
21
- def lg_proj(Beam, N, completeness = False):
17
+ def lg_proj(Beam, N):
22
18
  overlaps = np.zeros((int(2*N)+1, int(N/2)+1), dtype='complex')
23
19
  auxiliary_beam = Beam.copy_clean()
24
20
  for l in np.arange(-N, N+1, 1):
@@ -26,27 +22,23 @@ def lg_proj(Beam, N, completeness = False):
26
22
  auxiliary_beam.lg(l,p)
27
23
  overlaps[l+N,p] = overlap(Beam, auxiliary_beam)
28
24
  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
25
+ return l, p, overlaps
34
26
 
35
27
  def hg_basis(Beam, N, waist = None, norm1 = False):
36
- aux = Beam.copy_clean()
28
+ aux = Beam.copy_clean(1)
37
29
  if waist != None:
38
30
  aux.waist = waist
39
31
  basis = np.empty((N+1, N+1, Beam.Dy, Beam.Dx), dtype = 'complex')
40
32
  for n in range(N+1):
41
33
  for m in range(N+1):
42
34
  aux.hg(n, m)
43
- basis[n, m] = aux.field
35
+ basis[n, m] = aux.Ex
44
36
  if norm1 == True:
45
37
  basis = basis/np.max(basis)
46
38
  return basis
47
39
 
48
40
  def lg_basis(Beam, N, waist=None, norm1=False):
49
- aux = Beam.copy_clean()
41
+ aux = Beam.copy_clean(1)
50
42
  if waist != None:
51
43
  aux.waist = waist
52
44
  basis = np.empty((N+1, N+1, Beam.Dy, Beam.Dx), dtype = 'complex')
@@ -55,7 +47,7 @@ def lg_basis(Beam, N, waist=None, norm1=False):
55
47
  l = m-n
56
48
  p = min(n,m)
57
49
  aux.lg(l, p)
58
- basis[n, m] = aux.field
50
+ basis[n, m] = aux.Ex
59
51
  if norm1 == True:
60
52
  basis = basis/np.max(basis)
61
53
  return basis
@@ -73,18 +65,12 @@ def bessel_basis(Beam, Nmax, waist=None, norm1=False):
73
65
  basis = basis / np.max(np.abs(basis))
74
66
  return basis
75
67
 
76
- def build_from_coefs_and_basis(Beam, coefs, basis):
68
+ def build_from_coefs_and_basis(Beam, coefs, basis, pol_index = 0):
77
69
  N = len(coefs)
78
70
  aux = Beam.copy_clean()
79
71
  for i in range(N):
80
72
  for j in range(N):
81
- aux.field = aux.field + coefs[i,j] * basis[i,j]
73
+ aux.field[pol_index] = aux.field[pol_index] + coefs[i,j] * basis[i,j]
82
74
  Beam.field = aux.field
83
75
  return Beam
84
76
 
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
@@ -6,17 +6,14 @@ import time
6
6
 
7
7
  def slm_hologram(Beam, x_grating, y_grating, method, input_beam, eps, max_range):
8
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
9
 
13
10
  dx = (Beam.x[0,1] - Beam.x[0,0])
14
11
  dy = (Beam.y[1,0] - Beam.y[0,0])
15
12
  if input_beam is None:
16
13
  input_field = np.exp(-((Beam.x**2 + Beam.y**2)/(2*np.max(Beam.x))**2))
17
14
  else:
18
- input_field = input_beam.field
19
- desired = Beam.field/np.max(np.abs(Beam.field))
15
+ input_field = input_beam.Ex
16
+ desired = Beam.Ex/np.max(np.abs(Beam.Ex))
20
17
  lamb_x = dx*x_grating
21
18
  lamb_y = dy*y_grating
22
19
  Ain = np.abs(input_field)
@@ -49,10 +46,7 @@ def slm_hologram(Beam, x_grating, y_grating, method, input_beam, eps, max_range)
49
46
 
50
47
  def dmd_hologram(Beam, cx, cy, sign):
51
48
  #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)
49
+ U = np.abs(Beam.Ex)
56
50
  U = U/np.amax(U)
57
51
  phi = Beam.phase()
58
52
  A = np.arcsin(U)
@@ -0,0 +1,299 @@
1
+ from structured_optics.utils import *
2
+ import numpy as np
3
+ from scipy.special import factorial, jn_zeros, jv
4
+ from scipy.optimize import brentq
5
+
6
+
7
+
8
+ #modes implementations
9
+
10
+ @rotated_mode
11
+ def hg(Beam, N, M, z=0):
12
+ #get a HG mode at distance z of order N+M
13
+ zr = Beam.zr()
14
+ q0 = 1j*zr
15
+ q = -z + 1j*zr
16
+ k = 2*np.pi/Beam.lamb
17
+ w = Beam.waist*np.sqrt(1 + (z/zr)**2)
18
+ Cn = np.sqrt(np.sqrt(2/np.pi)*q0/(2**N * factorial(N) * q * Beam.waist))
19
+ un = Cn*(-np.conjugate(q)/q)**(N/2)*herm(np.sqrt(2)*(Beam.x-Beam.x0)/w, N)*np.exp(-1j*k*(Beam.x-Beam.x0)**2/(2*q))
20
+ Cm = np.sqrt(np.sqrt(2/np.pi)*q0/(2**M * factorial(M) * q * Beam.waist))
21
+ um = Cm*(-np.conjugate(q)/q)**(M/2)*herm(np.sqrt(2)*(Beam.y-Beam.y0)/w, M)*np.exp(-1j*k*(Beam.y-Beam.y0)**2/(2*q))
22
+ F = un*um*np.exp(1j*k*z)
23
+ return F
24
+
25
+ @rotated_mode
26
+ def hg_astigmatic(Beam, N, M, wx, wy, z=0):
27
+ #get a astigmatic HG mode at distance z of order N+M
28
+ zrx = np.pi*wx**2/Beam.lamb
29
+ q0x = 1j*zrx
30
+ qx = -z + q0x
31
+ k = 2*np.pi/Beam.lamb
32
+ wxz = wx*np.sqrt(1 + (z/zrx)**2)
33
+ Cn = np.sqrt(np.sqrt(2/np.pi)*q0x/(2**N * factorial(N) * qx * wx))
34
+ un = Cn*(-np.conjugate(qx)/qx)**(N/2)*herm(np.sqrt(2)*(Beam.x-Beam.x0)/wxz, N)*np.exp(-1j*k*(Beam.x-Beam.x0)**2/(2*qx))
35
+
36
+ zry = np.pi*wy**2/Beam.lamb
37
+ q0y = 1j*zry
38
+ qy = -z + q0y
39
+ wyz = wy*np.sqrt(1 + (z/zry)**2)
40
+ Cm = np.sqrt(np.sqrt(2/np.pi)*q0y/(2**M * factorial(M) * qy * wy))
41
+ um = Cm*(-np.conjugate(qy)/qy)**(M/2)*herm(np.sqrt(2)*(Beam.y-Beam.y0)/wyz, M)*np.exp(-1j*k*(Beam.y-Beam.y0)**2/(2*qy))
42
+ F = un*um*np.exp(1j*k*z)
43
+ return F
44
+
45
+ @rotated_mode
46
+ def lg(Beam,l,p, z=0):
47
+ #get a LG mode at distance z of order abs(N) + 2M
48
+ zr = Beam.zr()
49
+ k = 2*np.pi/Beam.lamb
50
+ w = Beam.waist*np.sqrt(1 + (z/zr)**2)
51
+ R = (z**2 + zr**2)
52
+ gouy = (np.abs(l) + 2*p + 1)*np.arctan(z/zr)
53
+ r = np.sqrt((Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2)
54
+ Cte = np.sqrt(2*factorial(p) / (np.pi*factorial(p+np.abs(l))))
55
+ 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) *\
56
+ np.exp(1j*(k*z + k*z*(r**2)/(2*R) + l*np.arctan2(Beam.y-Beam.y0,Beam.x-Beam.x0) - gouy))
57
+ return F
58
+
59
+ @rotated_mode
60
+ def nbessel(Beam, N, z=0):
61
+ #get a Bessel mode of order N at distance z
62
+ k = 2*np.pi / Beam.lamb # wavenumber
63
+ j01 = jn_zeros(abs(N), 1)[0] # first zero of J_N
64
+ # enforce the first zero at r = Beam.waist
65
+ kr = j01 / Beam.waist
66
+ kz = np.sqrt(k**2 - kr**2)
67
+ r = np.sqrt((Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2)
68
+ phi = np.arctan2(Beam.y-Beam.y0, Beam.x-Beam.x0)
69
+ F = (np.exp(1j*kz*z) * jv(abs(N), kr*r) * np.exp(-1j*N*phi))
70
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
71
+ return F
72
+
73
+ @rotated_mode
74
+ def gbessel(Beam, N, r0):
75
+ #get a gaussian bessel beam of order N at z=0
76
+ rad = 2*np.pi*Beam.waist**2/r0
77
+ r = np.sqrt((Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2)
78
+ F = jv(np.abs(N),rad*r/Beam.waist**2)*\
79
+ np.exp(-1j*N*np.arctan2(Beam.y-Beam.y0, Beam.x-Beam.x0))*np.exp(-(r/Beam.waist)**2)
80
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
81
+ return F
82
+
83
+ @rotated_mode
84
+ def lg_prod(Beam, N, ls, centers):
85
+ if centers is None:
86
+ centers = np.zeros((N, 2))
87
+ centers = np.asarray(centers)
88
+ if ls is None:
89
+ ls = np.ones(N, dtype=int)
90
+ ls = np.asarray(ls)
91
+
92
+ F = np.ones((Beam.Dy, Beam.Dx), dtype='complex128')
93
+ for i in range(N):
94
+ x0 = centers[i, 0]
95
+ y0 = centers[i, 1]
96
+ l = ls[i]
97
+ sign = -1 if l < 0 else 1
98
+ exp_pow = int(np.abs(l))
99
+ arg = (Beam.x - x0) + 1j * sign * (Beam.y - y0)
100
+ r2 = (Beam.x - x0)**2 + (Beam.y - y0)**2
101
+ lg = arg**exp_pow * np.exp(-r2/N / ((Beam.waist)**2))
102
+ F *= lg
103
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
104
+ return F
105
+
106
+ @rotated_mode
107
+ def frac_oam(Beam, Ma, n_modes, beta, theta_0, z=0):
108
+ #get a fractional OAM beam, with OAM Ma (!= integer), by the method of LG supperpositions.
109
+ mu = Ma%1
110
+ m = Ma-mu
111
+ if mu >=0.5:
112
+ n_min = np.ceil(Ma - n_modes / 2)
113
+ else:
114
+ n_min = np.floor(Ma - n_modes / 2)
115
+ n_max = n_min + n_modes - 1
116
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex128')
117
+ for l in np.arange(int(n_min), int(n_max)+1):
118
+ 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))
119
+ F += coef*(lg(Beam,l, 0, z = z))
120
+
121
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
122
+ return F
123
+
124
+ @rotated_mode
125
+ def frac_oam_qs(Beam, Ma, n_modes, beta, theta_0, z=0):
126
+ #get a quasi-stable fractional oam mode.
127
+ n_min = np.round(Ma-n_modes/2)
128
+ n_max = n_min + n_modes - 1
129
+ mu = Ma%1
130
+ m = Ma-mu
131
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex128')
132
+ for l in np.arange(int(n_min), int(n_max)+1):
133
+ 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))
134
+ p = np.floor((np.abs(Ma) + n_modes/2 -np.abs(l))/2)
135
+ F += coef*(lg(Beam, l, p, z = z))
136
+
137
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
138
+ return F
139
+
140
+
141
+ @rotated_mode
142
+ def IG_even(Beam, p, m, q, z=0):
143
+ #Even Ince-Gaussian mode IG_p,m^e(x,y)
144
+
145
+ xi, eta = cartesian_to_elliptic(Beam.x-Beam.x0, Beam.y-Beam.y0, q, Beam.waist, z, Beam.lamb)
146
+
147
+ Ce_xi = C_ince(1j*xi, p, m, q)
148
+ Ce_eta = C_ince(eta, p, m, q)
149
+
150
+ zr = np.pi * Beam.waist**2 / Beam.lamb
151
+ k = 2*np.pi/Beam.lamb
152
+ wz = Beam.waist * np.sqrt(1 + (z * Beam.lamb / (np.pi * Beam.waist**2))**2)
153
+ Rz = (z**2 + zr**2)
154
+ r2 = (Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2
155
+ gouy = (p+1)*np.arctan(z / zr)
156
+ F = Ce_xi * Ce_eta * np.exp(-r2 / (wz**2))*np.exp(1j*(k*z + k*z*(r2)/(2*Rz) - gouy))
157
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
158
+ return F
159
+
160
+ @rotated_mode
161
+ def IG_odd(Beam, p, m, q, z=0):
162
+ #Odd Ince-Gaussian mode IG_p,m^o(x,y)
163
+
164
+ xi, eta = cartesian_to_elliptic(Beam.x-Beam.x0, Beam.y-Beam.y0, q, Beam.waist, z, Beam.lamb)
165
+ So_xi = S_ince(1j*xi, p, m, q)
166
+ So_eta = S_ince(eta, p, m, q)
167
+
168
+ zr = np.pi * Beam.waist**2 / Beam.lamb
169
+ k = 2*np.pi/Beam.lamb
170
+ wz = Beam.waist * np.sqrt(1 + (z * Beam.lamb / (np.pi * Beam.waist**2))**2)
171
+ Rz = (z**2 + zr**2)
172
+ r2 = (Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2
173
+ gouy = (p+1)*np.arctan(z / zr)
174
+ F = So_xi * So_eta * np.exp(-r2 / (wz**2))*np.exp(1j*(k*z + k*z*(r2)/(2*Rz) - gouy))
175
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
176
+ return F
177
+
178
+ @rotated_mode
179
+ def HInceG(Beam, p, m, q, helicity, z=0):
180
+ #Hermite-Ince-Gaussian mode HIG_p,m^e(x,y), combining even and odd Ince-Gaussian modes with given helicity.
181
+ #For some reason IG_odd has a 3*pi/2 phase shift wrt IG_even, so the helicity sign is inverted here.
182
+ xi, eta = cartesian_to_elliptic(Beam.x-Beam.x0, Beam.y-Beam.y0, q, Beam.waist, z, Beam.lamb)
183
+
184
+ Ce_xi = C_ince(1j*xi, p, m, q)
185
+ Ce_eta = C_ince(eta, p, m, q)
186
+
187
+ So_xi = S_ince(1j*xi, p, m, q)
188
+ So_eta = S_ince(eta, p, m, q)
189
+
190
+ zr = np.pi * Beam.waist**2 / Beam.lamb
191
+ k = 2*np.pi/Beam.lamb
192
+ wz = Beam.waist * np.sqrt(1 + (z * Beam.lamb / (np.pi * Beam.waist**2))**2)
193
+ Rz = (z**2 + zr**2)
194
+ r2 = (Beam.x-Beam.x0)**2 + (Beam.y-Beam.y0)**2
195
+ gouy = (p+1)*np.arctan(z / zr)
196
+ 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))
197
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
198
+ return F
199
+
200
+ @rotated_mode
201
+ def circle(Beam, center, radius):
202
+ if radius == None:
203
+ radius = Beam.waist
204
+ #Create a circular mode
205
+ center_x, center_y = center
206
+ # Calculate distance from center for all points
207
+ distances = np.sqrt((Beam.x - center_x)**2 + (Beam.y - center_y)**2)
208
+ # Set points within radius to 1
209
+ mask = distances <= radius
210
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex')
211
+ F[mask] = 1
212
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
213
+ return F
214
+
215
+ @rotated_mode
216
+ def square(Beam, center, side_length):
217
+ if side_length == None:
218
+ side_length = Beam.waist
219
+ center_x, center_y = center
220
+
221
+ # Calculate square boundaries
222
+ half_side = side_length / 2.0
223
+
224
+ # Create mask for points inside square
225
+ mask = (np.abs(Beam.x - center_x) <= half_side) & (np.abs(Beam.y - center_y) <= half_side)
226
+
227
+ # Set points inside square to 1
228
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex')
229
+ F[mask] = 1
230
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
231
+ return F
232
+
233
+ @rotated_mode
234
+ def triangle(Beam, center, side_length):
235
+ if side_length == None:
236
+ side_length = Beam.waist
237
+ center_x, center_y = center
238
+
239
+ # Height of equilateral triangle
240
+ height = side_length * np.sqrt(3) / 2
241
+
242
+ # Vertices for upward-pointing equilateral triangle
243
+ # Top vertex
244
+ v0_x = center_x
245
+ v0_y = center_y + height * 2/3
246
+
247
+ # Bottom left vertex
248
+ v1_x = center_x - side_length / 2
249
+ v1_y = center_y - height * 1/3
250
+
251
+ # Bottom right vertex
252
+ v2_x = center_x + side_length / 2
253
+ v2_y = center_y - height * 1/3
254
+
255
+ # Use barycentric coordinates to determine if point is inside triangle
256
+ def sign(px, py, v1_x, v1_y, v2_x, v2_y):
257
+ return (px - v2_x) * (v1_y - v2_y) - (v1_x - v2_x) * (py - v2_y)
258
+
259
+ # Compute signs for all points
260
+ d1 = sign(Beam.x, Beam.y, v0_x, v0_y, v1_x, v1_y)
261
+ d2 = sign(Beam.x, Beam.y, v1_x, v1_y, v2_x, v2_y)
262
+ d3 = sign(Beam.x, Beam.y, v2_x, v2_y, v0_x, v0_y)
263
+
264
+ # Point is inside if all signs are same
265
+ has_neg = (d1 < 0) | (d2 < 0) | (d3 < 0)
266
+ has_pos = (d1 > 0) | (d2 > 0) | (d3 > 0)
267
+
268
+ mask = ~(has_neg & has_pos)
269
+ F = np.zeros((Beam.Dy, Beam.Dx), dtype='complex')
270
+ F[mask] = 1
271
+ F = F/np.sqrt(np.sum(np.abs(F**2))*(4*Beam.nix/Beam.Dx)*(Beam.niy/Beam.Dy))
272
+ return F
273
+
274
+ @rotated_mode
275
+ def lp(Beam, l, m, n_core, n_clad, parity="cos"):
276
+ """Evaluate the scalar LP_l field Psi(r,phi)"""
277
+ params = get_LP_params(l,m, n_core, n_clad, Beam.waist, Beam.lamb)
278
+
279
+ r = np.sqrt(Beam.x**2 + Beam.y**2)
280
+ PHI = np.arctan2(Beam.y, Beam.x)
281
+ a = Beam.waist
282
+ inside = r <= a
283
+ F = np.zeros_like(r)
284
+ F[inside] = jv(l, params['u'] * r[inside] / a)
285
+ scale = jv(l, params['u']) / kv(l, params['v'])
286
+ F[~inside] = scale * kv(l, params['v'] * r[~inside] / a)
287
+ ang = np.cos(l * PHI) if parity == "cos" else np.sin(l * PHI)
288
+ return F * ang
289
+
290
+ @rotated_mode
291
+ def lp_hel(Beam, l, m, n_core, n_clad):
292
+ co = lp(Beam, np.abs(l), m, n_core, n_clad, parity = 'cos')
293
+ si = lp(Beam, np.abs(l), m, n_core, n_clad, parity = 'sin')
294
+ if l>=0:
295
+ F = (co + 1j*si)/np.sqrt(2)
296
+ else:
297
+ F = (co - 1j*si)/np.sqrt(2)
298
+ return F
299
+
@@ -0,0 +1,25 @@
1
+ import numpy as np
2
+
3
+
4
+ HPROJ = np.array([[1,0],[0,0]], dtype='complex')
5
+ VPROJ = np.array([[0,0],[0,1]], dtype='complex')
6
+ DPROJ = np.array([[1,1],[1,1]], dtype='complex')/2
7
+ APROJ = np.array([[1,-1],[-1,1]], dtype='complex')/2
8
+ RPROJ = np.array([[1,1j],[-1j,1]], dtype='complex')/2
9
+ LPROJ = np.array([[1,-1j],[1j,1]], dtype='complex')/2
10
+
11
+
12
+ def J_rot(matrix, ang):
13
+ rt = np.array([[np.cos(ang), -np.sin(ang)],[np.sin(ang), np.cos(ang)]], dtype='complex')
14
+ return rt@matrix@np.conjugate(rt.T)
15
+
16
+ def J_phase_retarder(delta):
17
+ return np.array([[np.exp(1j*delta/2), 0],[0, np.exp(-1j*delta/2)]])
18
+
19
+ def J_hwp(ang):
20
+ return J_rot(J_phase_retarder(np.pi/2), ang)
21
+
22
+ def J_qwp(ang):
23
+ return J_rot(J_phase_retarder(np.pi/4), ang)
24
+
25
+
@@ -6,8 +6,8 @@ def propagate_fresnel(Beam, z):
6
6
  #Propagate the field by a distance z using fresnel integral. convolution method
7
7
  k = 2*np.pi/Beam.lamb
8
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)
9
+ Beam.fourier_field = fft.fft2(Beam.field, axes=(-2,-1))
10
+ Beam.field = fft.ifft2(prop*Beam.fourier_field, axes=(-2,-1))
11
11
  return Beam
12
12
 
13
13
  def propagate_incoherent(Beam, z):
@@ -41,6 +41,6 @@ def propagate_fraunhofer(Beam, z):
41
41
  Beam.nix = np.max(Beam.x)
42
42
  Beam.niy = np.max(Beam.y)
43
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)
44
+ Beam.fourier_field = fft.fft2(Beam.field, axes=(-2,-1))
45
45
  Beam.field = C*fft.fftshift(Beam.fourier_field)*dx*dy
46
46
  return Beam