structured-optics 0.2__tar.gz → 0.4__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 (25) hide show
  1. structured_optics-0.4/LICENSE +28 -0
  2. structured_optics-0.4/PKG-INFO +93 -0
  3. structured_optics-0.4/README.md +79 -0
  4. {structured_optics-0.2 → structured_optics-0.4}/pyproject.toml +3 -1
  5. {structured_optics-0.2 → structured_optics-0.4}/setup.py +1 -1
  6. structured_optics-0.4/structured_optics/__init__.py +4 -0
  7. {structured_optics-0.2 → structured_optics-0.4}/structured_optics/algebra_utils.py +10 -24
  8. {structured_optics-0.2 → structured_optics-0.4}/structured_optics/hologram.py +3 -9
  9. structured_optics-0.4/structured_optics/modes.py +299 -0
  10. structured_optics-0.4/structured_optics/polarization.py +25 -0
  11. {structured_optics-0.2 → structured_optics-0.4}/structured_optics/prop_methods.py +3 -3
  12. structured_optics-0.4/structured_optics/struct_opt.py +1460 -0
  13. {structured_optics-0.2 → structured_optics-0.4}/structured_optics/utils.py +211 -69
  14. structured_optics-0.4/structured_optics.egg-info/PKG-INFO +93 -0
  15. {structured_optics-0.2 → structured_optics-0.4}/structured_optics.egg-info/SOURCES.txt +3 -0
  16. structured_optics-0.2/PKG-INFO +0 -9
  17. structured_optics-0.2/structured_optics/__init__.py +0 -3
  18. structured_optics-0.2/structured_optics/modes.py +0 -393
  19. structured_optics-0.2/structured_optics/struct_opt.py +0 -392
  20. structured_optics-0.2/structured_optics.egg-info/PKG-INFO +0 -9
  21. {structured_optics-0.2 → structured_optics-0.4}/setup.cfg +0 -0
  22. {structured_optics-0.2 → structured_optics-0.4}/structured_optics/slmdisplay.py +0 -0
  23. {structured_optics-0.2 → structured_optics-0.4}/structured_optics.egg-info/dependency_links.txt +0 -0
  24. {structured_optics-0.2 → structured_optics-0.4}/structured_optics.egg-info/requires.txt +0 -0
  25. {structured_optics-0.2 → structured_optics-0.4}/structured_optics.egg-info/top_level.txt +0 -0
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, <Your Altilano Cristino Barbosa>
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.4
2
+ Name: structured_optics
3
+ Version: 0.4
4
+ Summary: Package for Structured Light simulation
5
+ Author: Altilano C. Barbosa
6
+ License-Expression: BSD-3-Clause
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: numpy
11
+ Requires-Dist: scipy
12
+ Requires-Dist: PyQt6
13
+ Dynamic: license-file
14
+
15
+ # structured-optics
16
+
17
+ A Python library for generating, propagating, and analyzing structured light beams
18
+ (Hermite-Gaussian, Laguerre-Gaussian, Bessel, Ince-Gaussian, fiber LP modes, and more)
19
+ on a discretized transverse grid.
20
+
21
+ [![PyPI version](https://img.shields.io/pypi/v/structured-optics.svg)](https://pypi.org/project/structured-optics/)
22
+
23
+ [![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD--3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
24
+
25
+ ## Features
26
+
27
+ - **Mode generation**: Hermite-Gaussian, Laguerre-Gaussian, Bessel, Gaussian-Bessel,
28
+ Ince-Gaussian (even/odd/helical), fractional OAM, fiber LP modes, and geometric
29
+ apertures (circle, square, triangle).
30
+ - **Propagation**: Fresnel, Fraunhofer, and incoherent propagation methods.
31
+ - **Polarization**: vector beams with Ex/Ey/Ez components, Jones-matrix optics
32
+ (waveplates, polarizers).
33
+ - **Optical elements**: lenses (spherical, astigmatic, tilted), slits, irises,
34
+ and other amplitude/phase masks.
35
+ - **Analysis**: power, intensity and phase profiles, centroid, beam size (std),
36
+ angular sections, and modal decomposition (HG/LG/Bessel basis projections).
37
+ - **Holography**: SLM and DMD hologram generation for experimental beam shaping.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install structured-optics
43
+ ```
44
+
45
+ ## Quick start
46
+
47
+ ```python
48
+ import structured_optics as so
49
+
50
+ # Create a beam on a 10mm x 10mm (2*5e-3) grid with 256x256 points
51
+ beam = so.Beam(nix=5e-3, Dx=256, waist=1e-3, lamb=1064e-9)
52
+
53
+ # Set the field to a Laguerre-Gaussian mode with l=2, p=0
54
+ beam.lg(l=2, p=0)
55
+
56
+ # Propagate 10 cm and check total power
57
+ beam.propagate(z=0.1, method='fresnel')
58
+ print(beam.Power())
59
+
60
+ # Look at the intensity profile
61
+ import matplotlib.pyplot as plt
62
+ plt.pcolormesh(beam.x, beam.y, beam.int_profile())
63
+ plt.show()
64
+ ```
65
+
66
+ ## Documentation
67
+
68
+ Full API documentation, including all mode types, propagation methods, and
69
+ optical elements, is available at:
70
+
71
+ Soon...
72
+
73
+ ## Requirements
74
+
75
+ - Python 3.9+
76
+ - numpy
77
+ - scipy
78
+ - PyQt6
79
+
80
+
81
+
82
+ ## Contributing
83
+
84
+ Issues and pull requests are welcome at
85
+ [github.com/Altilano/structured-optics](https://github.com/Altilano/structured-optics).
86
+
87
+ ## License
88
+
89
+ This project is licensed under the BSD-3-Clause License. See `LICENSE` for details.
90
+
91
+ ## Citation
92
+
93
+
@@ -0,0 +1,79 @@
1
+ # structured-optics
2
+
3
+ A Python library for generating, propagating, and analyzing structured light beams
4
+ (Hermite-Gaussian, Laguerre-Gaussian, Bessel, Ince-Gaussian, fiber LP modes, and more)
5
+ on a discretized transverse grid.
6
+
7
+ [![PyPI version](https://img.shields.io/pypi/v/structured-optics.svg)](https://pypi.org/project/structured-optics/)
8
+
9
+ [![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD--3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
10
+
11
+ ## Features
12
+
13
+ - **Mode generation**: Hermite-Gaussian, Laguerre-Gaussian, Bessel, Gaussian-Bessel,
14
+ Ince-Gaussian (even/odd/helical), fractional OAM, fiber LP modes, and geometric
15
+ apertures (circle, square, triangle).
16
+ - **Propagation**: Fresnel, Fraunhofer, and incoherent propagation methods.
17
+ - **Polarization**: vector beams with Ex/Ey/Ez components, Jones-matrix optics
18
+ (waveplates, polarizers).
19
+ - **Optical elements**: lenses (spherical, astigmatic, tilted), slits, irises,
20
+ and other amplitude/phase masks.
21
+ - **Analysis**: power, intensity and phase profiles, centroid, beam size (std),
22
+ angular sections, and modal decomposition (HG/LG/Bessel basis projections).
23
+ - **Holography**: SLM and DMD hologram generation for experimental beam shaping.
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install structured-optics
29
+ ```
30
+
31
+ ## Quick start
32
+
33
+ ```python
34
+ import structured_optics as so
35
+
36
+ # Create a beam on a 10mm x 10mm (2*5e-3) grid with 256x256 points
37
+ beam = so.Beam(nix=5e-3, Dx=256, waist=1e-3, lamb=1064e-9)
38
+
39
+ # Set the field to a Laguerre-Gaussian mode with l=2, p=0
40
+ beam.lg(l=2, p=0)
41
+
42
+ # Propagate 10 cm and check total power
43
+ beam.propagate(z=0.1, method='fresnel')
44
+ print(beam.Power())
45
+
46
+ # Look at the intensity profile
47
+ import matplotlib.pyplot as plt
48
+ plt.pcolormesh(beam.x, beam.y, beam.int_profile())
49
+ plt.show()
50
+ ```
51
+
52
+ ## Documentation
53
+
54
+ Full API documentation, including all mode types, propagation methods, and
55
+ optical elements, is available at:
56
+
57
+ Soon...
58
+
59
+ ## Requirements
60
+
61
+ - Python 3.9+
62
+ - numpy
63
+ - scipy
64
+ - PyQt6
65
+
66
+
67
+
68
+ ## Contributing
69
+
70
+ Issues and pull requests are welcome at
71
+ [github.com/Altilano/structured-optics](https://github.com/Altilano/structured-optics).
72
+
73
+ ## License
74
+
75
+ This project is licensed under the BSD-3-Clause License. See `LICENSE` for details.
76
+
77
+ ## Citation
78
+
79
+
@@ -4,8 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "structured_optics"
7
- version = "0.2"
7
+ version = "0.4"
8
8
  description = "Package for Structured Light simulation"
9
+ readme = "README.md"
10
+ license = "BSD-3-Clause"
9
11
  requires-python = ">=3.9"
10
12
  authors = [
11
13
  { name = "Altilano C. Barbosa" }
@@ -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
+