tmm package

Module contents

tmm - A transfer-matrix method optics package, for calculating reflection, transmission, absorption, and other relevant aspects of thin and thick multilayer (or single-layer) films.

Written by Steven Byrnes, http://sjbyrnes.com . Package lives at https://pypi.python.org/pypi/tmm

Released under MIT license (Expat).

For details see manual.pdf (should be included with the distribution, otherwise get it at http://sjbyrnes.com/fresnel_manual.pdf ). Physics background, conventions, and derivations are at https://arxiv.org/abs/1603.02720 For the various functions and their explanations and syntaxes, browse tmm_core, particularly the docstrings of all the functions. Most important of these are:

tmm.coh_tmm(...) – the transfer-matrix-method calculation in the coherent case (i.e. thin films)

tmm.inc_tmm(...) – the transfer-matrix-method calculation in the incoherent case (i.e. films tens or hundreds of wavelengths thick, or whose thickness is not very uniform.

In tmm.examples you will find some sample calculations and plots.

In tmm.tests you will find various tests that the package is coded correctly.

In tmm.colors you will find extra functions for calculating the color of a multilayer thin film under reflected light.

Submodules

tmm.tmm_core module

For information see the docstring of each function, and also see manual.pdf (should be included with the distribution, otherwise get it at http://sjbyrnes.com/fresnel_manual.pdf ). Physics background, conventions, and derivations are at https://arxiv.org/abs/1603.02720

The most two important functions are:

coh_tmm(...) – the transfer-matrix-method calculation in the coherent case (i.e. thin films)

inc_tmm(...) – the transfer-matrix-method calculation in the incoherent case (i.e. films tens or hundreds of wavelengths thick, or whose thickness is not very uniform.)

These functions are all imported into the main package (tmm) namespace, so you can call them with tmm.coh_tmm(...) etc.

tmm.tmm_core.R_from_r(r)[source]

Calculate reflected power R, starting with reflection amplitude r.

tmm.tmm_core.T_from_t(pol, t, n_i, n_f, th_i, th_f)[source]

Calculate transmitted power T, starting with transmission amplitude t.

n_i,n_f are refractive indices of incident and final medium.

th_i, th_f are (complex) propegation angles through incident & final medium (in radians, where 0=normal). “th” stands for “theta”.

In the case that n_i,n_f,th_i,th_f are real, formulas simplify to T=|t|^2 * (n_f cos(th_f)) / (n_i cos(th_i)).

See manual for discussion of formulas

class tmm.tmm_core.absorp_analytic_fn[source]

Bases: object

Absorption in a given layer is a pretty simple analytical function: The sum of four exponentials.

a(z) = A1*exp(a1*z) + A2*exp(-a1*z)
  • A3*exp(1j*a3*z) + conj(A3)*exp(-1j*a3*z)

where a(z) is absorption at depth z, with z=0 being the start of the layer, and A1,A2,a1,a3 are real numbers, with a1>0, a3>0, and A3 is complex. The class stores these five parameters, as well as d, the layer thickness.

This gives absorption as a fraction of intensity coming towards the first layer of the stack.

add(b)[source]

adds another compatible absorption analytical function

copy()[source]

Create copy of an absorp_analytic_fn object

fill_in(coh_tmm_data, layer)[source]

fill in the absorption analytic function starting from coh_tmm_data (the output of coh_tmm), for absorption in the layer with index “layer”.

flip()[source]

Flip the function front-to-back, to describe a(d-z) instead of a(z), where d is layer thickness.

run(z)[source]

Calculates absorption at a given depth z, where z=0 is the start of the layer.

scale(factor)[source]

multiplies the absorption at each point by “factor”.

tmm.tmm_core.absorp_in_each_layer(coh_tmm_data)[source]

An array listing what proportion of light is absorbed in each layer.

Assumes the final layer eventually absorbs all transmitted light.

Assumes the initial layer eventually absorbs all reflected light.

Entries of array should sum to 1.

coh_tmm_data is output of coh_tmm()

tmm.tmm_core.coh_tmm(pol, n_list, d_list, th_0, lam_vac)[source]

Main “coherent transfer matrix method” calc. Given parameters of a stack, calculates everything you could ever want to know about how light propagates in it. (If performance is an issue, you can delete some of the calculations without affecting the rest.)

pol is light polarization, “s” or “p”.

n_list is the list of refractive indices, in the order that the light would pass through them. The 0’th element of the list should be the semi-infinite medium from which the light enters, the last element should be the semi- infinite medium to which the light exits (if any exits).

th_0 is the angle of incidence: 0 for normal, pi/2 for glancing. Remember, for a dissipative incoming medium (n_list[0] is not real), th_0 should be complex so that n0 sin(th0) is real (intensity is constant as a function of lateral position).

d_list is the list of layer thicknesses (front to back). Should correspond one-to-one with elements of n_list. First and last elements should be “inf”.

lam_vac is vacuum wavelength of the light.

Outputs the following as a dictionary (see manual for details)

  • r–reflection amplitude
  • t–transmission amplitude
  • R–reflected wave power (as fraction of incident)
  • T–transmitted wave power (as fraction of incident)
  • power_entering–Power entering the first layer, usually (but not always) equal to 1-R (see manual).
  • vw_list– n’th element is [v_n,w_n], the forward- and backward-traveling amplitudes, respectively, in the n’th medium just after interface with (n-1)st medium.
  • kz_list–normal component of complex angular wavenumber for forward-traveling wave in each layer.
  • th_list–(complex) propagation angle (in radians) in each layer
  • pol, n_list, d_list, th_0, lam_vac–same as input
tmm.tmm_core.coh_tmm_reverse(pol, n_list, d_list, th_0, lam_vac)[source]

Reverses the order of the stack then runs coh_tmm.

tmm.tmm_core.ellips(n_list, d_list, th_0, lam_vac)[source]

Calculates ellipsometric parameters, in radians.

Warning: Conventions differ. You may need to subtract pi/2 or whatever.

tmm.tmm_core.find_in_structure(d_list, distance)[source]

d_list is list of thicknesses of layers, all of which are finite.

distance is the distance from the front of the whole multilayer structure (i.e., from the start of layer 0.)

Function returns [layer,z], where:

  • layer is what number layer you’re at.
  • z is the distance into that layer.

For large distance, layer = len(d_list), even though d_list[layer] doesn’t exist in this case. For negative distance, return [-1, distance]

tmm.tmm_core.find_in_structure_with_inf(d_list, distance)[source]

d_list is list of thicknesses of layers [inf, blah, blah, ..., blah, inf]

distance is the distance from the front of the whole multilayer structure (i.e., from the start of layer 1.)

Function returns [layer,z], where:

  • layer is what number layer you’re at,
  • z is the distance into that layer.

For distance < 0, returns [0, distance]. So the first interface can be described as either [0,0] or [1,0].

tmm.tmm_core.inc_absorp_in_each_layer(inc_data)[source]

A list saying what proportion of light is absorbed in each layer.

Assumes all reflected light is eventually absorbed in the 0’th medium, and all transmitted light is eventually absorbed in the final medium.

Returns a list [layer0absorp, layer1absorp, ...]. Entries should sum to 1.

inc_data is output of incoherent_main()

tmm.tmm_core.inc_find_absorp_analytic_fn(layer, inc_data)[source]

Outputs an absorp_analytic_fn object for a coherent layer within a partly-incoherent stack.

inc_data is output of incoherent_main()

tmm.tmm_core.inc_group_layers(n_list, d_list, c_list)[source]

Helper function for inc_tmm. Groups and sorts layer information.

See coh_tmm for definitions of n_list, d_list.

c_list is “coherency list”. Each entry should be ‘i’ for incoherent or ‘c’ for ‘coherent’.

A “stack” is a group of one or more consecutive coherent layers. A “stack index” labels the stacks 0,1,2,.... The “within-stack index” counts the coherent layers within the stack 1,2,3... [index 0 is the incoherent layer before the stack starts]

An “incoherent layer index” labels the incoherent layers 0,1,2,...

An “alllayer index” labels all layers (all elements of d_list) 0,1,2,...

Returns info about how the layers relate:

  • stack_d_list[i] = list of thicknesses of each coherent layer in the i’th stack, plus starting and ending with “inf”
  • stack_n_list[i] = list of refractive index of each coherent layer in the i’th stack, plus the two surrounding incoherent layers
  • all_from_inc[i] = j means that the layer with incoherent index i has alllayer index j
  • inc_from_all[i] = j means that the layer with alllayer index i has incoherent index j. If j = nan then the layer is coherent.
  • all_from_stack[i1][i2] = j means that the layer with stack index i1 and within-stack index i2 has alllayer index j
  • stack_from_all[i] = [j1 j2] means that the layer with alllayer index i is part of stack j1 with withinstack-index j2. If stack_from_all[i] = nan then the layer is incoherent
  • inc_from_stack[i] = j means that the i’th stack comes after the layer with incoherent index j, and before the layer with incoherent index j+1.
  • stack_from_inc[i] = j means that the layer with incoherent index i comes immediately after the j’th stack. If j=nan, it is not immediately following a stack.
  • num_stacks = number of stacks
  • num_inc_layers = number of incoherent layers
  • num_layers = number of layers total
tmm.tmm_core.inc_tmm(pol, n_list, d_list, c_list, th_0, lam_vac)[source]

Incoherent, or partly-incoherent-partly-coherent, transfer matrix method.

See coh_tmm for definitions of pol, n_list, d_list, th_0, lam_vac.

c_list is “coherency list”. Each entry should be ‘i’ for incoherent or ‘c’ for ‘coherent’.

If an incoherent layer has real refractive index (no absorption), then its thickness doesn’t affect the calculation results.

See https://arxiv.org/abs/1603.02720 for physics background and some of the definitions.

Outputs the following as a dictionary:

  • R–reflected wave power (as fraction of incident)
  • T–transmitted wave power (as fraction of incident)
  • VW_list– n’th element is [V_n,W_n], the forward- and backward-traveling intensities, respectively, at the beginning of the n’th incoherent medium.
  • coh_tmm_data_list–n’th element is coh_tmm_data[n], the output of the coh_tmm program for the n’th “stack” (group of one or more consecutive coherent layers).
  • coh_tmm_bdata_list–n’th element is coh_tmm_bdata[n], the output of the coh_tmm program for the n’th stack, but with the layers of the stack in reverse order.
  • stackFB_list–n’th element is [F,B], where F is light traveling forward towards the n’th stack and B is light traveling backwards towards the n’th stack.
  • num_layers– total number both coherent and incoherent.
  • power_entering_list–n’th element is the normalized Poynting vector crossing the interface into the n’th incoherent layer from the previous (coherent or incoherent) layer.
  • Plus, all the outputs of inc_group_layers
tmm.tmm_core.interface_R(polarization, n_i, n_f, th_i, th_f)[source]

Fraction of light intensity reflected at an interface.

tmm.tmm_core.interface_T(polarization, n_i, n_f, th_i, th_f)[source]

Fraction of light intensity transmitted at an interface.

tmm.tmm_core.interface_r(polarization, n_i, n_f, th_i, th_f)[source]

reflection amplitude (from Fresnel equations)

polarization is either “s” or “p” for polarization

n_i, n_f are (complex) refractive index for incident and final

th_i, th_f are (complex) propegation angle for incident and final (in radians, where 0=normal). “th” stands for “theta”.

tmm.tmm_core.interface_t(polarization, n_i, n_f, th_i, th_f)[source]

transmission amplitude (frem Fresnel equations)

polarization is either “s” or “p” for polarization

n_i, n_f are (complex) refractive index for incident and final

th_i, th_f are (complex) propegation angle for incident and final (in radians, where 0=normal). “th” stands for “theta”.

tmm.tmm_core.is_forward_angle(n, theta)[source]

if a wave is traveling at angle theta from normal in a medium with index n, calculate whether or not this is the forward-traveling wave (i.e., the one going from front to back of the stack, like the incoming or outgoing waves, but unlike the reflected wave). For real n & theta, the criterion is simply -pi/2 < theta < pi/2, but for complex n & theta, it’s more complicated. See https://arxiv.org/abs/1603.02720 appendix D. If theta is the forward angle, then (pi-theta) is the backward angle and vice-versa.

tmm.tmm_core.layer_starts(d_list)[source]

Gives the location of the start of any given layer, relative to the front of the whole multilayer structure. (i.e. the start of layer 1)

d_list is list of thicknesses of layers [inf, blah, blah, ..., blah, inf]

tmm.tmm_core.list_snell(n_list, th_0)[source]

return list of angle theta in each layer based on angle th_0 in layer 0, using Snell’s law. n_list is index of refraction of each layer. Note that “angles” may be complex!!

tmm.tmm_core.make_2x2_array(a, b, c, d, dtype=<class 'float'>)[source]

Makes a 2x2 numpy array of [[a,b],[c,d]]

Same as “numpy.array([[a,b],[c,d]], dtype=float)”, but ten times faster

tmm.tmm_core.position_resolved(layer, distance, coh_tmm_data)[source]

Starting with output of coh_tmm(), calculate the Poynting vector, absorbed energy density, and E-field at a specific location. The location is defined by (layer, distance), defined the same way as in find_in_structure_with_inf(...).

Returns a dictionary containing:

  • poyn - the component of Poynting vector normal to the interfaces
  • absor - the absorbed energy density at that point
  • Ex and Ey and Ez - the electric field amplitudes, where z is normal to the interfaces and the light rays are in the x,z plane.

The E-field is in units where the incoming |E|=1; see https://arxiv.org/pdf/1603.02720.pdf for formulas.

tmm.tmm_core.power_entering_from_r(pol, r, n_i, th_i)[source]

Calculate the power entering the first interface of the stack, starting with reflection amplitude r. Normally this equals 1-R, but in the unusual case that n_i is not real, it can be a bit different than 1-R. See manual.

n_i is refractive index of incident medium.

th_i is (complex) propegation angle through incident medium (in radians, where 0=normal). “th” stands for “theta”.

tmm.tmm_core.snell(n_1, n_2, th_1)[source]

return angle theta in layer 2 with refractive index n_2, assuming it has angle th_1 in layer with refractive index n_1. Use Snell’s law. Note that “angles” may be complex!!

tmm.tmm_core.unpolarized_RT(n_list, d_list, th_0, lam_vac)[source]

Calculates reflected and transmitted power for unpolarized light.

tmm.color module

Functions to calculate the color of a multilayer thin film under reflected light. A perfect mirror will look white, because we imagine seeing the white light source (“illuminant”) reflected in it. A half-reflective mirror will be gray, a non-reflective surface will be black, etc. See tmm.examples.sample5() for a few example calculations for how this is used.

For functions that require an illuminant, the most common choice would be to use colorpy.illuminants.get_illuminant_D65(), which approximates a phase of natural daylight. See http://en.wikipedia.org/wiki/Illuminant_D65 .

tmm.color.calc_color(spectrum, scale=None, show_warnings=True)[source]

Calculate the color in various representations.

spectrum is the output of calc_spectrum.

scale is the scaling method. Possibilities are:

  • scale=None means don’t scale. This is usually what you want, bucause the illuminant should be pre-scaled in an appropriate way. (Specifically, it’s scaled to get Y=1 for a perfect reflector.)
  • scale=’Y1’ means that the intensity is increased or decreased in order to set Y (the luminance) to 1. So you can get white but not gray, you can get orange but not brown, etc.
  • scale=0.789 multiplies X,Y,Z by 0.789. Any number > 0 is OK.

Returns a dictionary with rgb, irgb, xy, xyY, and XYZ. Definitions:

  • xy, xyY and XYZ are defined as in

    http://en.wikipedia.org/wiki/CIE_1931_color_space

  • rgb is the linear (i.e., proportional to intensity, not gamma-corrected) version of sRGB.

  • irgb is ready-to-display sRGB, i.e. it is clipped to the range 0-1, and gamma-corrected, and rounded to three integers in the range 0-255.

(sRGB is the standard RGB used in modern displays and printers.)

tmm.color.calc_reflectances(n_fn_list, d_list, th_0, pol='s', spectral_range='narrow')[source]

Calculate the reflection spectrum of a thin-film stack.

n_fn_list[m] should be a function that inputs wavelength in nm and outputs refractive index of the m’th layer. In other words, n_fn_list[2](456) == 1.53 + 0.4j mans that layer #2 has a refractive index of 1.53 + 0.4j at 456nm. These functions could be defined with scipy.interpolate.interp1d() for example.

pol, d_list and th_0 are defined as in tmm.coh_tmm ... but d_list MUST be in units of nanometers

spectral_range can be ‘full’ if all the functions in n_fn_list can take wavelength arguments between 360-830nm; or ‘narrow’ if some or all require arguments only in the range 400-700nm. The wavelengths outside the ‘narrow’ range make only a tiny difference to the color, because they are almost invisible to the eye. If spectral_range is ‘narrow’, then the n(400) values are used for 360-400 and n(700) for 700-830nm

Returns a 2-column array where the first column is wavelength in nm (360,361,362,...,830) and the second column is reflectivity (from 0 to 1, where 1 is a perfect mirror). This range is chosen to be consistent with colorpy.illuminants. See colorpy.ciexyz.start_wl_nm etc.

tmm.color.calc_spectrum(reflectances, illuminant)[source]
  • reflectances is the output of calc_reflec_spec()
  • illuminant is a 2D numpy arrays, with one row for each wavelength, with the first column holding the wavelength in nm, and the second column the intensity. This is the form returned by the functions in colorpy.illuminants. It is normally assumed that illuminant is normalized so that Y=1.
tmm.color.plot_reflectances(reflectances, filename='temp_plot.png', title='Reflectance', ylabel='Fraction reflected')[source]

Makes nice colored plot of reflectances. reflectances is the output of calc_reflectances(...)

tmm.color.plot_spectrum(spectrum, filename='temp_plot.png', title='Reflected light under illumination', ylabel='Intensity (a.u.)')[source]

Makes nice colored plot of the reflected color spectrum you see under a certain illuminant. spectrum is the output of calc_spectrum(...)

tmm.examples module

Examples of plots and calculations using the tmm package.

tmm.examples.sample1()[source]

Here’s a thin non-absorbing layer, on top of a thick absorbing layer, with air on both sides. Plotting reflected intensity versus wavenumber, at two different incident angles.

tmm.examples.sample2()[source]

Here’s the transmitted intensity versus wavelength through a single-layer film which has some complicated wavelength-dependent index of refraction. (I made these numbers up, but in real life they could be read out of a graph / table published in the literature.) Air is on both sides of the film, and the light is normally incident.

tmm.examples.sample3()[source]

Here is a calculation of the psi and Delta parameters measured in ellipsometry. This reproduces Fig. 1.14 in Handbook of Ellipsometry by Tompkins, 2005.

tmm.examples.sample4()[source]

Here is an example where we plot absorption and Poynting vector as a function of depth.

tmm.examples.sample5()[source]

Color calculations: What color is a air / thin SiO2 / Si wafer?

tmm.examples.sample6()[source]

An example reflection plot with a surface plasmon resonance (SPR) dip. Compare with http://doi.org/10.2320/matertrans.M2010003 (“Spectral and Angular Responses of Surface Plasmon Resonance Based on the Kretschmann Prism Configuration”) Fig 6a

tmm.tests module

Tests to ensure tmm package was coded correctly. Use run_all() to run them all in order.

tmm.tests.RT_test()[source]

Tests of formulas for R and T

tmm.tests.absorp_analytic_fn_test()[source]

Test absorp_analytic_fn functions

tmm.tests.basic_test()[source]

Compare with program I wrote previously in Mathematica. Also confirms that I don’t accidentally mess up the program by editing.

tmm.tests.coh_overflow_test()[source]

Test whether very very opaque layers will break the coherent program

tmm.tests.df(a, b)[source]
tmm.tests.inc_overflow_test()[source]

Test whether very very opaque layers will break the incoherent program

tmm.tests.incoherent_test()[source]

test inc_tmm(). To do: Add more tests.

tmm.tests.position_resolved_test()[source]

Compare with program I wrote previously in Mathematica. Also, various consistency checks.

tmm.tests.position_resolved_test2()[source]

Similar to position_resolved_test(), but with initial and final medium having a complex refractive index.

tmm.tests.run_all()[source]