Skip to main content

Ultrafast Laser TTM Toolbox: two-temperature model solvers for femtosecond laser heating of metals

Paper DOI DOI CI status Latest release MIT License Python 3.10+, NumPy, SciPy, Numba

Python solvers (package laserttm) for ultrafast pulsed-laser heating of metals, built on the two-temperature model (TTM). The toolbox spans single-pulse electron-lattice dynamics on femtosecond timescales, heat accumulation over thousands of pulses, and moving-beam scans. A two-stage solution strategy keeps multi-pulse simulations fast on a laptop — no MATLAB required.

This is the Python port of the Ultrafast Laser TTM Toolbox, the MATLAB reference implementation for the model published in:

Fieser, D., Dewanjee, U. N., and Hu, A. (2026). A Computationally Efficient Two-Stage Two-Temperature Model for Multi-pulse Femtosecond Laser Heat Accumulation in Tungsten. Journal of Materials Engineering and Performance. doi:10.1007/s11665-026-14738-6

The defaults reflect the tungsten work in the paper, but material presets (W, Cu, Au, Al) and a custom mode support other metals, pulse widths, spot sizes, repetition rates, and scanning conditions.

Highlights

  • Two-stage multi-pulse strategy. Each pulse period splits into a full electron-lattice TTM solve (stiff BDF, or step-identical adaptive RK4 in the 0D solvers) during the pulse and relaxation, then Crank-Nicolson thermal diffusion for the inter-pulse gap. The baseline 50-pulse accumulation run finishes in well under a second.
  • Six solver entry points. 0D surface point, 1D depth-resolved, radial profile, single-pulse visualization, electron-lattice inversion analysis, and a scanning-beam surface model.
  • Validated against the MATLAB reference. Every solver is tested against golden fixtures generated by the MATLAB toolbox: round-off-level agreement (~1e-15 relative) for the kernel-based solvers, integrator-tolerance agreement for the stiff ones. See Validation.
  • Captures the surface temperature inversion (lattice hotter than electrons after the pulse), which requires depth resolution and is a focus of the companion paper.
  • Config-dict interfaces. Every solver accepts a plain cfg dict with the same field names and defaults as the MATLAB cfg structs, and returns a results dict with the same shared v1 field contract, so existing studies translate directly.
  • Pure scientific Python. NumPy + SciPy + Numba (kernels are cached after first compile); figures in matplotlib.

Gallery

All figures below come from the solvers in this repository at their baseline example settings. The generating script is docs/assets/generate_gallery.py.

Electron and lattice surface temperature during one femtosecond pulse in tungsten

Single-pulse electron-lattice dynamics at the tungsten surface (0D solver): the electron bath spikes above 2600 K within the 500 fs pulse, then equilibrates with the lattice through electron-phonon coupling in a few picoseconds.

Multi-pulse heat accumulation in tungsten over 600 pulses

Multi-pulse heat accumulation (0D solver, 600 pulses at 5 MHz): the equilibrated and residual surface temperatures climb pulse by pulse as heat arrives faster than it diffuses away.

Peak surface temperature map for a scanning femtosecond laser beam
Scanning-beam peak-temperature footprint (40 W, 18 MHz, 1 m/s): accumulation along the scan carries the peak past tungsten's 3422 °C melt point.
Radial surface temperature profile under a Gaussian femtosecond laser spot
Residual radial temperature profile after 100 pulses under a Gaussian spot.

Getting Started

Requirements: Python ≥ 3.10. NumPy, SciPy, Numba, and matplotlib are installed automatically.

Install the latest release from PyPI:

pip install laserttm              # add "laserttm[mcp]" for the MCP server

Or, for development, clone the repository and install editable:

git clone https://github.com/dfieser/ultrafast-laser-ttm-py.git
cd ultrafast-laser-ttm-py
pip install -e ".[dev]"           # pytest + ruff + the MCP extra

Pin a released version (pip install laserttm==X.Y.Z) in environments used for real studies, and keep the editable install for development work only.

  1. Run a baseline example:
python examples/run_surface_point.py

Or call a solver directly with your own parameters:

from laserttm import surface_point_solver

cfg = {
    "material": "W",            # tungsten preset ('Cu', 'Au', 'Al', 'custom')
    "Pavg": 10,                 # average power [W]
    "spotRadius": 100e-6,       # 1/e^2 spot radius [m]
    "f_rep": 5e6,               # repetition rate [Hz]
    "tau_FWHM": 500e-15,        # pulse width, FWHM [s]
}
cfg["simDuration"] = 50 / cfg["f_rep"]   # simulate 50 pulses

results = surface_point_solver(cfg)

Every run writes its text output under outputs/ and returns a results dict you can inspect or post-process. The first solver call in a fresh environment takes a little longer while Numba compiles the kernels; they are cached on disk after that.

Suggested progression:

  1. examples/run_surface_point.py for the simplest pulse-accumulation case
  2. examples/run_depth_profile.py for the main 1D depth-resolved model
  3. examples/run_radial_profile.py for radial spread under a Gaussian spot
  4. examples/run_scanning_beam.py for a moving-beam process

The MATLAB repo's project wiki covers the model physics and every config field; the fields carry over one-to-one.

Solvers

Solver Module What it computes Best use
Surface point surface_point.py 0D electron and lattice temperatures at the surface, with inter-pulse depth diffusion fastest pulse-accumulation studies and sweeps
Depth profile depth_profile.py 1D depth-resolved Te(z,t) and Tl(z,t), per-pulse peaks, inversion metrics main multi-pulse workflow; resolves the surface inversion
Radial profile radial_profile.py radial surface temperature under a Gaussian spot ('scale' and 'independent' modes) melt-radius and footprint studies
Single pulse single_pulse.py one pulse with spatial snapshots at chosen delays early-time inspection and teaching figures
Inversion analysis inversion_quantifier.py per-pulse inversion magnitude, onset, and duration statistics quantifying the Tl > Te inversion across pulses
Scanning beam scanning_beam.py 2D surface temperature under a moving beam translating stationary results to a scanned process

All six return a results dict with the shared v1 contract fields: solver, solverId, contractVersion, material, outputFile, outputDir, and inputConfig, plus nPulses and wallTime_s where meaningful — identical to the MATLAB toolbox's contract.

Long runs: the radial solver accepts storeHistory: False to drop the per-pulse time histories (needed only for the timeline figures), which bounds memory for 10⁵-pulse-scale accumulation studies while leaving every physical result unchanged.

Command line

The package installs a laserttm console script for batch and scripted use:

laserttm list                              # available solver ids
laserttm run study.json --out results.json # run a config; also .npz output
laserttm version

A config file is the solver's cfg dict plus a solver key, with the same field names as the Python and MATLAB interfaces:

{
  "solver": "radial_profile",
  "material": "W",
  "Pavg": 70,
  "f_rep": 40e6,
  "spotRadius": 150e-6,
  "simDuration": 2.5e-5,
  "storeHistory": false
}

Plots are off by default in CLI runs; pass --plots or set makePlots/saveFigures in the config.

MCP server

With the mcp extra installed (pip install laserttm[mcp]), the laserttm-mcp script serves every solver as Model Context Protocol tools over stdio, so AI assistants can drive simulations directly. Register it with Claude Code:

claude mcp add laserttm -- laserttm-mcp

Because multi-pulse runs can take minutes, the server uses a job pattern: start_run launches a solver in a background worker process and returns a run id, check_run polls status with a log tail, and get_results returns the results summary once finished (full arrays land in ~/.laserttm/runs/<run_id>/results.npz; override the root with LASERTTM_RUNS_DIR). run_quick wraps the pattern for short runs, and cancel_run terminates a job.

Validation against the MATLAB reference

validation/fixtures/ holds golden fixtures produced by running every solver in MATLAB R2026a (validation/generate_fixtures.m) at fixed configurations, including the MATLAB repo's baseline examples. The test suite re-runs each configuration in Python and asserts agreement:

  • Solvers built on the hand-rolled, step-identical kernels (surface point, radial profile, scanning beam) agree to round-off: relative errors of ~1e-15, and at most 1.6e-10 K across a 36,000-pulse scan.
  • Solvers built on a stiff integrator (depth profile, single pulse, inversion quantifier) run MATLAB ode15s vs SciPy BDF — the same solver family at the same tolerances — and agree at integrator-tolerance level (relative errors ~1e-4 on peak temperatures).
python -m pytest                 # fast suite (~1 min)
python -m pytest -m slow         # + the 36,000-pulse scanning baseline (~9 min)
python validation/benchmark.py   # wall-time comparison vs MATLAB

Performance

Warm-run wall times on the same machine (MATLAB times recorded when the fixtures were generated; details in validation/fixtures/manifest.json):

Case MATLAB Python Speedup
surface_point_baseline (50 pulses) 7.11 s 0.07 s 108×
single_pulse_baseline 1.36 s 0.04 s 32×
radial_profile_baseline (100 pulses) 1.41 s 0.10 s 15×
depth_profile_baseline (100 pulses) 17.60 s 12.9 s 1.4×
scanning_small (3,600 pulses) 29.96 s 22.9 s 1.3×
scanning_baseline (36,000 pulses) 927.2 s 543.6 s 1.7×
inversion_baseline (20 pulses) 4.35 s 6.1 s 0.7×

The depth-solver family currently mirrors the MATLAB integration windows exactly; relaxing the step cap outside the source window (the source is identically zero beyond 10·τ from each pulse center) is a planned optimization for the stiff phase.

Repository Layout

ultrafast-laser-ttm-py/
  src/laserttm/     solver package (shared Numba kernels + six solver modules)
  examples/         editable single-run baseline scripts
  tests/            golden-fixture validation suite (pytest)
  validation/       MATLAB golden fixtures, generator, and benchmark harness
  docs/assets/      banner and gallery figures + generating script
  outputs/          default destination for generated results (untracked)

Documentation

How to Cite

If this toolbox contributes to published work, please cite the article:

@article{fieser2026twostage,
  author    = {Fieser, David and Dewanjee, Unmanaa Nileen and Hu, Anming},
  title     = {A Computationally Efficient Two-Stage Two-Temperature Model for
               Multi-pulse Femtosecond Laser Heat Accumulation in Tungsten},
  journal   = {Journal of Materials Engineering and Performance},
  publisher = {Springer},
  year      = {2026},
  doi       = {10.1007/s11665-026-14738-6},
}

To cite the software itself, use the version DOI from the Zenodo record (concept DOI 10.5281/zenodo.22210435 always resolves to the latest release) or the metadata in CITATION.cff:

@software{fieser_ttm_toolbox_py,
  author = {Fieser, David},
  title  = {Ultrafast Laser TTM Toolbox (Python)},
  year   = {2026},
  doi    = {10.5281/zenodo.22210435},
  url    = {https://github.com/dfieser/ultrafast-laser-ttm-py},
}

The MATLAB reference implementation has its own record (concept DOI 10.5281/zenodo.20389305).

Versioning and Releases

The VERSION file at the repository root is the single source of truth: the package version is read from it at build time ([tool.hatch.version] in pyproject.toml) and exposed at runtime as laserttm.__version__. Every push to main is automatically published as a GitHub release — the release workflow bumps the patch number in VERSION, tags the commit, and publishes; Zenodo then archives the release under a new version DOI, and the same workflow builds the sdist and wheel and publishes them to PyPI via Trusted Publishing (OIDC — no API tokens stored). To jump to a new minor or major version, edit VERSION yourself in your push and that exact version is released instead.

License

Released under the MIT License. See LICENSE.

Acknowledgments and Funding

This work was supported by the National Science Foundation under Award No. CMMI-2412544, Collaborative Research: Additive Manufacturing of Crack-Free Tungsten Using Ultrashort Pulsed Lasers (PI: Dr. Anming Hu, Division of Civil, Mechanical, and Manufacturing Innovation, NSF Program: AM-Advanced Manufacturing).

The authors gratefully acknowledge Drs. Yanfei Gao, Wenda Tan, and Seungha Shin for their contributions and collaboration on this project.

Additional support was provided by the University of Tennessee, Knoxville, through a hiring package. D.F. gratefully acknowledges support from the UTK 100 Talented PhD Scholarship.

Support for the Center for Materials Processing from the State of Tennessee and the Tennessee Higher Education Commission is also gratefully acknowledged.

Contributing

Bug fixes, portability improvements, and documentation polish are welcome. See CONTRIBUTING.md for scope guidance.

Metadata

Release files for laserttm 0.1.5

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

Source distribution (sdist)

Source distribution for laserttm 0.1.5
File Size Uploaded
laserttm-0.1.5.tar.gz 8.9 MB Details

Built distribution (wheel)

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

Total release size: 8.9 MB

Release files / laserttm-0.1.5.tar.gz

Download URL laserttm-0.1.5.tar.gz
Size 8.9 MB
Tags Source
SHA-256 checksum
How to use checksums
be932acc547430aa66917b710a8b6df43cac589e76b36c6da6fbf288f780b278
BLAKE2b-256 checksum
How to use checksums
8c9e9cf1dc780d5284966c77d02440675cffa297d2b1b9a8ed1b9cb5b427e439
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 Aug 31, 2026.

Transparency log

Release files / laserttm-0.1.5-py3-none-any.whl

Download URL laserttm-0.1.5-py3-none-any.whl
Size 70.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b66e10a6850a711f44f68369fbd12e54c8a3abaeb53780a24d3d4fa747330cc9
BLAKE2b-256 checksum
How to use checksums
158cb5b2f0a2506e356dc6715bf1384cf6419d2e67e995f7b3ffbc0b7627f924
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 Aug 31, 2026.

Transparency log
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