Skip to main content

PyStratify

PyPI Python tests DOI License: GPL v3+

Light scattering and emission by multilayered films, cylinders and spheres, in Python. One modal-response core solves all three geometries under plane waves, electric and magnetic point dipoles and focused (Cassegrain) beams, with numerics rebuilt so that every quantity stays finite and accurate: thin metal shells, multipole orders in the thousands, emitters a fraction of a nanometre from a surface.

Reflectance of a Bragg mirror, scattering by a silicon nanowire, and the multipoles of a gold nanoshell, computed with PyStratify

PyStratify is the engine of multilayer.app, a free browser calculator for the same three geometries, with a theory guide and every comparison with other codes plotted.

Why PyStratify

  • Three geometries, one method. The transfer-matrix method for films (after Byrnes's tmm, whose function names it keeps), its cylindrical analogue for coated wires and fibres, and for spheres the recursive transfer-matrix method of Moroz (2005) as used by STRATIFY (Rasskazov, Carney & Moroz, OSA Continuum 3, 2290 (2020)).
  • Sources beyond the plane wave. Electric and magnetic dipoles inside or outside the structure, with decay rates split into radiated, guided and absorbed power; focused beams as in FT-IR microspectroscopy.
  • Spheres go further. Absorbing, magnetic, gain and chiral (Pasteur) shells; 2D materials and Feibelman d-parameters on the interfaces; electric, magnetic, chiral and quadrupole emitters; angle-resolved far fields, near fields, stored energy, Casimir–Polder potentials and two-point Green's dyadics.
  • Accurate where others break down. Special functions as complex logarithms and a scaled two-sided recursion keep 1e-12 agreement with 60-digit references, energy balance to 1e-14 at 0.05 nm from a lossless interface, and absorbing spheres to size parameter 20 000 (docs/NUMERICS.md).
  • Checked in public. Every comparison with another code is a script in benchmarks/ that anyone can rerun (below).
  • Light to install. Pure Python on NumPy and SciPy; whole spectra are vectorised.

Install

pip install pystratify

Python ≥ 3.10, NumPy ≥ 1.24, SciPy ≥ 1.10. The development version: pip install git+https://github.com/multilayerapp/PyStratify.git.

Quick start

Lengths share one unit of your choosing (here nanometres), angles are in radians, and indices are n + ik. No optical constants are shipped: take them from a database such as refractiveindex.info.

Films. The planar module keeps the tmm interface: here a quarter-wave Bragg mirror, 8 pairs of TiO₂ / SiO₂ on glass.

import numpy as np
import pystratify as ps
from pystratify import planar

n_list = [1.0] + [2.35, 1.46] * 8 + [1.52]                    # incident medium, layers, substrate
d_list = [np.inf] + [600 / 4 / 2.35, 600 / 4 / 1.46] * 8 + [np.inf]
R = [planar.coh_tmm("s", n_list, d_list, 0.0, wl)["R"] for wl in np.linspace(400, 900, 501)]

Cylinders. A silicon nanowire of radius 80 nm at normal incidence, both polarisations.

wire = ps.solve_cylinder([80.0], [3.6, 1.0], 775.0)            # radii, indices (host last), wavelength
ps.cross_widths(wire, "axial-electric")["efficiency_scattering"]
ps.cross_widths(wire, "axial-magnetic")["efficiency_scattering"]

Spheres. A silica–gold nanoshell in water over a whole spectrum, with its multipole decomposition.

wavelength = np.linspace(450, 1100, 651)
n_gold = 0.2 + 3.5j                                           # use tabulated n, k for real work
n = np.stack([np.full(651, 1.45), np.full(651, n_gold), np.full(651, 1.33)], axis=1)
sol = ps.solve([60.0, 75.0], n, wavelength)                   # radii of every interface, n per shell
cs = ps.cross_sections(sol)
cs.q_ext, cs.q_sca, cs.q_abs                                  # efficiencies over wavelength
cs.ext_by_order[:, ps.TM, 0] / cs.geometric                   # the electric dipole's share

Emitters. Decay rates of a dipole beside a sphere, and above a silver mirror through the unified solve_problem interface, which takes the same arguments for all three geometries.

rates = ps.decay_rates([40.0], [n_gold, 1.0], 600.0, r=40.0 + np.geomspace(0.2, 100, 60))
rates.total, rates.radiative, rates.nonradiative               # (positions, [radial, tangential])

mirror = ps.Problem("films", [np.inf, 200.0, np.inf], [1.0, 0.135 + 3.99j, 1.52], 633.0,
                    ps.PointDipole(-100.0, "electric", layer=0))  # 100 nm above 200 nm of silver on glass
result = ps.solve_problem(mirror)
result["total"], result["escape"], result["absorbed"]          # perpendicular, parallel, isotropic

Decay rates of a dipole beside a gold sphere and above a silver mirror

Focused beams. An FT-IR objective (NA 0.5, central obscuration 0.23) on a polymer fibre.

beam = ps.FocusedBeam(0.5, 0.23)                              # NA, obscuration NA
wavenumber = np.linspace(1000, 4000, 61)                      # cm⁻¹
fibre = ps.focused_cylinders([5.0], [1.49 + 0.01j, 1.0], 1e4 / wavenumber, beam)   # lengths in µm here
fibre.apparent_absorbance

Far more is in the user guide: conventions, every source and output, chiral media, 2D sheets and the full sphere toolbox. The runnable scripts in examples/ (multipole spectra, scattering patterns, near-field maps, decay rates, circular dichroism, quadrupole emitters, graphene-coated spheres…) each save a figure, and crosscheck/ recomputes figures of classic papers from their stated parameters.

Verified against independent codes

PyStratify's largest differences from sixteen independent references, and a dipole beside a fibre against 3D FDTD

Codes that solve the same equations exactly agree with PyStratify to between 1e-15 and 2e-6, the larger differences being the reference's own precision (float32 in tmmax, a contour integral in smuthi). Solvers that discretize space agree to their mesh or grid error, 0.7–4%; for Meep and MNPBEM the difference was shown to shrink as the grid or mesh is refined. Each point is a script in benchmarks/ that writes its own report, and python benchmarks/verify_all.py reruns the whole study in pinned environments (benchmarks/README.md). MATRIX.md maps every check by geometry, source and quantity, and the Verification page plots each one.

Speed

Typical timings on one core:

task time
601-wavelength spectrum, gold nanoshell 25 ms
sphere of size parameter 500, all orders 8 ms
decay rates at 100 positions 0.5–25 nm from a gold nanoshell 0.15 s
scattering pattern on a 181 × 361 (θ, φ) grid 10 ms
300 × 300 near-field map 1.4 s

More, and the accuracy to expect in every regime: docs/NUMERICS.md.

Documentation

User guide the problem description, focused beams, the sphere toolbox, conventions
API contract which names are stable for 1.x, provisional, legacy or internal
Numerics how accuracy is kept, accuracy to expect, known limitations, timings
Benchmarks every comparison with another code, and how to rerun it
Tests what each test file checks
Changelog what changed in each release
Audit of STRATIFY the defects of the MATLAB code that PyStratify fixes
Learn the theory, from Maxwell's equations to each geometry, with live figures

PyStratify is before 1.0: the names listed as frozen in the API contract keep their meaning from 1.0 on. Questions and bug reports are welcome in issues.

Tests

pip install -e ".[test]"
pytest

Citing

PyStratify implements the method of STRATIFY; please cite the paper,

I. L. Rasskazov, P. S. Carney and A. Moroz, "STRATIFY: a comprehensive and versatile MATLAB code for a multilayered sphere," OSA Continuum 3, 2290 (2020), doi:10.1364/OSAC.399979,

and the software itself: doi:10.5281/zenodo.23287561 names every version and resolves to the latest; each release also has its own DOI on that Zenodo page, which is the one to cite for the exact code you ran (CITATION.cff).

Licence

GPL-3.0-or-later, as STRATIFY (this is a derivative work). The planar routines derive from Byrnes's tmm and keep its MIT notice (src/pystratify/PLANAR-LICENSE.txt).

Metadata

Release files for pystratify 0.10.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pystratify 0.10.6
File Size Uploaded Provenance Actions
pystratify-0.10.6.tar.gz 249.0 kB With provenance Details for pystratify-0.10.6.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pystratify 0.10.6
File Interpreter ABI Platform Actions
pystratify-0.10.6-py3-none-any.whl Python 3 none any Details for pystratify-0.10.6-py3-none-any.whl

Total release size: 457.3 kB

Release files / pystratify-0.10.6.tar.gz

Download URL pystratify-0.10.6.tar.gz
Size 249.0 kB
Tags Source
SHA-256 checksum
How to use checksums
da4b674a7598d881adef9b7573478994f9f0ae3b4bfc1f0a08e4069f48505f26
BLAKE2b-256 checksum
How to use checksums
3d0a488f437e3fbc3ce062c2b88244a56f0df094a6136abfeb7009ddb6a12fb8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / pystratify-0.10.6-py3-none-any.whl

Download URL pystratify-0.10.6-py3-none-any.whl
Size 208.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b5cff8e6cf176222797e1a82f904be4898ccd37135a7143ba4ee6240d7caac28
BLAKE2b-256 checksum
How to use checksums
f789bba1d47dc4c335b205d75842b2884b491ac44e68d820c78d001c318cf770
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Security for pystratify 0.10.6

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pystratify-0.10.6.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page