Payne Zero

Stellar spectra from
physical models,
in seconds.

Payne Zero implements one-dimensional, plane-parallel Kurucz atmosphere and spectrum calculations under local thermodynamic equilibrium (LTE). 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

Why calculate the physical spectrum directly?

Stellar spectra respond to temperature, gravity, chemical composition, and atmospheric structure together. A change in one element can alter its own lines, blended features, molecular equilibrium, the supply of free electrons, continuum opacity, and sometimes the atmosphere itself. A physical forward model keeps those connections inside the calculation.

Spectral emulators made repeated stellar-parameter fitting practical when atmosphere and synthesis programs were too slow to call for every trial. Payne Zero reorganizes the same physical target for modern multicore CPUs and accelerators. “Zero” means that no learned label-to-flux layer stands between the requested stellar state and its spectrum: opacity and radiative transfer are evaluated directly. A learned model is still useful for predicting the starting atmosphere, while the physical solver decides whether a retained atmosphere has converged.

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.

Bracket notation measures an abundance relative to the Sun on a logarithmic scale: for example, [Fe/H] = −0.5 means one third of the solar iron-to-hydrogen ratio. A difference of 1 dex is a factor of ten. “Alpha enhancement” describes the shared behavior of elements such as O, Mg, Si, S, Ca, and Ti relative to the bulk metal content.

One abundance description for either workflow

Choose the mixture needed by the star

Payne Zero accepts three descriptions of the chemical mixture in 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. Converge the atmosphere before retaining a physical result for this general mixture.

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 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 300000 \
  --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=300_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. Element-by-element fixed-atmosphere synthesis is a preliminary model and is explicitly marked as requiring atmosphere convergence.

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 300000 \
  --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=300_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.

Measured wall-time replay

Explore stellar spectra

Choose a stellar state to replay the campaign's separately measured atmosphere and synthesis wall times. The display interpolates between retained measurements for continuity; it does not run Payne Zero or generate a new spectrum in your browser.

Open full screen ↗

Documentation