Source code for casper.interface.synthetic_functions

import pickle  # nosec B403
from typing import Dict, Tuple

import numpy as np
import pandas as pd
from numpy.typing import ArrayLike
from scipy.interpolate import interp1d

from casper.interface import config
from casper.interface.gisic.normalize import normalize


[docs] def get_interp(): """ Load and return the precomputed spectral interpolation library. This function reads a pickled file containing a precomputed interpolator used for synthetic spectra at a resolution of R~2000. The interpolator is used to estimate model spectra at arbitrary stellar parameters. Returns ------- INTERPOLATOR : object The loaded interpolation object from the pickled file. """ with open("interface/libraries/SYNTHETIC_SPEC_R2000_INTERP.pkl", "rb") as master_lib: INTERPOLATOR = pickle.load(master_lib) # nosec B301 return INTERPOLATOR
[docs] def get_grav_interp(): """ Load and return the pre-computed surface-gravity interpolator. This utility opens the pickled file that stores an isochrone-based interpolator for stellar surface gravity (log g) and returns the loaded object. Returns ------- GRAV_INTERP : object The surface-gravity interpolation object loaded from ``interface/libraries/grav_interp.pkl``. """ with open("interface/libraries/grav_interp.pkl", "rb") as grav_lib: GRAV_INTERP = pickle.load(grav_lib) # nosec B301 return GRAV_INTERP
[docs] def normalize_synth_spectrum(synth_wave: ArrayLike, synth_flux: ArrayLike) -> np.ndarray: """ Normalize a synthetic spectrum using GISIC continuum fitting. This function applies the GISIC normalization algorithm across multiple `sigma` values (defined in the config) to generate continuum fits for a synthetic spectrum. The median of all continuum estimates is used to normalize the input flux. Parameters ---------- synth_wave : array_like Wavelength values of the synthetic spectrum. synth_flux : array_like Corresponding flux values of the synthetic spectrum. Returns ------- synth_norm : ndarray The continuum-normalized synthetic flux array. Flux values are scaled such that the continuum is near unity. Values below 0 or above 2 are clipped to 1.0 for stability. """ cont_array = [] for SIGMA in config.SIGMA: _, _, cont_synth_flux = normalize( synth_wave, synth_flux, sigma=SIGMA, k=config.k, cahk=config.cahk, band_check=config.band_check, flux_min=config.flux_min, boost=config.boost, ) cont_array.append(cont_synth_flux) cont = np.median(np.array(cont_array), axis=0) synth_norm = np.divide(synth_flux, cont) if len(synth_norm[synth_norm < 0.0]) > 1: synth_norm[synth_norm < 0.0] = 1.0 if len(synth_norm[synth_norm > 2.0]) > 1: synth_norm[synth_norm > 2.0] = 1.0 return synth_norm
[docs] def ln_chi_square_sigma(flux: ArrayLike, synth: ArrayLike, xi: ArrayLike) -> float: """ Calculate truncated log chi-square probability distribution function for an absorption feature. The function allows evaluating how well the synthetic spectrum matches the observed one by comparing observed flux values to synthetic (model) flux values using a version of the chi-square formula. The uncertainty is based on the signal-to-noise ratio and is adjusted using the model flux. Parameters ---------- flux : array_like The observed flux values. synth : array_like The synthetic (model) flux values, matched in wavelength to the observed flux. xi : array_like or float The inverse signal-to-noise ratio (1 / SNR). This gets multiplied by the synthetic flux to get the uncertainty for each point. Returns ------- float A number that tells us how likely the model is to match the data. If the result is not valid (e.g., negative), it returns -infinity. """ dof = len(flux) - 1 chi2 = np.square(np.divide(flux - synth, xi * synth)).sum() if chi2 > 0.0: return (0.5 * dof - 1) * np.log(chi2) - 0.5 * chi2 else: return -np.inf
[docs] def CAII_CH_CHI_LH( obs: pd.DataFrame, synth: Dict[str, ArrayLike], CA_BOUNDS: Tuple[float, float], CH_BOUNDS: Tuple[float, float], CA_XI: float, CH_XI: float, ) -> float: """ Calculate how well the synthetic spectrum matches the observed data in the Ca II and CH wavelength regions. Parameters ---------- obs : DataFrame Observed spectrum with "wave" and "norm" columns. synth : dict Synthetic spectrum with: - "wave": array of wavelengths - "norm": array of normalized flux values CA_BOUNDS : tuple Wavelength range for the Ca II region (min, max). CH_BOUNDS : tuple Wavelength range for the CH region (min, max). CA_XI : float Inverse signal-to-noise ratio (1 / SNR) for the Ca II region. CH_XI : float Inverse signal-to-noise ratio (1 / SNR) for the CH region. Returns ------- float The total log-likelihood score for both regions. Higher values mean the synthetic model matches the data better. """ synth_function = interp1d(synth["wave"], synth["norm"]) CA_FRAME = obs[obs["wave"].between(*CA_BOUNDS, inclusive="both")] CH_FRAME = obs[obs["wave"].between(*CH_BOUNDS, inclusive="both")] CHI_CA = ln_chi_square_sigma(CA_FRAME["norm"], synth_function(CA_FRAME["wave"]), CA_XI) CHI_CH = ln_chi_square_sigma(CH_FRAME["norm"], synth_function(CH_FRAME["wave"]), CH_XI) return CHI_CA + CHI_CH