Skip to main content

API

fit_gaussians

The main entry point. Decomposes a 1-D signal into Gaussian components.

from phspectra import fit_gaussians

components = fit_gaussians(
signal, # 1-D NumPy array of flux values
*,
beta=3.5, # persistence threshold in units of noise sigma
max_refine_iter=3, # maximum refinement iterations
snr_min=1.5, # minimum amplitude SNR
mf_snr_min=5.0, # minimum matched-filter SNR
f_sep=1.2, # blended-pair separation factor (FWHM units)
neg_thresh=5.0, # negative-dip threshold (RMS units)
)

Parameters

ParameterTypeDefaultDescription
signalNDArray[np.floating](required)1-D spectrum (flux values)
betafloat3.5Persistence threshold in units of σrms\sigma_\mathrm{rms}. The threshold is computed as β×σrms\beta \times \sigma_\mathrm{rms}.
max_refine_iterint3Maximum refinement iterations.
snr_minfloat1.5Minimum amplitude A/σrmsA / \sigma_\mathrm{rms} for a component to survive validation.
mf_snr_minfloat5.0Minimum matched-filter SNR: SNRmf=(A/σrms)σ  π1/4\mathrm{SNR}_\mathrm{mf} = (A/\sigma_\mathrm{rms})\sqrt{\sigma}\;\pi^{1/4}
f_sepfloat1.2Two components are considered blended when μiμj<fsepmin(FWHMi,FWHMj)\lvert\mu_i - \mu_j\rvert < f_\mathrm{sep} \cdot \min(\mathrm{FWHM}_i, \mathrm{FWHM}_j)
neg_threshfloat5.0A negative residual dip deeper than neg_threshσrms\texttt{neg\_thresh} \cdot \sigma_\mathrm{rms} triggers component splitting.

Returns list[GaussianComponent] -- fitted Gaussians sorted by mean position. Returns an empty list when no peaks survive thresholding.

find_peaks_by_persistence

Low-level peak detection via 0-dimensional persistent homology.

from phspectra import find_peaks_by_persistence

peaks = find_peaks_by_persistence(
signal, # 1-D NumPy array
*,
min_persistence=0.0, # discard peaks below this threshold
)

Returns list[PersistentPeak] -- peaks sorted by persistence (most significant first).

estimate_rms

Signal-masked noise estimation following Riener et al. (2019, Sect 3.1.1).

from phspectra import estimate_rms

rms = estimate_rms(
signal, # 1-D NumPy array
*,
mask_pad=2, # padding around masked positive runs
mad_clip=5.0, # clipping threshold in sigma
)

Returns float -- estimated noise RMS (σrms\sigma_\mathrm{rms}).

Types

GaussianComponent

A frozen dataclass representing a single fitted Gaussian.

from phspectra import GaussianComponent
FieldTypeDescription
amplitudefloatPeak height of the Gaussian
meanfloatCentre position (channel units)
stddevfloatStandard deviation (width)

The FWHM of a component is FWHM=22ln2σ2.3548σ\mathrm{FWHM} = 2\sqrt{2\ln 2} \cdot \sigma \approx 2.3548 \cdot \sigma.

PersistentPeak

A frozen dataclass representing a peak detected by persistent homology.

from phspectra.persistence import PersistentPeak
FieldTypeDescription
indexintIndex of the local maximum in the signal
birthfloatFunction value at which this component was born (peak height)
deathfloatFunction value at which it merged into an older component
persistencefloatbirth - death -- the significance of the peak
saddle_indexintChannel index of the saddle point where this component died. Set to -1 for the global maximum (which never dies).

Error handling

The library does not define custom exception classes. All parameters are keyword-only (except signal), so passing positional arguments raises a TypeError.

Both fit_gaussians and estimate_rms raise ValueError if the input signal contains NaN values. Callers must handle missing channels (e.g. np.nan_to_num(signal, nan=0.0)) before calling these functions.

Internally, curve fitting may encounter RuntimeError (non-convergence) or numpy.linalg.LinAlgError (singular Jacobian). These are caught and handled gracefully -- the function returns the best result available rather than raising. If no peaks survive thresholding, fit_gaussians returns an empty list.

The C extension may raise ValueError when the parameter vector exceeds its compiled limit. In this case the library falls back to SciPy's curve_fit automatically.