specsr.linefit

Gaussian emission-line fitting, and the S/N derived from it.

Fits a Gaussian on a linear continuum in a window around each expected line and reports amplitude over continuum scatter. This is the measurement behind the paper’s S/N figure.

The continuum scatter is estimated from sidebands — an annulus around the line, excluding its core — rather than from the whole window. Using the window would fold the line itself into the noise estimate and depress the S/N of exactly the strong lines the figure is about.

Note what this quantity is and is not: it references only the spectrum being measured, never the HR truth, so a high S/N means “a confident detection of something”, not “the right line flux”. Establishing that a line is correct needs the reference, which is why specsr.plotting.plot_line_flux_comparison() exists alongside it. That figure takes its fluxes from this same fit (measure_line_fluxes()).

Functions

fit_hbeta_oiii(wavelength, flux, z, *[, ...])

Joint fit of H-beta and the [O III] doublet: three Gaussians, one width.

fit_line_sideband_weighted(x, y, mu0, *[, ...])

Fit one line.

gauss_lin(x, amp, mu, sigma, c0, c1)

Gaussian on a linear continuum.

line_flux_from_fit(fit)

Integrated flux of the fitted Gaussian, sqrt(2 pi) * amp * sigma.

line_snr_from_fit(fit)

(amp/sigma_cont, amp/amp_err) from a fit, or (nan, nan).

mad_sigma(y)

Robust scatter via MAD, NaN-aware.

measure_line_fluxes(wavelength, z, spectra, ...)

Per-line Gaussian-fit flux and S/N for several spectrum sets.

measure_line_snr(wavelength, z, spectra, ...)

Per-line S/N for several spectrum sets over the same objects.

specsr.linefit.fit_hbeta_oiii(wavelength, flux, z, *, window_kms=20000.0, sigma_lo_kms=40.0, sigma_hi_kms=3000.0)[source]

Joint fit of H-beta and the [O III] doublet: three Gaussians, one width.

A single Gaussian cannot measure these lines at prism resolution. Centred on H-beta it widens until it holds the [O III] lines as well (fitted sigma ~8000 km/s for half the sample, and ten times the true flux); centred on 5007 it absorbs 4959 and reads a third high. Fitting the three together at fixed centres with a shared width assigns the blended flux to the right line. It is the model scripts/doublet_deblending.py uses for the doublet ratio, over a wider window so that the prism’s H-beta wing and the continuum beyond it are inside the fit.

The fit runs in velocity about the doublet midpoint, with amplitudes bounded non-negative, for the reasons given there.

Returns {"amp": (A_hb, A_4959, A_5007), "sigma_um": (...), "sigma_kms": float}, with each sigma_um the shared velocity width at that line’s wavelength, or None when the lines fall off the grid or the fit fails.

Parameters:
specsr.linefit.fit_line_sideband_weighted(x, y, mu0, *, fit_halfwin=0.25, core_halfwin=0.05, sb_gap=0.03, sb_width=0.12, sigma_bounds=(0.001, 0.12), mu_bounds_half=0.01, allow_negative_amp=True, maxfev=40000)[source]

Fit one line. Returns a dict of parameters, or None if unfittable.

Negative amplitudes are allowed by default: forcing positivity would turn a non-detection into a small positive bump and manufacture signal where there is none.

Parameters:
specsr.linefit.gauss_lin(x, amp, mu, sigma, c0, c1)[source]

Gaussian on a linear continuum.

specsr.linefit.line_flux_from_fit(fit)[source]

Integrated flux of the fitted Gaussian, sqrt(2 pi) * amp * sigma.

In the units of flux times wavelength of the spectrum that was fitted, and NaN for a failed fit. A negative amplitude gives a negative flux: it is a measurement of no emission, and is left to the caller to count.

specsr.linefit.line_snr_from_fit(fit)[source]

(amp/sigma_cont, amp/amp_err) from a fit, or (nan, nan).

specsr.linefit.mad_sigma(y)[source]

Robust scatter via MAD, NaN-aware. NaN when there is too little data.

specsr.linefit.measure_line_fluxes(wavelength, z, spectra, scales, lines_rest_um, line_names=None, **fit_kw)[source]

Per-line Gaussian-fit flux and S/N for several spectrum sets.

The flux counterpart of measure_line_snr(), from the same fit, so the flux of a line and its S/N are two readings of one measurement.

Parameters:
  • spectra – {"LR": array, "SR": array, "HR": array, ...}, each (n, n_lambda), in the normalised flux the fit’s starting values and bounds are tuned for.

  • scales – {kind: (n,) array}, the per-spectrum factor that turns each set’s normalised flux back into physical flux density. An additive offset needs no undoing: the fit carries its own continuum.

Returns:

  • {f"{line}_{quantity}_{kind}": array} for the quantities flux

  • (physical flux density times the wavelength unit), sn, sigma and

  • mu (both in the wavelength unit). NaN where a line falls off the grid

  • or the fit fails.

specsr.linefit.measure_line_snr(wavelength, z, spectra, lines_rest_um, line_names=None, **fit_kw)[source]

Per-line S/N for several spectrum sets over the same objects.

Parameters:
  • spectra – {"LR": array, "SR": array, "HR": array, ...}, each (n, n_lambda).

  • lines_rest_um – Rest wavelengths, microns. Redshifted per object with z.

Returns:

  • {f"{line}_sn_{kind}": array} with NaN where a line falls off the grid or

  • the fit fails — left as NaN rather than zero, since “not measurable” and

  • ”measured as zero” are different statements.