A Python library for optical simulations of multilayer structures using the transfer-matrix method, extended to support nonlinear processes (SHG, SFG, DFG) and Gaussian beam propagation.
See also: GeneralTmm — a 4×4 TMM for anisotropic (birefringent) multilayer structures.
- Standard TMM — reflection, transmission, absorption for p- and s-polarized plane waves at arbitrary angles
- Parameter sweeps — over wavelength, angle of incidence, layer thickness, or any other parameter
- 1D and 2D electromagnetic field profiles — E and H field distributions through the structure
- Field enhancement — calculation of field enhancement factors (e.g. for SPP excitation)
- Gaussian beam propagation — any beam profile through layered structures, not just plane waves
- Second-order nonlinear processes — SHG, SFG, DFG in multilayer structures
- Wavelength-dependent materials — interpolated from measured optical data (YAML format)
- High performance — C++ core (Eigen) with Cython bindings, OpenMP parallelization
- Cross-platform wheels — Linux (x86_64), Windows (x64, ARM64), macOS (ARM64); Python 3.10–3.14
pip install NonlinearTMMPre-built wheels are available for most platforms. A C++ compiler is only needed when installing from source.
The library exposes three main classes: Material, TMM, and SecondOrderNLTMM.
| Class / method | Purpose |
|---|---|
Material(wls, ns) |
Wavelength-dependent material from arrays of λ and complex n |
Material.Static(n) |
Constant refractive index (shortcut) |
TMM(wl=…, pol=…, I0=…) |
Create a solver; wl = wavelength (m), pol = "p" or "s" |
tmm.AddLayer(d, mat) |
Append layer (d in m, inf for semi-infinite) |
tmm.Sweep(param, values) |
Solve for an array of values of any parameter |
tmm.GetFields(zs) |
E, H field profiles along the layer normal |
tmm.GetFields2D(zs, xs) |
E, H on a 2-D grid |
tmm.GetEnhancement(layerNr) |
Field enhancement in a given layer |
tmm.wave |
Access _Wave parameters for Gaussian beam calculations |
tmm.WaveSweep(param, values) |
Parameter sweep for beam calculations |
tmm.WaveGetFields2D(zs, xs) |
2-D field map for beam excitation |
SecondOrderNLTMM(…) |
Second-order nonlinear TMM (SHG, SFG, DFG) |
For the full API, see the reference documentation.
Surface Plasmon Polaritons — ExampleTMM.py
Kretschmann configuration (prism | 50 nm Ag | air) at 532 nm. Demonstrates reflection sweeps, field enhancement, and 1D/2D field visualization of surface plasmon polaritons.
import math
import numpy as np
from NonlinearTMM import TMM, Material
# Materials
prism = Material.Static(1.5)
ag = Material.Static(0.054007 + 3.4290j) # Silver @ 532nm
air = Material.Static(1.0)
# Set up TMM (Kretschmann configuration)
tmm = TMM(wl=532e-9, pol="p", I0=1.0)
tmm.AddLayer(math.inf, prism)
tmm.AddLayer(50e-9, ag)
tmm.AddLayer(math.inf, air)
# Sweep angle of incidence
betas = np.sin(np.radians(np.linspace(0, 80, 500))) * 1.5
result = tmm.Sweep("beta", betas, outEnh=True, layerNr=2)Gaussian Beam Excitation — ExampleTMMForWaves.py
The same Kretschmann structure excited by a 10 mW Gaussian beam (waist 10 μm). Shows how finite beam width affects resonance depth and field enhancement.
Second-Harmonic Generation — ExampleSecondOrderNonlinearTmm.py
Second-harmonic generation (SHG) in a 1 mm nonlinear crystal with
χ⁽²⁾ nonlinearity. Two s-polarized pump beams at 1000 nm generate a
second-harmonic signal at 500 nm. The SecondOrderNLTMM class also supports
sum-frequency generation (SFG) and difference-frequency generation (DFG).
Loot, A., & Hizhnyakov, V. (2017). Extension of standard transfer-matrix method for three-wave mixing for plasmonic structures. Applied Physics A, 123(3), 152. doi:10.1007/s00339-016-0733-0
Loot, A., & Hizhnyakov, V. (2018). Modeling of enhanced spontaneous parametric down-conversion in plasmonic and dielectric structures with realistic waves. Journal of Optics, 20, 055502. doi:10.1088/2040-8986/aab6c0
Full documentation is available at https://ardiloot.github.io/NonlinearTMM/.
- Getting started — installation, package structure, examples
- API reference — complete class and method reference
git clone --recurse-submodules https://github.com/ardiloot/NonlinearTMM.git
cd NonlinearTMM
# Install uv if not already installed:
# https://docs.astral.sh/uv/getting-started/installation/
# Create venv, build the C++ extension, and install all dependencies
uv syncuv run pytest -vPre-commit hooks are configured to enforce formatting (ruff, clang-format) and catch common issues. To install the git hook locally:
uv run pre-commit installTo run all checks manually:
uv run pre-commit run --all-files| Workflow | Trigger | What it does |
|---|---|---|
| Pytest | Push to master / PRs |
Tests on {ubuntu, windows, macos} × Python {3.10–3.14} |
| Pre-commit | Push to master / PRs |
Runs ruff, clang-format, ty, and other checks |
| Publish to PyPI | Release published | Builds wheels + sdist via cibuildwheel, uploads to PyPI |
| Publish docs | Release published | Builds Sphinx docs and deploys to GitHub Pages |
Versioning is handled automatically by setuptools-scm from git tags.
- Ensure CI is green on the
masterbranch. - Create a new release on GitHub:
- Go to Releases → Draft a new release
- Create a new tag following PEP 440 (e.g.
v1.2.0) - Target the
masterbranch (or a specific commit on master) - Click Generate release notes for an auto-generated changelog
- For pre-releases (e.g.
v1.2.0rc1), check Set as a pre-release — these upload to TestPyPI instead of PyPI
- Publish the release — the workflow builds wheels for Linux (x86_64), Windows (x64, ARM64), and macOS (ARM64), and uploads to PyPI.


