diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ae98775d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +*.pyc +*.os + diff --git a/INSTALL.MacOSX b/INSTALL.MacOSX new file mode 100644 index 00000000..43bf82e0 --- /dev/null +++ b/INSTALL.MacOSX @@ -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. diff --git a/README.md b/README.md index 76ed774f..534dd17c 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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: + + + +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. diff --git a/camfr/__init__.py b/camfr/__init__.py index f52221fa..e8725363 100644 --- a/camfr/__init__.py +++ b/camfr/__init__.py @@ -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. diff --git a/camfr/geometry.py b/camfr/geometry.py index 7dd38c63..771db4a0 100755 --- a/camfr/geometry.py +++ b/camfr/geometry.py @@ -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 ############################################################################ # @@ -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 [] @@ -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 @@ -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 @@ -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] @@ -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 @@ -701,7 +704,7 @@ 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 @@ -709,7 +712,7 @@ def same(y0,y1): return abs(y0-y1) < 1e-6 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. @@ -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. @@ -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 diff --git a/camfr/geometry3d.py b/camfr/geometry3d.py index 36d12fc2..f0dfa616 100644 --- a/camfr/geometry3d.py +++ b/camfr/geometry3d.py @@ -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 ############################################################################ # @@ -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) @@ -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. @@ -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 diff --git a/examples/contrib/Example - Silicon-Waveguide ModeSim v2018-01.py b/examples/contrib/Example - Silicon-Waveguide ModeSim v2018-01.py new file mode 100644 index 00000000..d9cb3e28 --- /dev/null +++ b/examples/contrib/Example - Silicon-Waveguide ModeSim v2018-01.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +""" + +CAMFR example showing the solving and plotting of a simple multi-mode Silicon waveguide clad in SiO2. + +Spyder `Run Configuration` should be either: + Execute in Current Interpreter + OR + + Execute in New Interpreter + AND + Interact with shell after completion + + +@author: Demis D. John +@email: demis@ucsb.edu +""" + +#################################################### + +''' Import the CAMFR module: ''' +import camfr + +from numpy import pi + + +#################################################### + +''' Some refractive indices ("n"), thicknesses and widths ''' +wl = 1.550 # wavelength in microns + +core = camfr.Material( 3.4 ) # Silicon, RIx +clad = camfr.Material( 1.446 ) # Thermal Oxide +air = camfr.Material( 1.0 ) + +tCore = 0.300 # core thickness in Microns +tClad = 2.0 + +wCore = 1.00 # core width +wSides = 2.0 # lateral width of simulation space + + + +''' Setup some CAMFR simulation parameters ''' + +camfr.set_lambda(wl) # all CAMFR functions/classes should be under the camfr object +lam0 = abs(camfr.get_lambda()) +k0 = 2*pi/lam0 # propagation const. + + +''' Number of modes to find. Computation time scales logarithmically with the number of eigenmodes used. ''' +camfr.set_N(4) + + + +''' -- Construct Waveguide -- ''' +''' Vertical, bottom-to-top ''' +center = camfr.Slab( clad(tClad) + core(tCore) + clad(tClad) ) +side = camfr.Slab( clad(2*tClad) + air(center.width() - 2*tClad) ) + +''' Horizontal, left-to-right ''' +wg = side(wSides) + center(wCore) + side(wSides) + + +s = camfr.Section( wg, 16, 30 ) +''' + Section( + Waveguide, + <# plane waves in 1st stage estimation>, + <# 1D modes in each Slab during 2nd stage> + ) + + Num. of planewaves should improve mode ordering and finding + Num. of Slab Modes should improve accuracy of final mode profiles +''' + + +## CAMFR mode-solver params +camfr.set_section_solver(camfr.L) +camfr.set_mode_correction(camfr.full) +''' `full`: solve for all modes. Other options include `guided_only`, `none` don't refine the modes after the initial estimate. See CAMFR.pdf page 52.''' + +camfr.set_left_wall(camfr.E_wall) +camfr.set_right_wall(camfr.E_wall) +camfr.set_lower_wall(camfr.slab_E_wall) +camfr.set_upper_wall(camfr.slab_E_wall) + +''' Perfectly-matched layers @ simulation edges, to simulate infinite layers. ''' +camfr.set_right_PML(-0.04) +camfr.set_left_PML(-0.04) +camfr.set_upper_PML(-0.04) +camfr.set_lower_PML(-0.04) + + +s.calc() # calculate the modes. + +''' Plot some modes. ''' +#fig = s.plot(dx=0.3, dy=0.3) +fig = s.plot(field=['P','Ex','Ey'], mode=[0,1,2], dx=0.05, dy=0.05) + +''' Save the figure as an image. ''' +#fig.savefig('Silicon_WG_-_Modesolver_example_v1.png') + + +print 'done.' + + + diff --git a/examples/contrib/Silicon_WG_-_Modesolver_example_v1.png b/examples/contrib/Silicon_WG_-_Modesolver_example_v1.png new file mode 100644 index 00000000..07b02e77 Binary files /dev/null and b/examples/contrib/Silicon_WG_-_Modesolver_example_v1.png differ diff --git a/machine_cfg.py.MacOSX b/machine_cfg.py.MacOSX index cfde3b5d..6ad3d5bb 100644 --- a/machine_cfg.py.MacOSX +++ b/machine_cfg.py.MacOSX @@ -1,14 +1,36 @@ # This Python script contains all the machine dependent settings # needed during the build process. +# Tested up to Mac OS 10.13 "High Sierra" +# building for python 2.7, with all dependencies installed using MacPorts +# +# Ports+variants to install via MacPorts (and @version used in this build): +# py27-spyder @2.3.8_0 +# 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 +# +# All ports built for x86_64 as per MacPorts default on recent 64-bit machines + +# Known bugs: +# Currently installs a _camfr.dylib file into the module folder. This needs to be renamed to _camfr.so for Python to use it. +# Command to rename the file before using CAMFR in python (triple-click to select): +# 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 + +# Demis D. John, 2018-02-27, demis@ucsb.edu +############################################ + # Compilers to be used. -cc = "/usr/bin/gcc-4.0" -cxx = "/usr/bin/g++-4.0" +cc = "/usr/bin/gcc" +cxx = "/usr/bin/g++" f77 = "/usr/local/gfortran/bin/gfortran" link = cxx -link_flags = " -undefined dynamic_lookup -dynamic"#-dynamic -single_module -undefined dynamic_lookup" +link_flags = " -undefined dynamic_lookup -dynamic" #-dynamic -single_module -undefined dynamic_lookup" # Compiler flags. # # Note: for the Fortran name definition you can define one of the following @@ -22,25 +44,32 @@ base_flags = " -DFORTRAN_SYMBOLS_WITH_DOUBLE_TRAILING_UNDERSCORE -DNDEBUG" flags_noopt = base_flags -fflags = base_flags + " -march=prescott -O2 -pipe -fomit-frame-pointer -funroll-loops -fstrict-aliasing -g -fPIC" -flags = fflags + " -arch i386 -ftemplate-depth-60" +fflags = base_flags + " -march=native -O2 -pipe -fomit-frame-pointer -funroll-loops -fstrict-aliasing -g -fPIC" +flags = fflags + " -arch x86_64 -ftemplate-depth-60" # Include directories. +# MacPorts directories, MacPorts python2.7 +include_dirs = ["/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7", +"/opt/local/include", +"/opt/local/include/boost", +"/opt/local/include/blitz", +"/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages"] -include_dirs = ["/Library/Frameworks/Python.framework/Versions/2.5/include/python2.5", "/Users/bram/Documents/Work/Compilaties/BuildFolder/include/boost-1_33_1","/Users/bram/Documents/Work/Compilaties/blitz-0.9","/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages"] - -library_dirs = ["/Users/bram/Documents/Work/Compilaties/blitz-0.9/lib/.libs","/Users/bram/Documents/Work/Compilaties/BuildFolder/lib/libjuist","/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A","/usr/local/gfortran/lib"]#"/Users/bram/Documents/Work/Compilaties/gfortran"] +# MacPorts directories +library_dirs = ["/opt/local/lib/", +"/opt/local/lib/lapack/", +"/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A", +"/opt/local/lib/gcc5/"] # Library names. +libs = ["libboost_python-mt", "libblitz", "liblapack","libblas","gfortran"] -libs = ["boost_python", "blitz", "LAPACK","BLAS","gfortran"] -# Command to strip library of excess symbols: - - -dllsuffix = ".so" +# Command to strip library of excess symbols: +dllsuffix = ".dylib" strip_command = ""#"strip --strip-unneeded camfr/_camfr" + dllsuffix -# Extra files to copy into installation directory. +# Extra files to copy into installation directory. +# This PDF should be Built first or the command will fail. extra_files = [("doc", ["docs/camfr.pdf"])] diff --git a/setup.py b/setup.py index b9afa41b..afd6870b 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,10 @@ #! /usr/bin/env python +# To Do: +# Must run the following command afterwards: +# 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 + + from distutils.core import setup from distutils.util import byte_compile from distutils.command.build import build diff --git a/visualisation/camfr_PIL.py b/visualisation/camfr_PIL.py index c8d74491..4105e963 100644 --- a/visualisation/camfr_PIL.py +++ b/visualisation/camfr_PIL.py @@ -10,7 +10,8 @@ ############################################################################## from camfr import * -from numpy import * +#from numpy import * +import numpy as np # Colormap codes. @@ -62,7 +63,7 @@ def set_arrowsize(arrsz=10): p6 = [-0.245, 0.344] p7 = [ 3.387, 0.344] -ARROW = array([ p1, p2, p3, p4, p5, p6, p7 ]) +ARROW = np.array([ p1, p2, p3, p4, p5, p6, p7 ]) @@ -148,7 +149,7 @@ def _create_scaled_matrix_plot(colormap, z, r_x=0, r_y=0, import Image def round(x): - return int(math.floor(x+.5)) + return int(np.math.floor(x+.5)) # Determine width and height of a pixel. @@ -201,8 +202,8 @@ def _create_scaled_arrow_plot(px, pz, r_x=0, r_y=0, pz*=scale_x px*=scale_y - for x in array(range(width))[::ARROWSIZE]: - for y in array(range(height))[::ARROWSIZE]: + for x in np.array(range(width))[::ARROWSIZE]: + for y in np.array(range(height))[::ARROWSIZE]: X = scale_x*x Y = scale_y*y _create_arrow(draw,(X,Y), pz[y,x], px[y,x]) @@ -226,21 +227,21 @@ def _create_scaled_arrow_plot(px, pz, r_x=0, r_y=0, def _scale_function(z, r_x, r_y, min_area, scale): def round(x): - return int(math.floor(x+.5)) + return int(np.math.floor(x+.5)) height = z.shape[0] width = z.shape[1] - if type(r_x)!=ndarray or asarray(r_x).shape[0]==1: r_x = range(width) - if type(r_y)!=ndarray or asarray(r_y).shape[0]==1: r_y = range(height) + if type(r_x)!=np.ndarray or np.asarray(r_x).shape[0]==1: r_x = range(width) + if type(r_y)!=np.ndarray or np.asarray(r_y).shape[0]==1: r_y = range(height) if len(r_x)>1: d_x = r_x[1] - r_x[0] else : d_x = 1 if len(r_y)>1: d_y = r_y[1] - r_y[0] else: d_y = 1 - if (height*width*d_x*d_y < min_area): - scale = math.sqrt(min_area/(height*width*d_x*d_y)) + if (height * width * d_x * d_y < min_area): + scale = np.math.sqrt(min_area/(height*width*d_x*d_y)) if d_x < d_y: scale_x = round(scale*d_x) @@ -269,13 +270,13 @@ def round(x): def _create_arrow(draw, p, dx, dy): - dq = sqrt(dx**2+dy**2) # Distance. + dq = np.sqrt(dx**2+dy**2) # Distance. - if (abs(dx) <= 1e-10): dx = 1e-10 # Don't set arc yet (+/-). - arc = arctan(dy/dx) - if dx < 0: arc += pi + if (np.abs(dx) <= 1e-10): dx = 1e-10 # Don't set arc yet (+/-). + arc = np.arctan(dy/dx) + if dx < 0: arc += np.pi - arrow = array(ARROW) + arrow = np.array(ARROW) # Turn the arrow. arrow[:,0] += arc @@ -285,8 +286,8 @@ def _create_arrow(draw, p, dx, dy): points = [] # Calc every point. for pt in arrow: - dpx = pt[1]*cos(pt[0]) - dpy = pt[1]*sin(pt[0]) + dpx = pt[1] * np.cos(pt[0]) + dpy = pt[1] * np.sin(pt[0]) points.append((p[0]+dpx,p[1]-dpy)) draw.polygon(points, fill=0x000000) @@ -315,7 +316,7 @@ def _create_matrix_plot(z_, r_x=0, r_y=0, colorcode=0, if (zmin < 0) and (0 < zmax) and (not colorcode): colormap = create_bipolar_colormap() - zmax = ml.max(asarray([-zmin, zmax])) + zmax = ml.max(np.asarray([-zmin, zmax])) z += zmax z *= (len(colormap)-1)/(2*zmax) else: @@ -349,7 +350,7 @@ def _create_arrow_plot(px, pz, r_x=0, r_y=0, # Scale pz & px - pmax = max( ml.max(ml.max(abs(pz))), ml.max(ml.max(abs(px)))) + pmax = np.max( ml.max(ml.max(np.abs(pz))), ml.max(ml.max(np.abs(px)))) if (pmax == 0): cst = 0 else: cst = ARROWSIZE/pmax pz = pz*cst @@ -477,25 +478,25 @@ def _create_phasor_movie(z_, r_x=0, r_y=0, min_area=100000, scale=1, ln=0): # the feeling of the image.. so not that correct. zcst = 1e-8 - zmax = 2*log(ml.max(ml.max(abs(z)))+ zcst) - zmin = log(zcst) # Around -23. + zmax = 2 * np.log(ml.max(ml.max(np.abs(z)))+ zcst) + zmin = np.log(zcst) # Around -23. z_scale = (len(colormap)-1)/(zmax-zmin) # Calculate each frame. for Nr in range(0,frames): pic = _create_scaled_matrix_plot(colormap, - ((log(z.real**2 + zcst) - zmin)*z_scale), + ((np.log(z.real**2 + zcst) - zmin)*z_scale), r_x, r_y, min_area = min_area, scale = scale) movie.append(pic) - z *= exp(2j*pi/frames) + z *= np.exp(2j*pi/frames) else: colormap = create_bipolar_colormap() # Scale factors for z. - zmax = ml.max(ml.max(abs(z))) + zmax = ml.max(ml.max(np.abs(z))) if (zmax == 0): # in this case, z=0, the middle of the color palet #(z+zmax)*z_scale) should be = len(colormap)/2 @@ -512,7 +513,7 @@ def _create_phasor_movie(z_, r_x=0, r_y=0, min_area=100000, scale=1, ln=0): colormap,((z+zmax)*z_scale).real,r_x,r_y, min_area = min_area, scale = scale) movie.append(pic) - z *= exp(2j*pi/frames) + z *= np.exp(2j*np.pi/frames) return movie @@ -634,8 +635,8 @@ def create_black_white_colormap(): def _create_color_range(c1=(0,0,0), c2=(255,255,255), ad_last = 0 ): # Calc max range. - diff = array(c2)-array(c1) - colors = float(max(abs(diff))) + diff = np.array(c2)-array(c1) + colors = float(np.max(np.abs(diff))) # Dred,dgreen,dblue. dc = diff / colors @@ -684,11 +685,11 @@ def plot_neff(waveguide): def plot_f(f, r_x, r_y, filename=0, colormap=palet): - fz = zeros([len(r_y),len(r_x)], float) + fz = np.zeros([len(r_y),len(r_x)], float) for i_y in range(len(r_y)): for i_x in range(len(r_x)): - fz[len(r_y)-1-i_y, i_x] = abs(f(r_x[i_x] + r_y[i_y]*1j)) + fz[len(r_y)-1-i_y, i_x] = np.abs(f(r_x[i_x] + r_y[i_y]*1j)) plot_matrix(fz, r_x, r_y, filename, colormap) @@ -705,7 +706,7 @@ def plot_n_waveguide(waveguide, r_x): v = [] for i_x in range(len(r_x)): - v.append((r_x[i_x], abs(waveguide.n(Coord(r_x[i_x],0,0))))) + v.append((r_x[i_x], np.abs(waveguide.n(Coord(r_x[i_x],0,0))))) plot_vector(v) @@ -749,7 +750,7 @@ def plot_n_stack(stack, r_x, r_z, r_y=0, filename=0, colormap=whiteblack): if rxrange and ryrange and rzrange: print "Error: plot_n_stack can only make cross sections." - n = zeros([len(ax1),len(ax2)],float) + n = np.zeros([len(ax1),len(ax2)],float) if rzrange: _calc_n_stack(n, stack, r_x[::-1], r_y[::-1], r_z) else: @@ -786,9 +787,9 @@ def plot_arrow_stack(stack, r_x, r_z, r_y = 0, filename=0): rzrange = True if rxrange and ryrange and rzrange: print "Error: plot_n_stack can only make cross sections" - px = zeros([len(r_x),len(r_z)], float) - py = zeros([len(r_y),len(r_z)], float) - pz = zeros([len(r_x),len(r_z)], float) + px = np.zeros([len(r_x),len(r_z)], float) + py = np.zeros([len(r_y),len(r_z)], float) + pz = np.zeros([len(r_x),len(r_z)], float) if rzrange: _calc_arrow_stack(px, py, pz, stack, r_x[::-1], r_y[::-1], r_z) else: @@ -810,7 +811,7 @@ def plot_arrow_stack(stack, r_x, r_z, r_y = 0, filename=0): def plot_n_section(stack, r_x, r_y, filename, colormap): - n = zeros([len(r_y),len(r_x)], float) + n = np.zeros([len(r_y),len(r_x)], float) _calc_n_stack(n, stack, array(r_x)[::-1], r_y) plot_matrix(n, r_x, r_y, filename, colormap) @@ -825,10 +826,10 @@ def plot_n_section(stack, r_x, r_y, filename, colormap): def plot_n(o, r1, r2=0, r3=0, filename=0, colormap=whiteblack): - if type(r2)!=ndarray or asarray(r2).shape[0]==1: + if type(r2)!=np.ndarray or np.asarray(r2).shape[0]==1: plot_n_waveguide(o, r1) elif type(o) == Stack or type(o) == BlochStack or type(o) == Cavity: - if type(r3)!=ndarray or asarray(r3).shape[0]==1: + if type(r3)!=np.ndarray or np.asarray(r3).shape[0]==1: plot_n_stack(o, r1, r2, 0.0, filename, colormap) else: plot_n_stack(o, r1, r3, r2, filename, colormap) @@ -895,7 +896,7 @@ def plot_field_stack(stack, component, r_x, r_z, r_y = 0, filename=0, if rxrange and ryrange and rzrange: print "Error: plot_n_stack can only make cross sections" - f = zeros([len(ax1),len(ax2)], float) + f = np.zeros([len(ax1),len(ax2)], float) if rzrange: _calc_field_stack(f, stack, r_x[::-1], r_y[::-1], component, r_z) else: @@ -906,7 +907,7 @@ def plot_field_stack(stack, component, r_x, r_z, r_y = 0, filename=0, # Overlay index/arrow profile. if overlay_n: - n = zeros([len(ax1),len(ax2)], float) + n = np.zeros([len(ax1),len(ax2)], float) if rzrange: _calc_n_stack(n, stack, r_x[::-1], r_y[::-1], r_z) else: @@ -914,9 +915,9 @@ def plot_field_stack(stack, component, r_x, r_z, r_y = 0, filename=0, pic_n = _create_matrix_plot(n, ax2, ax1, whiteblack) if arrow: - px = zeros([len(r_x),len(r_z)], float) - py = zeros([len(r_y),len(r_z)], float) - pz = zeros([len(r_x),len(r_z)], float) + px = np.zeros([len(r_x),len(r_z)], float) + py = np.zeros([len(r_y),len(r_z)], float) + pz = np.zeros([len(r_x),len(r_z)], float) if rzrange: _calc_arrow_stack(px, py, pz, stack, r_x[::-1], r_y[::-1], r_z) else: @@ -948,7 +949,7 @@ def plot_field_stack(stack, component, r_x, r_z, r_y = 0, filename=0, def _calc_field_stack(f, stack, r_x, r_y, component, r_z=0): - if type(r_z)!=ndarray or asarray(r_z).shape[0]==1: + if type(r_z)!=np.ndarray or asarray(r_z).shape[0]==1: # 2D for x in range(len(r_x)): for z in range(len(r_y)): @@ -1011,16 +1012,16 @@ def _calc_arrow_stack(px, pz, stack, r_x, r_z): global ARROWSIZE if get_polarisation() == TM: - for x in array(range(len(r_x)))[::ARROWSIZE]: - for z in array(range(len(r_z)))[::ARROWSIZE]: + for x in np.array(range(len(r_x)))[::ARROWSIZE]: + for z in np.array(range(len(r_z)))[::ARROWSIZE]: f = stack.field(Coord(r_x[x], 0, r_z[z])) h2 = f.H2().conjugate() px[x,z] = (-f.Ez() * h2).real pz[x,z] = ( f.E1() * h2).real elif get_polarisation() == TE: - for x in array(range(len(r_x)))[::ARROWSIZE]: - for z in array(range(len(r_z)))[::ARROWSIZE]: + for x in np.array(range(len(r_x)))[::ARROWSIZE]: + for z in np.array(range(len(r_z)))[::ARROWSIZE]: f = stack.field(Coord(r_x[x], 0, r_z[z])) e2 = f.E2() px[x,z] = ( e2 * f.Hz().conjugate()).real @@ -1028,24 +1029,24 @@ def _calc_arrow_stack(px, pz, stack, r_x, r_z): elif get_polarisation() == TE_TM: # 3D stack if len(r_x)==1: - for y in array(range(len(r_y)))[::ARROWSIZE]: - for z in array(range(len(r_z)))[::ARROWSIZE]: + for y in np.array(range(len(r_y)))[::ARROWSIZE]: + for z in np.array(range(len(r_z)))[::ARROWSIZE]: f = stack.field(Coord(x,r_y[y], r_z[z])) py[y,z] = (f.H1().conjugate()*f.Ez() \ -f.E1()*f.Hz().conjugate()).real pz[y,z] = (f.E1()*f.H2().conjugate() \ -f.H1().conjugate()*f.E2()).real elif len(r_y)==1: - for x in array(range(len(r_x)))[::ARROWSIZE]: - for z in array(range(len(r_z)))[::ARROWSIZE]: + for x in np.array(range(len(r_x)))[::ARROWSIZE]: + for z in np.array(range(len(r_z)))[::ARROWSIZE]: f = stack.field(Coord(r_x[x],y, r_z[z])) px[x,z] = (f.E2()*f.Hz().conjugate() \ -f.H2().conjugate()*f.Ez()).real pz[x,z] = (f.E1()*f.H2().conjugate() \ -f.H1().conjugate()*f.E2()).real else: - for x in array(range(len(r_x)))[::ARROWSIZE]: - for y in array(range(len(r_y)))[::ARROWSIZE]: + for x in np.array(range(len(r_x)))[::ARROWSIZE]: + for y in np.array(range(len(r_y)))[::ARROWSIZE]: f = stack.field(Coord(r_x[x],r_y[y], z)) px[y,x] = (f.E2()*f.Hz().conjugate() \ -f.H2().conjugate()*f.Ez()).real @@ -1066,7 +1067,7 @@ def _calc_arrow_stack(px, pz, stack, r_x, r_z): def plot_field_section_mode(mode, component, r_x, r_y, filename, colormap, overlay_n=1, contour=1): - f = zeros([len(r_y),len(r_x)], float) + f = np.zeros([len(r_y),len(r_x)], float) for i_x in range(len(r_x)): for i_y in range(len(r_y)): @@ -1078,7 +1079,7 @@ def plot_field_section_mode(mode, component, r_x, r_y, filename, colormap, # Overlay index profile. - n = zeros([len(r_y),len(r_x)], float) + n = np.zeros([len(r_y),len(r_x)], float) for i_x in range(len(r_x)): for i_y in range(len(r_y)): @@ -1100,10 +1101,10 @@ def plot_field_section_mode(mode, component, r_x, r_y, filename, colormap, def plot_field(o, component, r1, r2=0, r3=0, filename=0, colormap=0, overlay_n=1, contour=1, arrow=0): - if type(r2)!=ndarray or asarray(r2).shape[0]==1: + if type(r2)!=np.ndarray or np.asarray(r2).shape[0]==1: plot_field_waveguide(o, component, r1) elif type(o) == Stack or type(o) == BlochMode or type(o) == Cavity: - if type(r3)!=ndarray or asarray(r3).shape[0]==1: # 2D stack. + if type(r3)!=np.ndarray or np.asarray(r3).shape[0]==1: # 2D stack. plot_field_stack(o, component, r1, r2, 0, filename, colormap, overlay_n,contour,arrow) else: @@ -1157,7 +1158,7 @@ def animate_field_stack(stack, component, r_x, r_z, r_y = 0, filename=0, if rxrange and ryrange and rzrange: print "Error: plot_n_stack can only make cross sections" - f = zeros([len(ax1),len(ax2)], complex) + f = np.zeros([len(ax1),len(ax2)], complex) if rzrange: _calc_field_stack(f, stack, r_x[::-1], r_y[::-1], component, r_z) else: @@ -1167,7 +1168,7 @@ def animate_field_stack(stack, component, r_x, r_z, r_y = 0, filename=0, # Overlay index profile. - n = zeros([len(ax1),len(ax2)], float) + n = np.zeros([len(ax1),len(ax2)], float) if rzrange: _calc_n_stack(n, stack, r_x[::-1], r_y[::-1], r_z) else: @@ -1192,7 +1193,7 @@ def animate_field_stack(stack, component, r_x, r_z, r_y = 0, filename=0, def animate_field_section_mode(mode, component, r_x, r_y, filename=0, overlay_n=1, contour=1): - f = zeros([len(r_y),len(r_x)], complex) + f = np.zeros([len(r_y),len(r_x)], complex) for i_x in range(len(r_x)): for i_y in range(len(r_y)): @@ -1204,7 +1205,7 @@ def animate_field_section_mode(mode, component, r_x, r_y, filename=0, # Overlay index profile. - n = zeros([len(r_y),len(r_x)], float) + n = np.zeros([len(r_y),len(r_x)], float) for i_x in range(len(r_x)): for i_y in range(len(r_y)): @@ -1230,7 +1231,7 @@ def animate_field(o, component, r1, r2, r3=0, filename=0, overlay_n=1, contour=1, ln=0): if type(o) == Stack or type(o) == BlochMode or type(o) == Cavity: - if type(r3)!=ndarray: # 2D + if type(r3)!=np.ndarray: # 2D animate_field_stack(o,component,r1,r2,0.0,filename, overlay_n,contour,ln) else: diff --git a/visualisation/section_matplotlib.py b/visualisation/section_matplotlib.py new file mode 100755 index 00000000..2d5d3e5f --- /dev/null +++ b/visualisation/section_matplotlib.py @@ -0,0 +1,233 @@ +#! /usr/bin/env python + +########################################################################### +# +# File: matplotlib_section.py +# Author: demis@ucsb.edu +# Date: 20180311 +# Version: 1.0 +# +# Copyright (C) 2018 Demis D. John - Univ. of California Santa Barbara +# +############################################################################ + +from _camfr import * # import the Section and Slab classes, in order to add functions to them. +import numpy as np +import matplotlib.pyplot as plt + + + +############################################################################ +# +# This file contains MatPlotLib-based plotting functions that will be added to +# various CAMFR classes, such as the Section and Slab classes. +# To add functions to classes defined externally, first create a function with +# a placeholder name, typically beginning with two underscores __, as this +# indicates that a user should generally not be calling this func directly. +# Then, add an attribute to the target class that points to this function. +# `self` will be the object, once this func is called as a method of that object +# +# Demis D. John, 2018-03-11, Univ. of California Santa Barbara +# +############################################################################ + +############################################################################ +# +# Choose some global plotting options: +# +############################################################################ + +## Colormap +colormap = plt.get_cmap('hot') + +AxisBGColor = 'black' # background color of every axis + + + +############################################################################ +# +# Functions +# +############################################################################ + +def __Section_plot(self, field="Ex", mode=0, dx=0.100, dy=0.100, annotations=True): + ''' + Plot a 2D mode profile of the specified field, using MatPlotLib. + + Parameters + ---------- + ModeObj: a CAMFR Mode object, often acquired via `SectionObj.mode(0)`. + + field : {'Ex', 'Ey', 'Ez', 'Hx', 'Hy', 'Hz'} + Electric (E) or Magnetic (H) field, x/y/z component. + Case-insensitive. + Defaults to 'Ex', x-component (horizontal) of the electric field, or `E1` in CAMFR parlance. + Can pass an iterable of strings, such as + ['Ex', 'Ey'] + to plot multiple fields on a single figure. Each field will be plotted along the rows of figure axes. + + mode : integer + Which waveguide mode to plot, an integer from 0 to get_N(), however many modes are calculated for the Section + Defaults to 0. + Can pass an iterable of integers, such as + [0, 1, 2] + to plot multiple modes on a single figure. Each mode will be plotted along the columns of figure axes. + + dx, dy: float + x & y resolution for Mode Profiles & Gamma/field plotting. Default is 0.100um for both. + + annotations : boolean, optional + If true, the effective index, mode number and field component will written on each mode plot. True by default. + + Not Implemented: + field = 'P' will plot normalized Power. + + + + Returns + ------- + Returns the handle to the matplotlib figure object. This allows you to save or close the figure etc., via + modeplot(mode=0).savefig('savedimage.png') + or + >>> fig = modeplot(mode=0, field='Ez') + >>> fig.savefig('savedimage.png') + >>> from matplotlib.pyplot import close as plot_close + >>> plot_close(fig) # close the figure during loops + You can also retrieve the axis, pcolormesh, axes etc. objects from this handle as so: + >>> [ax] = fig.get_axes() + >>> QuadMeshObj = ax.get_children()[0] + >>> XAxisObj = ax.get_children()[5] + + ''' + + ## sanitize `field` argument, check if iterable + if hasattr(field, '__iter__'): + numfields = len(field) + else: + numfields = 1; # loop only once + field = [field] # make iterable with one item + + ## Convert `field` strings to corresponding CAMFR string + # See pg. 59 `Field` of CAMFR manual for available options. + # For a Section, axis 1 = X, horizontal, and axis 2 = Y, vertical. + # user options :: CAMFR option as dictionary + fieldopts = {} # initialize dictionary + fieldopts['ex'] = 'E1' + fieldopts['ey'] = 'E2' + fieldopts['ez'] = 'Ez' + fieldopts['hx'] = 'H1' + fieldopts['hy'] = 'H2' + fieldopts['hz'] = 'Hz' + fieldopts['p'] = 'abs_S' + + # create the complementary array of CAMFR fields + try: + cfield = [fieldopts[a] for a in [b.lower() for b in field] ] + except KeyError, k: + raise ValueError( "Unrecognized value %s found for the `field` argument. " % k + "The following options are valid: %s" % fieldopts.keys() ) + #end try(fieldopts) + + + + + ## sanitize `mode` argument, check if iterable + if hasattr(mode, '__iter__'): + nummodes = len(mode) + else: + nummodes = 1; # loop only once + mode = [mode] # make iterable with one item + + # get Section object + obj = self + + + # no harm in re-importing already imported modules, no extra processing time: + import matplotlib.pyplot as plt + import matplotlib.cm as cm # colormaps + import numpy as np + plt.ion() # interactive plotting mode, for live updating + + + + + fig, ax = plt.subplots(nrows=nummodes, ncols=numfields, sharex=True, sharey=True, squeeze=False) + + + #print( "Mode Profile Resolution: (%f,%f)" %(dx,dy) ) + Modes = np.copy(mode) # convert to array + #Fields = np.copy(field) + m = -1 + for modeN in Modes: + f = -1 + m = m+1 + for camfrfield in cfield: + f = f+1 + print( "Calculating fields for Mode %i: %s" %( modeN,field[f].title() ) ) + x = np.arange(0, obj.width(), dx) + y = np.arange(0, obj.height(), dy) + X,Y = np.meshgrid(x,y) + + + intensity = np.zeros((np.size(y),np.size(x)), float) + F = np.zeros((np.size(y),np.size(x)), float) + + + for i in range(0, np.size(y)): + for j in range(0, np.size(x)): + # Use getattr to allow user to specify the field to plot/use. + F[i,j] = \ + np.abs( + getattr( obj.mode(modeN).field(Coord(X[i,j], Y[i,j], 0)) , camfrfield)() + ) ** 2.0 + #end for(x) + #end for(y) + + #TEtot = np.sum(TEfield[:]) + #TMtot = np.sum(TMfield[:]) + #TEfrac = 100.*(TEtot/(TEtot+TMtot)) + + #print( '* TE Fraction: %3.1f ' %TEfrac + 'n = %1.7f + i* ' % obj.mode(modeN).n_eff().real + '%1.7e' % obj.mode(modeN).n_eff().imag ) + + ## Plot the specified mode/field: + axis = ax[ m , f ] # which axis to plot on + + axis.pcolormesh( X, Y, F, cmap=colormap ) + if m==( len(Modes)-1 ): axis.set_xlabel(r'x ($\mu{}m$)') # LaTeX notation, overkill + if f==0: + ystr = "Mode(" + str(modeN) + ")" + axis.set_ylabel(ystr) + #if f==0: axis.set_ylabel(r'y ($\mu{}m$)') + if m==0: + axis.set_title( field[f].title() ) + axis.set_xlim( axis.get_xlim()[0], obj.width() ) + axis.set_ylim( axis.get_ylim()[0], obj.height() ) + #axis.set_axis_bgcolor( AxisBGColor ) # this version works for matplotlib v2.0 + + + if annotations: + titlestr = "Mode(" + str(modeN) + "): " + field[f].title() + #axis.set_title( titlestr ) + axis.text( 0.05, 0.9, titlestr, transform=axis.transAxes, horizontalalignment='left', color='green', fontsize=9, fontweight='bold') + + n_str = "$\mathregular{n_{eff} =}$ %0.5f" % ( obj.mode(modeN).n_eff().real ) + if f==0: axis.text( 0.05, 0.05, n_str, transform=axis.transAxes, horizontalalignment='left', color='green', fontsize=9, fontweight='bold') + #end if(annotations) + + ## update the plots: + if (m==0) and (f==0): + fig.canvas.window().raise_() # bring plot window to front (a hack - delete this if it causes trouble) + fig.canvas.draw() # update the figure + plt.pause(0.05) # allow GUI to update (may pop a warning) + + # end for Fields + # end for Modes + + return fig + +#end _Section_plot() + + +# add the above function to the Section class: +Section.plot = __Section_plot +# This determines the real name of the function as a Section method, and points to this function. \ No newline at end of file