Tools for interpolating spectral models with neural networks. This package uses neural networks to predict stellar spectra from stellar labels (effective temperature, surface gravity, chemical abundances, etc.) and fit observed spectra to determine stellar parameters.
Clone this repository and install using pip (modern Python packaging):
git clone https://github.com/yourusername/The_Payne.git
cd The_Payne
pip install -e .For development, install in editable mode (recommended):
pip install -e .For a standard installation:
pip install .Note: If you previously used python setup.py install, please use pip install . instead. The old setup.py method is deprecated as of Python 3.12+.
The tutorial.ipynb shows simple use cases for fitting stellar spectra.
- Spectral model and fitting: Numpy and Scipy
- Training neural networks: PyTorch (GPU required for training, optional for prediction/fitting)
- All dependencies will be automatically installed with this package
- Developed in Python 3.7+ using Anaconda
- Compatible with Python 3.8+
spectral_model.py - Neural network prediction
get_spectrum_from_neural_net(): Predicts normalized stellar spectra from scaled stellar labels using a trained neural networkleaky_relu(): Activation function used in the neural networks
fitting.py - Spectral fitting routines
fit_normalized_spectrum_single_star_model(): Fits a single-star model to an observed spectrum using optimization to determine best-fit stellar parameters (Teff, logg, abundances, radial velocity, etc.)
training.py - Neural network training (requires GPU)
neural_net(): Trains a neural network on a grid of synthetic spectra to learn the mapping from stellar labels to spectraPayne_model: PyTorch neural network architecture
utils.py - Utility functions
read_in_neural_network(): Loads pre-trained neural network weights and biasesload_wavelength_array(): Loads the APOGEE wavelength gridload_apogee_mask(): Loads pixel mask for bad pixels during fittingdoppler_shift(): Applies Doppler shift to spectra for radial velocityget_apogee_continuum(): Continuum normalizes APOGEE spectraload_training_data(): Loads Kurucz synthetic training spectra
process_spectra.py - APOGEE data handling
read_apogee_catalog(): Downloads and reads APOGEE allStar catalogget_combined_spectrum_single_object(): Downloads and processes a single APOGEE combined spectrumtoAspcapGrid(): Converts apStar wavelength grid to ASPCAP grid
radam.py - Optimization algorithms
RAdam: Rectified Adam optimizer for neural network trainingPlainRAdam,AdamW: Alternative optimizers
neural_nets/: Pre-trained neural network coefficientsother_data/: APOGEE wavelength grids, masks, and Kurucz training spectra
- PaynePredictor: Easy spectrum prediction from stellar parameters
- PayneFitter: Automated fitting with uncertainty estimation
- PayneTrainer: Simplified neural network training with progress tracking
Use PaynePredictor to generate synthetic spectra from stellar parameters:
from The_Payne import PaynePredictor
# Initialize the predictor (uses default pre-trained network)
predictor = PaynePredictor()
# Or load your custom network: predictor = PaynePredictor(nn_path="my_network.npz")
print(predictor) # Shows model info: labels, pixels, wavelength range
# Get default stellar parameters (mid-range values)
labels = predictor.get_default_labels()
# Customize stellar parameters
labels[0] = 5500 # Teff = 5500 K
labels[1] = 4.5 # logg = 4.5
labels[18] = 0.0 # [Fe/H] = 0.0 (solar metallicity)
# Predict spectrum with radial velocity
spectrum = predictor.predict_spectrum(labels, rv=25.0) # rv in km/s
wavelength = predictor.get_wavelength()
# View label names
label_names = predictor.get_label_names()
print(label_names) # ['Teff', 'logg', 'Vturb', '[C/H]', ...]
# Scale labels for neural network input (if needed)
scaled_labels = predictor.scale_labels(labels)Use PayneFitter to determine stellar parameters from observed spectra:
from The_Payne import PayneFitter, utils
import numpy as np
# Load some example data to use as "observed" spectrum
# (Replace this with your own observed spectrum)
_, _, valid_labels, valid_spectra = utils.load_training_data()
observed_spec = valid_spectra[0] # Use first validation spectrum as example
# Create uncertainty array (adjust based on your data)
spec_err = np.ones_like(observed_spec) * 0.002 # 0.2% uncertainty
# Initialize the fitter (uses default pre-trained network)
fitter = PayneFitter(use_mask=True) # Uses default APOGEE mask
# Or load your custom network: fitter = PayneFitter(nn_path="my_network.npz")
print(fitter) # Shows model info and masked pixel percentage
# Fit the observed spectrum
print("\nFitting spectrum...")
fitted_labels, uncertainties, model_spectrum = fitter.fit_spectrum(
observed_spec, spec_err
)
# Display results
print("\nFitted Parameters:")
print(f"Teff: {fitted_labels[0]:7.1f} ± {uncertainties[0]:5.1f} K")
print(f"logg: {fitted_labels[1]:7.2f} ± {uncertainties[1]:5.2f}")
print(f"[Fe/H]: {fitted_labels[18]:6.2f} ± {uncertainties[18]:5.2f}")
print(f"RV: {fitted_labels[-1]:7.2f} ± {uncertainties[-1]:5.2f} km/s")
# Evaluate fit quality
chi2, chi2_reduced, dof = fitter.compute_chi2(
observed_spec, spec_err, fitted_labels[:-1], fitted_labels[-1]
)
print(f"\nReduced χ²: {chi2_reduced:.3f} (dof = {dof})")
# Get residuals
residuals = fitter.get_residuals(observed_spec, model_spectrum)
print(f"RMS residual: {np.sqrt(np.mean(residuals**2)):.6f}")
# Optional: Customize masking (e.g., mask a telluric region)
fitter.add_mask_region(15890, 15920) # wavelength in Angstroms
# fitter.reset_mask() # Reset to default if neededUse PayneTrainer to train your own models on custom spectral grids:
from The_Payne import PayneTrainer
from The_Payne import utils
# Load training data (or use your own)
train_labels, train_spectra, valid_labels, valid_spectra = utils.load_training_data()
# Initialize trainer
trainer = PayneTrainer(
training_labels=train_labels,
training_spectra=train_spectra,
validation_labels=valid_labels,
validation_spectra=valid_spectra,
num_neurons=300, # neurons per hidden layer
use_cuda=True # use GPU (highly recommended)
)
print(trainer) # Shows training configuration
# Train the network
history = trainer.train(
num_steps=10000, # training iterations
learning_rate=1e-4, # learning rate
batch_size=512, # batch size
save_path="my_network.npz", # where to save best model
verbose=True # print progress
)
# Training automatically saves the best model based on validation loss
# You can also manually save
trainer.save_network("final_network.npz")
trainer.save_training_history("training_history.npz")
# View training progress
import matplotlib.pyplot as plt
plt.plot(history['training_loss'], label='Training')
plt.plot(history['validation_loss'], label='Validation')
plt.xlabel('Step (x100)')
plt.ylabel('Loss')
plt.legend()
plt.show()Using Your Custom-Trained Network:
After training, you can use your custom network for predictions and fitting:
from The_Payne import PaynePredictor, PayneFitter
# Load your custom-trained network for prediction
predictor = PaynePredictor(nn_path="my_network.npz")
labels = predictor.get_default_labels()
labels[0] = 5000 # Teff
spectrum = predictor.predict_spectrum(labels)
# Load your custom-trained network for fitting
fitter = PayneFitter(nn_path="my_network.npz")
fitted_labels, uncertainties, model_spectrum = fitter.fit_spectrum(
observed_spec, spec_err
)See tutorial.ipynb - Jupyter notebook with detailed workflows
- Using pre-trained models: Load neural network → predict spectra for given stellar parameters or fit observed spectra to derive parameters
- Training new models: Prepare training spectra → train neural network on GPU → save network coefficients → use for predictions/fitting
The default model predicts 25 labels:
- Physical parameters: Teff, log(g), microturbulent velocity
- Chemical abundances: [C/H], [N/H], [O/H], [Na/H], [Mg/H], [Al/H], [Si/H], [P/H], [S/H], [K/H], [Ca/H], [Ti/H], [V/H], [Cr/H], [Mn/H], [Fe/H], [Co/H], [Ni/H], [Cu/H], [Ge/H]
- Other: C12/C13, macroturbulent velocity, radial velocity
Problem: "CUDA requested but not available"
Solution: This is just a warning. The code will automatically fall back to CPU. Training on CPU is much slower but will work for small datasets. For production training, use a system with CUDA-compatible GPU.
Please cite Ting et al. 2019 when using this code. The paper describes the method and its application to APOGEE spectra.
- Yuan-Sen Ting -- ting dot 74 at osu dot edu
Copyright 2018 by Yuan-Sen Ting.
This software is governed by the MIT License: In brief, you can use, distribute, and change this package as you please.