Skip to content

Commit

Permalink
Merge pull request #14 from DanielKotik/develop
Browse files Browse the repository at this point in the history
Python package `optbeam`
  • Loading branch information
DanielKotik committed Aug 15, 2021
2 parents 6d95a48 + e98aec5 commit fbbfccd
Show file tree
Hide file tree
Showing 37 changed files with 678 additions and 490 deletions.
11 changes: 11 additions & 0 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[bumpversion]
current_version = 1.4.2
commit = True
tag = True
sign_tags = True

[bumpversion:file:setup.py]

[bumpversion:file:optbeam/__init__.py]

[bumpversion:file:CITATION.cff]
21 changes: 16 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Airy_2d/Airy2d-out/*
Laguerre_Gauss_3d/LaguerreGauss3d-out/*
Gauss_2d/planar/*
Gauss_2d/concave/*
Gauss_2d/convex/*
scripts/Airy_2d/Airy2d-out/*
scripts/Laguerre_Gauss_3d/LaguerreGauss3d-out/*
scripts/Gauss_2d/planar/*
scripts/Gauss_2d/concave/*
scripts/Gauss_2d/convex/*

### Jupyter ###
.ipynb_checkpoints
Expand All @@ -14,9 +14,17 @@ __pycache__/
*.py[cod]
*$py.class

### Cython ###
# C extensions
*.so

# profiling annotions
*.html

# Cython generated C source code
*.c


# Distribution / packaging
.Python
build/
Expand Down Expand Up @@ -112,3 +120,6 @@ dmypy.json

# Pyre type checker
.pyre/

# Virtual environment created via python -m venv optbeam-env
optbeam-env
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
![concave](Gauss_2d/img/concave_intensity_cropped_rotated_resized.png)
![planar](Gauss_2d/img/planar_intensity_cropped_rotated_resized.png)
![convex](Gauss_2d/img/convex_intensity_cropped_rotated_resized.png)
![Airy](Airy_2d/img/Airy_beam_M_0_W_4_scattering.png)
![concave](scripts/Gauss_2d/img/concave_intensity_cropped_rotated_resized.png)
![planar](scripts/Gauss_2d/img/planar_intensity_cropped_rotated_resized.png)
![convex](scripts/Gauss_2d/img/convex_intensity_cropped_rotated_resized.png)
![Airy](scripts/Airy_2d/img/Airy_beam_M_0_W_4_scattering.png)

![snap](Laguerre_Gauss_3d/img/vortex_beam_m_2_longitudinal_resized.png)
![snap](Laguerre_Gauss_3d/img/vortex_beam_m_2_transverse_resized.png)
![snap](Laguerre_Gauss_3d/img/vortex_beam_m_2_3d_half_resized.png)
![Airy](Airy_2d/img/Airy_beam_M_0_W_4_free_space.png)
![snap](scripts/Laguerre_Gauss_3d/img/vortex_beam_m_2_longitudinal_resized.png)
![snap](scripts/Laguerre_Gauss_3d/img/vortex_beam_m_2_transverse_resized.png)
![snap](scripts/Laguerre_Gauss_3d/img/vortex_beam_m_2_3d_half_resized.png)
![Airy](scripts/Airy_2d/img/Airy_beam_M_0_W_4_free_space.png)

# Optical-beams-MEEP
[![DOI](https://zenodo.org/badge/91711821.svg)](https://zenodo.org/badge/latestdoi/91711821)
Expand All @@ -31,8 +31,19 @@ Originally, these files have been used in studying optical beam shifts providing
We highly recommend to install the parallel version of PyMeep via Conda:

```shell
$ conda create -n pmp -c conda-forge pymeep=*=mpi_mpich_*
# create conda virtual environment "pmp"
$ conda create -n pmp -c conda-forge pymeep=*=mpi_mpich_* scipy

# next command is optional, but recommended to enforce environment isolation (no local
# user site packages in conda environment that may shadow conda installed dependencies)
$ conda env config vars set PYTHONNOUSERSITE=True -n pmp

# activate environment
$ conda activate pmp

# install optbeam package inside environment (-e flag is optional; it makes an
# editable install for developers)
$ python -m pip install [-e] .
```

For detailed installation instructions, see the [Meep documentation](https://meep.readthedocs.io/en/latest/Installation/#conda-packages).
Expand Down
Empty file added optbeam/_2d/__init__.py
Empty file.
136 changes: 136 additions & 0 deletions optbeam/_2d/beams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@

import math
import sys

try:
import cython
except ModuleNotFoundError:
cython = None

if not cython or not cython.compiled:
from math import (exp as _exp,
sqrt as _sqrt)
from cmath import exp as _cexp

if cython and not cython.compiled:
print("\nPlease consider compiling `%s.py` via Cython: "
"`$ cythonize -3 -i %s.py`\n" % (__name__, __name__))

from .helpers import _complex_quad, Beam2d


class Beam2dCartesian(Beam2d):
"""..."""

def profile(self, r):
"""Field amplitude function.
Plane wave decomposition: calculate field amplitude at light source
position if not coinciding with beam waist.
"""
self._ry = r.y
self._rz = r.z

if not self.called:
print("Calculating inital field configuration. "
"This will take some time...")
self.called = True

try:
(result,
real_tol,
imag_tol) = _complex_quad(self if cython
and cython.compiled else self._integrand,
-self._k, self._k)
except Exception as e:
print(type(e).__name__ + ":", e)
sys.exit()

return result

def spectrum(self, k_y):
"""Spectrum amplitude distribution function, f."""
raise NotImplementedError

def _phase(self, k_y, x, y):
"""Phase function."""
return x*_sqrt(self._k**2 - k_y**2) + k_y*y

def _integrand(self, k_y):
"""Integrand function."""
return self.spectrum(k_y) * _cexp(1j*self._phase(k_y, self.x, self._ry))


# -----------------------------------------------------------------------------
# specific beam implementations based on Beam2dCartesian base class
# -----------------------------------------------------------------------------
class Gauss2d(Beam2dCartesian):
"""2d Gauss beam."""

def __init__(self, x, params, called=False):
"""..."""
self._W_y = params['W_y']
self._norm = 2 * _sqrt(math.pi) / self._W_y
super().__init__(x, params, called)

def profile(self, r):
"""..."""
# beam profile distribution (field amplitude) at the waist of the beam
if self.x == 0:
return self._norm * _exp(-(r.y / self._W_y)**2)
else:
return super().profile(r)

def spectrum(self, k_y):
"""Spectrum amplitude function, f."""
return self._f_Gauss(k_y, self._W_y)

def _f_Gauss(self, k_y, W_y):
"""Gaussian spectrum amplitude."""
return _exp(-(k_y*W_y/2)**2)


class IncAiry2d(Beam2dCartesian):
"""2d incomplete Airy beam."""

def __init__(self, x, params, called=False):
"""..."""
self._W_y = params['W_y']
self._M = params['M']
self._W = params['W']
super().__init__(x, params, called)

def profile(self, r):
"""..."""
if self.x == 0:
# adjust integration boundaries
self._a = self._M-self._W
self._b = self._M+self._W

return super().profile(r)

def spectrum(self, k_y):
"""..."""
return self._f_Airy(k_y, self._W_y, self._M, self._W)

def _f_Airy(self, k_y, W_y, M, W):
"""Airy spectrum amplitude."""
return W_y * _cexp(1.0j*(-1/3)*(k_y*W_y)**3) \
* self._heaviside(W_y * k_y - (M - W)) \
* self._heaviside((M + W) - W_y * k_y)

def _heaviside(self, x):
"""Theta (Heaviside step) function."""
return 0 if x < 0 else 1

def _integrand(self, k_y):
"""..."""
if self.x == 0:
xi = k_y
return _cexp(1.0j*(-(xi**3)/3 + (xi * self._ry/self._W_y)))
else:
# next line needs _integrand declared cpdef without nogil attribute,
# and will execute slower than repeating the super class integration
# routine function here
#return super(IncAiry2d, self)._integrand(k_y)
return self.spectrum(k_y) * _cexp(1j*self._phase(k_y, self.x, self._ry))
95 changes: 95 additions & 0 deletions optbeam/_2d/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

import sys

try:
import cython
except ModuleNotFoundError:
cython = None

if cython:
if cython.compiled:
from scipy import LowLevelCallable
else:
print("\nPlease consider compiling `%s.py` via Cython: "
"`$ cythonize -3 -i %s.py`\n" % (__name__, __name__))

from scipy.integrate import quad
from types import MappingProxyType


def _real_1d_func(x, func):
"""Return real part of a 1d function."""
return func(x).real


def _imag_1d_func(x, func):
"""Return imag part of a 1d function."""
return func(x).imag


def _imag_1d_func_c(n, arr, func_ptr):
"""Return imag part of a 1d function.
Cython implementation.
"""
# pure python formulation of:
# return (<Beam2dCartesian>func_ptr)(arr[0]).imag
return cython.cast(Beam2d, func_ptr)._integrand(arr[0]).imag


def _real_1d_func_c(n, arr, func_ptr):
"""Return real part of a 1d function.
Cython implementation.
"""
# pure python formulation of:
# return (<Beam2dCartesian>func_ptr)(arr[0]).real
return cython.cast(Beam2d, func_ptr)._integrand(arr[0]).real


def _complex_quad(func, a, b, kwargs={}):
"""Integrate real and imaginary part of the given function."""
if cython and cython.compiled:
# pure python formulation of: cdef void *f_ptr = <void*>func
f_ptr = cython.declare(cython.p_void, cython.cast(cython.p_void, func))

func_capsule = PyCapsule_New(f_ptr, cython.NULL, cython.NULL)

current_module = sys.modules[__name__]

ll_real_1d_func_c = LowLevelCallable.from_cython(current_module,
'_real_1d_func_c',
func_capsule)
ll_imag_1d_func_c = LowLevelCallable.from_cython(current_module,
'_imag_1d_func_c',
func_capsule)
real, real_tol = quad(ll_real_1d_func_c, a, b, **kwargs)
imag, imag_tol = quad(ll_imag_1d_func_c, a, b, **kwargs)
else:
real, real_tol = quad(_real_1d_func, a, b, (func,), **kwargs)
imag, imag_tol = quad(_imag_1d_func, a, b, (func,), **kwargs)

return real + 1j*imag, real_tol, imag_tol


class Beam2d:
"""Abstract base class."""

def __init__(self, x, params, called=False):
"""..."""
self.x = x
self._k = params['k']
self._params = MappingProxyType(params) # read-only view of a dict
self.called = called

@property
def params(self):
"""Beam specific parameters.
This is a read-only property.
"""
return self._params

def _integrand(self, x):
"""Integrand function over one coordinate x."""
raise NotImplementedError
Empty file added optbeam/_3d/__init__.py
Empty file.

0 comments on commit fbbfccd

Please sign in to comment.