Skip to content

Commit

Permalink
Merge pull request #4 from demisjohn/master
Browse files Browse the repository at this point in the history
Pull in DemisJohn's modifications, to transfer ownership
  • Loading branch information
demisjohn authored Apr 11, 2018
2 parents 2d12348 + 1bc956a commit 0a158e0
Show file tree
Hide file tree
Showing 12 changed files with 651 additions and 119 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
*.pyc
*.os

49 changes: 49 additions & 0 deletions INSTALL.MacOSX
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
COMPILING AND INSTALLING

For the compilation of CAMFR on MacOSX, it is relatively straightforward to use MacPorts to install all dependencies, although with the drawback that the built-in Mac OS installation of Python will be superseded by the MacPorts python installation.
The following instructions will install a new Python 2.7 interpreter, alongside your built-in MacOS Python installation. Be sure to used the correct python shell when running the CAMFR install script, and note that CAMFR will only be installed to this particular python interpreter unless you manually copy the module into another `site-packages` folder.

First, download & install MacPorts. Follow the simple instructions here:
https://www.macports.org/install.php
Make sure to also install Xcode (from the App Store or from the apple developer website), agree to the Xcode license and install the Xcode command-line tools, as mentioned on the MacPorts installation page.
Once MacPorts is installed, run `sudo port selfupdate` in the terminal to update all the sources and port lists.

Install all the required CAMFR dependencies with this command:
sudo port install boost +no_single -no_static +python27 blitz +gcc5 lapack +gfortran llvm-3.3 scons py27-Pillow py27-spyder
This requires an internet connection and can take a while to download and build every package.

This will install the following MacPorts packages +variants (and @version used in the tested build):
py27-Pillow @3.2.0_0 (replaces PIL)
blitz @0.10_4+gcc5
boost @1.66.0_0+no_single+no_static+python27
lapack @3.7.1_0+gfortran
llvm-3.3 @3.3_10
scons @3.0.1_0
py27-spyder @2.3.8_0 (could replace this with py27-scipy if you don't want the IDE)

The above command will also install the following ports, as dependencies:
gcc5 @5.5.0_0 (also installs 64-bit gfortran libraries, unlike XCode's 32-bit versions)
py27-scipy @1.0.0_0+gfortran
py27-numpy @1.14.1_0+gfortran
py27-matplotlib @1.5.1_2+cairo+tkinter
blitz @0.10_4+gcc7
(among many many others)

Make a link to the gfortran compiler via:
sudo ln -s /opt/local/bin/gfortran-mp-5 /opt/local/bin/gfortran


Install the CAMFR library by typing 'sudo python2.7 setup.py install' from the CAMFR directory. The command `python2.7` should run the MacPorts python installation - you can check this via `which python2.7` and make sure the executable is located within the "/opt/..." MacPorts directory.
This will first build the CAMFR library, and then install it to the MacPorts python2.7 `site-packages` directory. See below to compile the documentation camfr.pdf prior to installation.

Due to a bug in the setup.py script, we lastly must rename the camfr compiled library, via
sudo mv /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/camfr/_camfr.dylib /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/camfr/_camfr.so

You'll notice that the camfr docs failed to build & install, unless you have a TeX installation active. This would produce the camfr.pdf documentation file within the camfr module folder.
To have this build and install as well, you must install TeX, via
sudo port install texinfo (and maybe texlive-basic)
Then run the docs makefile;
cd camfr/docs; make
The camfr.pdf file is now available in the docs folder. You can copy it to a location of your choice, or a subsequent run of `sudo python2.7 setup.py install` will then copy the PDF into the CAMFR module folder in site-packages.

Tested on macOS 10.12, 10.13, on MacBook Pro 2010 and 2017.
125 changes: 111 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
# CAMFR
Forked from Sourceforge project for maintenance
Originally written by Peter Bienstman

Better maintained fork by [demisjohn](https://github.com/demisjohn/CAMFR)
Forked from [Sourceforge project](http://camfr.sourceforge.net/) for maintenance.

Originally written by [Peter Bienstman at Ghent University, Belgium](http://www.photonics.intec.ugent.be/contact/people.asp?ID=5).


# Description from [SourceForge](http://camfr.sourceforge.net/)
## Introduction

CAMFR (CAvity Modelling FRamework) is a fast, flexible, friendly full-vectorial Maxwell solver. Its main focus is on applications in the field of nanophotonics, like
wavelength-scale microstructures (like photonic crystal devices)
lasers (like vertical-cavity surface-emitting lasers)
light-emitting diodes (like resonant-cavity LEDs)
It is based on a combination of eigenmode expansion and advanced boundary conditions like perfectly matched layers (PML).
CAMFR (CAvity Modelling FRamework) is a Python module providing a fast, flexible, full-vectorial Maxwell solver for electromagnetics simulations. Its main focus is on applications in the field of nanophotonics, like
- wavelength-scale microstructures (like photonic crystal devices, optical waveguides)
- lasers (like vertical-cavity surface-emitting lasers)
- light-emitting diodes (like resonant-cavity LEDs)

It is based on a combination of eigenmode expansion (EME) and advanced boundary conditions like perfectly matched layers (PML).

Using an intuitive python scripting interface one can create and solve for the optical modes/fields in various wavelength-scale structures. Additional math and plotting can then be performed via the SciPy stack. The Eigenmode Expansion (EME) method is especially well-suited to solving for very thin layers, or structures in which the X and Y dimensions are very different, where typical methods like FDTD and FEM have trouble with the vastly differing X/Y discretization.

You can find more information, publications and details [here](http://www.photonics.intec.ugent.be/research/topics.asp?ID=17).



## Features

CAMFR is an ongoing active research project, started at the photonics group of the Department of Information Technology (INTEC) at Ghent University in Belgium. This means that it contains many attractive features and algorithms currently not yet found in commercial modelling tools. CAMFR can be used to calculate
CAMFR was a research project, started at the photonics group of the Department of Information Technology (INTEC) at Ghent University in Belgium. CAMFR can be used to calculate
- the scattering matrix of a structure
- the field inside a structure, for any given excitation
- band diagrams of an infinite periodic structure
-threshold material gain and resonance wavelength of laser modes
- threshold material gain and resonance wavelength of laser modes
- the response to a current source in an arbitrary cavity
- structures terminated by a semi-infinite repetition of another structure

Expand All @@ -31,9 +38,99 @@ Additionally, there is code to model the extraction from light emitting diodes,

Defining structures is quite straightforward, either layer-by-layer, or using geometric primitive shapes. There are also integrated plotting routines for rapid simulation feedback.

## Framework character
CAMFR is conceived as a C++ framework, with all the algorithms implemented in terms of abstract waveguides and scatterers. This makes it extremely easy to extend CAMFR to new geometries.
The end user does not deal with this C++ code directly, but rather through bindings to the Python scripting language. This makes the code very clear and flexible, and allows e.g. to seamlessly integrate CAMFR with Python-aware visualistion tools.
One of the main benefits of the Eigenmode Expansion (EME) method is that very thin (nm) and thick (um) structures can be combined without incurring significant numerical errors, as is often the case for Finite-Difference meshing, in which large differences in the X/Y grids cause calculation problems. Also, once the modes/scattering matrices of a 1D slab or 2D Section have been calculated, extending those regions over an additional dimension does not require large amounts of computational power, as most of the work was in calculating the initial eigenmodes of the structure. This means that repeating structures can be simulated fairly quickly, since the eigenmodes are only calculated once for a repeating section.



## Framework/Module Character

CAMFR is utilized as a Python module, although internally it is conceived as a C++ framework, with all the algorithms implemented in terms of abstract waveguides and scatterers. This makes it extremely easy to extend CAMFR to new geometries.

The end user does not deal with this C++ code directly, but rather through bindings to the Python scripting language. This makes the code very clear and flexible, and allows e.g. to seamlessly integrate CAMFR with Python-aware visualistion tools such as [matplotlib](https://matplotlib.org) and [numpy](http://www.numpy.org).



## Examples
### Silicon Waveguide Mode Solver
Silicon waveguide, Power, Ex and Ey plotted with matplotlib:

<img src="examples/contrib/Silicon_WG_-_Modesolver_example_v1.png" width="350">

See the file `examples/contrib/Example - Silicon-Waveguide ModeSim v2018-01.py` for a full working example.

### Brief Example
Example of rectangular waveguide construction syntax: We will create a rectangular waveguide of SiO2 cladding and Silicon core, calculate the first 4 modes mode & plot them.

>>> import camfr # import the module

First, create some Materials with some refractive index:

>>> SiO = camfr.Material( 1.45 ) # refractive index of SiO2
>>> Si = camfr.Material( 3.4 ) # refractive index of Silicon

Then, create some 1-D slabs, by calling those Materials with a thickness value, and adding them together from bottom to top in a Slab:

>>> clad = camfr.Slab( SiO(15.75) ) # Thicknesses in microns
>>> core = camfr.Slab( SiO(10.0) + Si(2.5) + SiO(5.0) )

This created an imaginary "Slab" structure from bottom-to-top. For example `core` looks like:

top
--------------------
SiO
5.0 um thick
--------------------
Si
2.50 um thick
--------------------
SiO
10.0 um thick
--------------------
bottom

Then make a 2-D structure by calling these Slabs with a width value, and adding them together from left to right in a Waveguide:

>>> WG = camfr.Section( clad(3.0) + core(1.0) + clad(4.0) ) # Widths in microns

Which creates this imaginary 2-D Waveguide structure from left-to-right:

top
---------------------------------------------------------
|<----- 3.0um------>|<-----1.0um------>|<---- 4.0um---->|
| | SiO | |
| | 5.0 um thick | |
| |------------------| |
| SiO | SiN | SiO |
| 15.75um | 2.50 um thick | 15.75um |
| thick |------------------| thick |
| | SiO | |
| | 10.0 um thick | |
---------------------------------------------------------
bottom

You can then have CAMFR calculate the modes as so:

>>> WG.calc()

And plot the modes like so:

>>> WG.plot() # plots the fundamental mode with MatPlotLib.
>>> fig = WG.plot(field=['P','Ex','Ey'], mode=[0,1,2]) # plots the Power and fields of 3 modes

See the Examples directory for full examples, as some details are missing here.



## Installation
CAMFR currently only supports Python 2.7.

To use CAMFR, download one of the released versions (see the "releases" or "tags" section of this github repo), or the bleeding-edge code, and extract the archive into a directory. Follow the instruction in the `INSTALL` text file for your system. You will have to compile the CAMFR library, as it compiles C code to generate the Python library. A number of dependencies are required, which you can hopefully install easily through your system's package manager, or download directly from the developer websites.

The preferred method to run your scripts is through a Python IDE like Spyder (a matlab-like IDE). The simplest installation of Spyder (along with all required scientific python modules) can be accomplished via [Python(x,y)](https://code.google.com/p/pythonxy/) (Win) or [Anaconda](http://continuum.io/downloads) (Mac,Win,Linux), or from source, for example via MacPorts `port install py27-spyder` on Mac OS.

CAMFR scripts can also be run like any typical Python script on the command line via `python myScript.py` or `python -i myScript.py` to make it interactive afterwards.



## License and support
All the code is released under the GPL.
9 changes: 5 additions & 4 deletions camfr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

from __future__ import division

from numpy import *
import numpy as np

import os
if not os.environ.get('NO_CAMFR_GRAPHICS'):
from pylab import *

from _camfr import *
from camfr_PIL import *
from geometry import *
from geometry3d import *
from camfr_PIL import * # converted numpy* to np.*
from geometry import * # converted numpy* to np.*
from geometry3d import * # converted numpy* to np.*
from material import *
from section_matplotlib import * # matplotlib functions for Section objects
from camfrversion import *

# Splash screen.
Expand Down
41 changes: 22 additions & 19 deletions camfr/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
#
############################################################################

from math import *
from math import * # pretty sure this isn't used in this module
from camfr import *
from numpy import *
#from numpy import *
import numpy as np

import ImageFile, Image, ImageDraw
import PIL.Image as Image
import PIL.ImageFile as ImageFile
import PIL.ImageDraw as ImageDraw

############################################################################
#
Expand Down Expand Up @@ -107,7 +110,7 @@ def __init__(self, c, r, mat):
def intersection_at_x(self, x):
D = self.r**2 - (x - self.c.x)**2
if D > 0:
return [ self.c.y - sqrt(D), self.c.y + sqrt(D) ]
return [ self.c.y - np.sqrt(D), self.c.y + np.sqrt(D) ]
else:
return []

Expand Down Expand Up @@ -270,13 +273,13 @@ def similar(slab1, slab2, dy):

# Same interface positions?

if abs(dy) < 1e-12:
if np.abs(dy) < 1e-12:
dy = 1e-12

for i in range(len(slab1)):
if abs(slab1[i][0] - slab2[i][0]) >= dy:
if np.abs(slab1[i][0] - slab2[i][0]) >= dy:
return 0
if abs(slab1[i][1] - slab2[i][1]) >= dy:
if np.abs(slab1[i][1] - slab2[i][1]) >= dy:
return 0

return 1
Expand Down Expand Up @@ -539,7 +542,7 @@ def rescale_nearest(self, image, Xsteps, Zsteps):
def rescale_antialias(image, Xsteps, Zsteps):
return image.resize((Xsteps,Zsteps),Image.ANTIALIAS )

def rescale_custom(image, x, z, Xsteps, Zsteps, method='AVARAGE'):
def rescale_custom(image, x, z, Xsteps, Zsteps, method='AVERAGE'):

# Algorithm =
# resize from fine grid x-z to X-Z
Expand All @@ -560,10 +563,10 @@ def rescale_custom(image, x, z, Xsteps, Zsteps, method='AVARAGE'):
for Z in range (Zsteps):
for X in range (Xsteps):

x1 = int(floor(X*dX/dx)) # First x-matching value.
j = int(floor(dX/dx)) # Number of pixels in x direction.
z1 = int(floor(Z*dZ/dz)) # First z-matching value
k = int(floor(dZ/dz)) # Number of pixels in z direction.
x1 = int(np.floor(X*dX/dx)) # First x-matching value.
j = int(np.floor(dX/dx)) # Number of pixels in x direction.
z1 = int(np.floor(Z*dZ/dz)) # First z-matching value
k = int(np.floor(dZ/dz)) # Number of pixels in z direction.

#list with x-lenghts
LX = [(x1+1)*dx - X*dX]
Expand All @@ -589,9 +592,9 @@ def rescale_custom(image, x, z, Xsteps, Zsteps, method='AVARAGE'):
average += opp / (1+pix[x1+_x,z1+_z])

if (method=='AVERAGE'):
newcolor = floor(average/(dX*dZ))
newcolor = np.floor(average/(dX*dZ))
else: # Inverse average.
newcolor = floor((dX*dZ)/average)-1
newcolor = np.floor((dX*dZ)/average)-1
L[Ln] =(newcolor)
Ln += 1

Expand Down Expand Up @@ -701,15 +704,15 @@ def to_expression(self, x0, x1, dx=0, y0=0, y1=0, dy=0, add_flipped=0,
if ys1 > y1:
ys1 = y1

def same(y0,y1): return abs(y0-y1) < 1e-6
def same(y0,y1): return np.abs(y0-y1) < 1e-6

new_slab = []
j = 0
while j < len(slab):

if (slab[j][1] < ys0) or same(slab[j][1], ys0) or \
(slab[j][0] > ys1) or same(slab[j][0], ys1) or \
(abs(ys0-ys1) < .001*dy):
(np.abs(ys0-ys1) < .001*dy):
new_slab.append(slab[j]) # No intersection.
else:
if not same(slab[j][0], ys0): # Old material pre.
Expand Down Expand Up @@ -769,7 +772,7 @@ def same(y0,y1): return abs(y0-y1) < 1e-6

i = i_end

d[-1] += (x1-x0) - sum(d)
d[-1] += (x1-x0) - np.sum(d)
slabs = new_slabs

# Create expression.
Expand Down Expand Up @@ -823,10 +826,10 @@ def pretty_print(s):
nr = s.mode(i).n_eff().real
ni = s.mode(i).n_eff().imag

if abs(nr) < 1e-6:
if np.abs(nr) < 1e-6:
nr = 0

if abs(ni) < 1e-6:
if np.abs(ni) < 1e-6:
ni = 0

print i, nr, ni
Expand Down
14 changes: 8 additions & 6 deletions camfr/geometry3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

from camfr import *
from geometry import *
# geometry.py imports numpy as np
import numpy as np # no harm in reimporting a module

############################################################################
#
Expand Down Expand Up @@ -103,8 +105,8 @@ def intersection_at_z(self, z):
return Line(Point(self.c.x,self.c.y-self.h/2.0), \
Point(self.c.x,self.c.y+self.h/2.0),self.mat)
else:
return Rectangle(Point(self.c.x-sqrt(D),self.c.y-self.h/2.0), \
Point(self.c.x+sqrt(D),self.c.y+self.h/2.0), \
return Rectangle(Point(self.c.x - np.sqrt(D), self.c.y - self.h / 2.0), \
Point(self.c.x + np.sqrt(D), self.c.y + self.h / 2.0), \
self.mat)


Expand Down Expand Up @@ -246,14 +248,14 @@ def to_expression(self, x0, x1, dx, y0, y1, dy, z0, z1, dz, add_flipped=0):
if ys1 > y1:
ys1 = y1

def same(y0,y1): return abs(y0-y1) < 1e-6
def same(y0,y1): return np.abs(y0-y1) < 1e-6

new_slab = []
j = 0
while j< len(slab):
if (slab[j][1] < ys0) or same(slab[j][1], ys0) or \
(slab[j][0] > ys1) or same(slab[j][0], ys1) or \
(abs(ys0-ys1) < .001*dy):
(np.abs(ys0-ys1) < .001*dy):
new_slab.append(slab[j]) # No intersection.
else:
if not same(slab[j][0], ys0): # Old material pre.
Expand Down Expand Up @@ -384,10 +386,10 @@ def pretty_print(s):
nr = s.mode(i).n_eff().real
ni = s.mode(i).n_eff().imag

if abs(nr) < 1e-6:
if np.abs(nr) < 1e-6:
nr = 0

if abs(ni) < 1e-6:
if np.abs(ni) < 1e-6:
ni = 0

print i, nr, ni
Loading

0 comments on commit 0a158e0

Please sign in to comment.