Skip to main content

pyMSTM

Python bindings for MSTM, the Multiple Sphere T-Matrix code by Daniel Mackowski: electromagnetic scattering by clusters of spheres, including spheres in layered media and in periodic lattices.

pyMSTM compiles MSTM's Fortran sources (the december2023 version) into a Python extension with numpy.f2py. If you use it, please cite MSTM (see Citing).

Installation

pip install pymstm            # or: uv add pymstm
pip install "pymstm[dask]"    # with run_dask() for distributed solves

Wheels are published for Linux x86_64 and CPython 3.11–3.14. On other platforms pip builds the source distribution, which needs a Fortran compiler (gfortran) and a C compiler.

Quick start

MSTM is a validated, self-contained problem description; solve() runs it through the bindings (or, with cli=True, through the MSTM command-line program) and returns an MstmResult:

from pymstm import MSTM

# One sphere, size parameter x = 2*pi*r/lambda = 5, refractive index 1.5
result = MSTM(radii=[5.0], positions=[(0, 0, 0)], ref_re=[1.5], ref_im=[0.0]).solve()
print(result.q_ext_total, result.q_abs_total, result.q_sca_total)

# The same sphere in water (absolute refractive indices)
in_water = MSTM(
    radii=[5.0], positions=[(0, 0, 0)], ref_re=[1.5], ref_im=[0.0],
    medium_ref_re=1.33, compute_mueller=True, n_theta=181,
).solve()
in_water.mueller[0].s11   # phase function (integrates to 4*pi) at theta = 0

MstmBindings is the low-level, stateful interface to everything MSTM offers (layers, periodic lattices, T-matrices, random orientation):

from pymstm import MstmBindings

m = MstmBindings()
m.set_spheres(radii=[5.0, 5.0], positions=[[0, 0, -6], [0, 0, 6]],
              orders=[12, 12], ref_re=[1.5, 1.5], ref_im=[0.01, 0.01])
m.set_incident(alpha_deg=0, beta_deg=30)
m.prepare()
r = m.solve()               # dict: qext_tot, qabs_tot, qsca_tot, q_ext, q_abs, ...
s = m.get_scattering_angle(costheta=0.5, phi=0.0)   # 16 Mueller elements
m.finalize()

MSTM keeps its state in process-global Fortran variables, so only the most recently created MstmBindings can be used; an older instance raises RuntimeError.

Units and conventions

Lengths. MSTM's length unit is 1/k0, with k0 = 2*pi/lambda the vacuum wavenumber: radii and positions are size parameters (x = k0 * r). To give lengths in physical units instead (say nm), call set_length_scale(k0) (k0 in nm⁻¹), or set MstmTask.length_scale; it multiplies radii, positions and lattice cell widths. MstmBindings applies the scale when set_spheres and set_lattice are called, so call set_length_scale first. set_lattice(wx, wy) takes the cell widths in the same units as the radii.

Medium and relative setups. For spheres of index m in a medium of index n, either set the medium (medium_ref_re/medium_ref_im, set_medium_ref, MstmTask.medium_ref_index) and give absolute sphere indices, with lengths in units of 1/k0; or use the relative setup: vacuum medium, sphere indices m/n and lengths scaled by n (length_scale = 2*pi*n/lambda). For a non-absorbing medium both give the same efficiencies (with the default convention below). An absorbing medium needs the medium setup.

Efficiencies are cross sections divided by pi * r**2: for the totals r is the volume-equivalent radius of the external spheres (get_cross_section_radius()), per sphere it is that sphere's radius. With a periodic lattice they are per unit-cell area (projected on the incident direction): qabs_tot is the unit-cell absorptance.

cross_section_convention (MSTM, MstmBindings(...), MstmTask):

  • "physical" (default): cross sections per incident irradiance in the medium. A single sphere gives the Lorenz–Mie efficiencies in the host, and Q_ext tends to 2 for large spheres.
  • "mstm": MSTM's own numbers, as its command-line program prints them. In a medium of index n, MSTM normalises by the vacuum irradiance of the incident field amplitude, so its extinction, absorption and scattering efficiencies (totals, per sphere, per lattice cell) are Re(n) times the physical ones (get_mie_efficiencies(): Re(n)**2). The unit-cell reflectance and absorptance printed by the CLI carry the same factor, and its transmittance is computed as 1 - R - A.

Re(n) is that of the incident medium (layer 0 with plane boundaries), the quantity MSTM itself divides its scattering matrix by. In vacuum both conventions agree. pyMSTM 1.x returned the "mstm" numbers.

Scattering matrix. MstmBindings.get_scattering_angle(costheta, phi) takes angles in the lab frame (the forward direction is (theta, phi) = (beta, alpha) of set_incident) and returns MSTM's raw matrix, flattened row by row. Its S11 integrates over the sphere to C_sca = qsca * pi * r**2 (lengths in units of 1/k0, qsca in the "mstm" convention); in vacuum this is the Bohren–Huffman convention dC_sca/dOmega = S11 / k**2. MstmResult.mueller and s11_to_phase_function give the phase function, normalised to 4*pi, which does not depend on the convention.

Features

  • Fixed orientation: total and per-sphere extinction, absorption and scattering efficiencies; the 4x4 Mueller matrix at any angle.
  • T-matrix of a cluster (compute_tmatrix), and random-orientation scattering matrices from it (ranorient_smatrix).
  • Plane boundaries (layered media) and 2D periodic lattices.
  • solve_task/run_dask for many independent solves on a Dask cluster.
  • The MSTM command-line program as an alternative backend (cli=True, run_mstm), for cross-checks and MPI runs.

Parallel solves (threads, Dask)

MSTM keeps all its state in Fortran module variables, so one process runs one solve at a time. The long Fortran calls release the GIL, and pymstm.MSTM_LOCK serialises whole set -> prepare -> solve sequences (MSTM.solve() and solve_task() take it; hold it yourself when driving MstmBindings from several threads). Parallelism comes from processes:

import math
from dataclasses import replace
from pymstm import MstmTask, solve_task, run_dask

n_host = 1.5
base = MstmTask(
    radii=[5.0, 5.0], positions=[[-6, 0, 0], [6, 0, 0]],  # nm
    ref_index=(2.9 + 4.2j) / n_host, cell=(80.0, 80.0),   # relative setup; cell=None: isolated
    solution_eps=1e-8,
)
tasks = [replace(base, length_scale=2 * math.pi * n_host / wl) for wl in (500.0, 700.0, 1000.0)]

r = solve_task(tasks[0])          # in-process; same numbers as MstmBindings by hand
results = run_dask(tasks, scheduler_address="tcp://scheduler:8786")  # task order
results[0]["qabs_tot"]            # unit-cell absorptance A (lattice); per-sphere in "q_abs"

Each result is a small dict: qext_tot/qabs_tot/qsca_tot, per-sphere q_ext/q_abs/q_sca, mie_orders, t_matrix_order, cross_section_radius, iterations, status (0 = converged), solution_error, wall_time_s. run_dask takes client= (left open), scheduler_address=, or neither (temporary LocalCluster), and progress_callback(fraction, index).

isolate=True (on solve_task and run_dask) runs each solve in a fresh child interpreter in its own temp directory: a Fortran crash becomes a RuntimeError instead of killing the worker, and nothing can leak from one solve into the next. It costs one interpreter start per task (< 1 s), so it is off by default; turn it on for long production runs.

Running on a remote Dask cluster

  1. Install pyMSTM on the workers, with distributed (pip install "pymstm[dask]"), ideally in the image or environment the workers start from. For an unreleased version, build a portable wheel with the Wheels workflow, or locally with uvx cibuildwheel --only cp314-manylinux_x86_64 (it bundles libgfortran; a wheel built with uv build on NixOS links the Nix store copy and is not portable), and install that.
  2. Worker settings: dask worker tcp://scheduler:8786 --nworkers <cores> --nthreads 1. Extra threads per worker only queue on MSTM_LOCK. No worker-ttl change is needed: the GIL is released during solves, so heartbeats continue. Give each worker enough --memory-limit for its largest geometry.
  3. Run run_dask(tasks, client=client, isolate=True); results come back in task order.

The MSTM command-line program (optional)

MSTM(..., cli=True), run_mstm() and the cross-check tests run MSTM's own program. pyMSTM looks for mstm (or mstm-mpi with mpi_processes=) on PATH, or takes binary_path= or the PYMSTM_MSTM_BIN / PYMSTM_MSTM_MPI_BIN environment variables. It must be built from the same december2023 sources:

nix build github:arunoruto/pyMSTM#mstm        # or #mstm-mpi
# or, in a checkout with the submodule:
make cli                                      # -> build/mstm

Programs built from the older code/mstm-v4.0 tree of the MSTM repository normalise S11 differently and write no results for a non-vacuum medium; pyMSTM does not support them.

Development

git clone --recurse-submodules https://github.com/arunoruto/pyMSTM.git
cd pyMSTM
uv sync --extra dask              # builds the extension (needs gfortran)
make cli                           # optional: the CLI for the cross-checks
PYMSTM_MSTM_BIN=build/mstm uv run pytest

The editable install rebuilds the extension on import when a source file changed (meson and ninja from the virtual environment must be on PATH).

Citing

pyMSTM is a wrapper; the method and the Fortran code are MSTM's. Please cite

  • D. W. Mackowski and M. I. Mishchenko, "A multiple sphere T-matrix Fortran code for use on parallel computer clusters", J. Quant. Spectrosc. Radiat. Transfer 112, 2182–2192 (2011), doi:10.1016/j.jqsrt.2011.02.019,
  • for plane boundaries and periodic lattices (MSTM version 4): D. W. Mackowski, "The extension of the Multiple Sphere T Matrix code to include multiple plane boundaries and 2-D periodic systems", J. Quant. Spectrosc. Radiat. Transfer 290, 108292 (2022), doi:10.1016/j.jqsrt.2022.108292,

and pyMSTM itself if it helped (see CITATION.cff).

License

pyMSTM is MIT-licensed (LICENSE). MSTM is MIT-licensed by Daniel Mackowski; its third-party routines and the GNU Fortran runtime libraries bundled in the Linux wheels are listed in LICENSES/.

Metadata

Release files for pymstm 2.0.0

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

Source distribution (sdist)

Source distribution for pymstm 2.0.0
File Size Uploaded Provenance Actions
pymstm-2.0.0.tar.gz 336.2 kB With provenance Details for pymstm-2.0.0.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for pymstm 2.0.0
File Actions
pymstm-2.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details for pymstm-2.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
pymstm-2.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details for pymstm-2.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
pymstm-2.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details for pymstm-2.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
pymstm-2.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details for pymstm-2.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Total release size: 7.0 MB

Release files / pymstm-2.0.0.tar.gz

Download URL pymstm-2.0.0.tar.gz
Size 336.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e06706e3ab7d1b782786a1e7be84efb4eafde4ca72ebf4c9d56ad2bf27fd060c
BLAKE2b-256 checksum
How to use checksums
e54f40e6a6e66e41c5018da70894ada82611e1f7d12d9a99e1ee50089e057214
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 / pymstm-2.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL pymstm-2.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 1.7 MB
Tags CPython 3.14 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
e6e7d77d2ad56199c71bd1591d6f7042fa175b116e9ca3d3be7c9732ab7e7b86
BLAKE2b-256 checksum
How to use checksums
ac4cfe31f5a695a7d2467f376f5d4c83d60f2bbab08f0614be688dec6696e97f
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 / pymstm-2.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL pymstm-2.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 1.7 MB
Tags CPython 3.13 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
8382e8c49af693015c88e722d862f4f37cbc334dd33bdb24a74ba677fa4c7a24
BLAKE2b-256 checksum
How to use checksums
0545022a29c65897324fda30a241abdd6dfc2d36a8be7ed5a1de4c1b9a2ec1b8
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 / pymstm-2.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL pymstm-2.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 1.7 MB
Tags CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
dbdd94ef90e69cab02907cbce871b64228adfb28317dac833b2d6856096cd68b
BLAKE2b-256 checksum
How to use checksums
cf02d88b1a2f7fba48e2ddbe77336d7a52f6dda084534fc377a22ec83e6d6f44
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 / pymstm-2.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL pymstm-2.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 1.7 MB
Tags CPython 3.11 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
5e4ab89075237b531f90847366b3d38af4d4f7daa0f72dd5b30ac70ad27a1ff5
BLAKE2b-256 checksum
How to use checksums
ab7c307c60871cf024a6ab5eb72f161474ae7b34c823dd649665971a73048ba8
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 pymstm 2.0.0

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 5 files in this release have been uploaded with provenance metadata.

Select from 5 files

Attestation for pymstm-2.0.0.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