TorchFDTD
Open-source GPU FDTD · MIT

Full-wave photonics on the GPU, with gradients.

TorchFDTD simulates Maxwell's equations on NVIDIA GPUs with fused CUDA kernels, returns discrete adjoint gradients through PyTorch autograd, and opens every project in a browser CAD workbench.

pip install torchfdtd
3D electric-field wavefronts focusing above a silicon-pillar metalens, computed with TorchFDTD
Full 3D FDTD: a pulse focusing above a 112-pillar metalens, instantaneous Ex.
Speed

Fused kernels make the time stepping fast.

30–36×faster full solve than Meep 1.34 on 4 MPI ranks
6.4–7.3×faster full solve than FDTDX 0.6.2 on the same GPU
2.0×faster full permittivity gradient than FDTDX's checkpointed adjoint
2.42 Bcells with a full gradient on a 48 GiB GPU, streamed through DRAM

Meep and FDTDX rows: vacuum and sphere scenes at 64³ and 96³, 800 steps, RTX 3060 against Meep on an i7-12700. Conditions, hardware and raw records are in the cross-solver comparison and the measurement record.

Fused CUDA kernels

One launch updates the fields, the CPML and the dispersive terms of every cell, and CUDA Graphs remove the per-step launch cost. Memory traffic stays close to the minimum bytes per cell.

Reversible adjoints

The backward pass rebuilds earlier fields by running the update in reverse, so gradients need only the boundary record instead of every time step.

Beyond GPU memory

Streamed execution moves state between VRAM, DRAM and NVMe with measured reservations and a restart journal for runs that outgrow the card.

Inverse design

Optimize a device in a few lines of PyTorch.

A 2D wavelength splitter: one input guide, a 3 × 3 µm design region and two output guides. The adjoint of both wavelengths comes from one run. After 60 Adam updates on a CPU, the thresholded design sends 97.5% of 1.31 µm light up and 93.2% of 1.55 µm light down.

optimizer.zero_grad(set_to_none=True)
epsilon, density = material(theta, project, beta, background)
t = transmissions(model, epsilon, project, incident)
loss = -objective(t)      # [port, wavelength]
loss.backward()           # discrete Yee/CPML adjoint
optimizer.step()

Run the example

Initial and optimized wavelength splitter with field maps at 1.31 and 1.55 micrometres and transmission history
Thresholded design, |Ez|² at each wavelength and transmission during the optimization.
Paper designs

Freeform metasurfaces at full aperture.

Tiled 3D full-wave adjoints with angular-spectrum propagation design a 200 µm nine-wavelength lens, a color hologram and a polarization-switched hologram. Drivers, configurations and optimized layouts are in the repository.

Nine-wavelength freeform lens: optimization history, dielectric layout and focal fields
E1: a 200 µm freeform lens with a common focus for nine wavelengths from 420 to 670 nm.
Color hologram and polarization-switched hologram with targets, layouts and reconstructed images
E2 and E3: a 450/540/635 nm color hologram and a hologram that switches image with input polarization.
Features

One project, in Python or in the browser.

Cite

Using TorchFDTD in published work

@misc{park2026torchfdtd,
  author        = {Park, Hyoseok},
  title         = {{TorchFDTD}: {GPU}-accelerated finite-difference time-domain simulation with discrete adjoints and host-streamed execution for photonic inverse design},
  year          = {2026},
  eprint        = {2609.30039},
  archivePrefix = {arXiv},
  primaryClass  = {physics.optics}
}

Software archive: 10.5281/zenodo.22928834