Payne Zero

Stellar spectra from
physical models,
in seconds.

Payne Zero implements one-dimensional, plane-parallel LTE Kurucz atmosphere and spectrum calculations. Atmospheres are iterated on multicore CPUs; spectra are synthesized with PyTorch on CUDA, Apple Metal, or CPU. Both model-generation workflows calculate the emergent spectrum from opacity and radiative transfer.

Python 3.11+License BSD 3-ClauseRuns on CUDA · Apple Metal · CPU

Atmospheres and spectra

Begin by choosing how to describe the chemical mixture. Payne Zero uses that same description to initialize the atmosphere in either workflow. You can then synthesize while holding the initialized structure fixed, or iterate the atmosphere to convergence before synthesizing. The abundance choice and the atmosphere treatment are separate decisions.

One abundance description for either workflow

Choose the mixture needed by the star

The three descriptions below are accepted by both the fixed-atmosphere and converged-atmosphere calculations.

Bulk metallicity + alpha pattern

Use a bulk metallicity and shared alpha-element pattern when they adequately describe the star.

metallicity=-0.5, alpha_enhancement=0.2

Independent C, N, and O

Let carbon, nitrogen, and oxygen vary independently for evolved giants and other CNO-sensitive spectra.

c_over_m=-0.25, n_over_m=0.35, o_over_m=0.15

Element-by-element

Start from [Fe/H] and specify the elements that differ from it when a grouped abundance pattern is not sufficient.

fe_over_h=-0.4, x_over_h={"C": -0.65, "N": -0.05}
Physically converged atmosphere

Iterate the chosen mixture, then calculate its spectrum

Use this workflow when the atmospheric structure is a retained result or when the final spectrum must use an atmosphere that has passed the convergence test. The first command solves the atmosphere; the second synthesizes its structured product. The example uses a bulk metallicity and alpha pattern; the independent CNO and element-by-element inputs above can be supplied in the same place.

Command line
shell
payne-zero-atmosphere \
  --effective-temperature 5777 \
  --log-surface-gravity 4.44 \
  --metallicity 0.0 \
  --alpha-enhancement 0.0 \
  --microturbulence-km-s 1.0 \
  --out runs/sun_atmosphere

payne-zero-synthesis \
  runs/sun_atmosphere/payne_zero_structured_atmosphere.npz \
  --wl-start-nm 500 \
  --wl-end-nm 510 \
  --r-grid 20000 \
  --out runs/sun_converged_spectrum.npz
Python API
python
from payne_zero_atmosphere import solve_structured_atmosphere
from payne_zero_synthesis import synthesize

solar_labels = dict(
    effective_temperature=5777,
    log_surface_gravity=4.44,
    metallicity=0.0,
    alpha_enhancement=0.0,
    microturbulence_km_s=1.0,
)

atmosphere_path = solve_structured_atmosphere(
    **solar_labels,
    out_dir="runs/sun_atmosphere",
)

spectrum = synthesize(
    atmosphere_path,
    wavelength_start_nm=500,
    wavelength_end_nm=510,
    resolution=20_000,
    device="auto",
)

spectrum.save_npz("runs/sun_converged_spectrum.npz")
Fixed initialized atmosphere

Synthesize without the atmosphere iterations

Use this workflow for exploration and repeated evaluations when a separately converged atmosphere is not required. Any of the three abundance descriptions predicts a starting structure; synthesis then holds it fixed while rebuilding populations, opacity, and radiative transfer. The saved product records that the atmosphere was initialized rather than converged.

Command line
shell
payne-zero-synthesis \
  --effective-temperature 5777 \
  --log-surface-gravity 4.44 \
  --metallicity 0.0 \
  --alpha-enhancement 0.0 \
  --microturbulence-km-s 1.0 \
  --wl-start-nm 500 \
  --wl-end-nm 510 \
  --r-grid 20000 \
  --out runs/sun_initialized_spectrum.npz
Python API
python
from payne_zero_synthesis import synthesize_from_labels

spectrum = synthesize_from_labels(
    effective_temperature=5777,
    log_surface_gravity=4.44,
    metallicity=0.0,
    alpha_enhancement=0.0,
    microturbulence_km_s=1.0,
    wavelength_start_nm=500,
    wavelength_end_nm=510,
    r_grid=20_000,
    device="auto",
)

spectrum.save_npz("runs/sun_initialized_spectrum.npz")
Continue with the model-generation guide →

What the code implements

Spectrum

Opacity and radiative transfer

Accelerator-parallel synthesis makes the physical forward model practical to call directly. Every spectrum is calculated from atomic and molecular opacity, line profiles, and the LTE transfer equation rather than predicted by a label-to-flux emulator.

Execution

CPU atmosphere; parallel synthesis

Ordered atmosphere passes use compiled multicore CPU kernels. Synthesis batches independent wavelengths and line profiles on CUDA, Apple Metal, or CPU.

Observed spectra

From model wavelengths to observed pixels

Total and continuum flux can be shifted, broadened, passed through an instrument line-spread function, and sampled onto observed pixels before normalization.

Atomic data

Differentiable line parameters

PyTorch differentiates selected oscillator strengths and damping terms through opacity, transfer, broadening, sampling, and the comparison with standard-star spectra.

Kurucz parity and runtime

For four matched stellar controls over 300–1000 nm at R = 300,000, the pooled RMS difference from the Kurucz calculation is below one part in a thousand in normalized flux. This remains true when Payne Zero and the original programs first solve their atmospheres independently. This is a numerical implementation comparison, not a claim of agreement with every observed stellar spectrum.

In the paper's warm repeated-call measurement, the 300–1000 nm solar spectrum took 14 seconds on one NVIDIA H100. Over the narrower APOGEE 1500–1700 nm interval, synthesizing a solar spectrum from the same atmosphere took 1.4 seconds on the H100 at R = 300,000, before instrument convolution or resampling. One physical atmosphere pass took 2.1–5.3 seconds on 16 AMD EPYC threads across the four controls. These timings exclude first-use compilation and depend on wavelength range, R, device, numerical type, and cache state.

Documentation