SpectrumFit

SpectrumFit is the analyzer that comes with plesty-lib, in plesty.lib.analyzer. It fits every emission line in every spectrum of a SpectrumData (a stack of spectra, one per step of a scan; see Example: SpectrumData), each on a background, and returns the lines’ positions, widths and heights with their uncertainties.

It needs SciPy, from the fit extra (see Install), and says so if it is missing.

Each spectrum in the stack is called a frame. SpectrumData.from_run builds the stack from a run: one frame per step that saved a spectrum, with the values scanned in that step (here the angle hwp_deg) kept as the frame’s coordinates.

from plesty.lib.analyzer import SpectrumFit
from plesty.lib.data import SpectrumData

spectrum = SpectrumData.from_run(run_dir, coordinates=("hwp_deg",))
outputs = SpectrumFit(prominence=250, window=0.05, center_tolerance=0.1)(spectrum=spectrum)
fit = outputs["fit"]                    # SpectrumFitData
fit["center"], fit["area_err"]          # (frames, peaks)
fit.coordinates["hwp_deg"]              # one angle per row
outputs["curve"]                        # the fitted model, nan outside fitted ranges

How it fits:

  1. Lines — peaks="auto" finds them in the average of the selected frames (prominence, distance); a list of positions is used as given, or relocated to the strongest local maximum within tolerance. With average="median" the average is the per-pixel median: a cosmic ray hits one frame and drops out of the median, while in the mean of a few dozen frames it can still stand out as a line.

  2. Groups — each line’s FWHM is estimated on the average; neighbours closer than overlap × the larger FWHM are fitted together.

  3. Range — a group is fitted over window beyond its outer lines, re-centred on each frame’s starting centres so it follows a drifting line; without a window, up to halfway to the neighbouring groups.

  4. Frames — the average seeds the first frame in order; with track, each frame seeds the next, and center_tolerance keeps a line near its seed. A frame whose fit fails is nan.

  5. Quality — reported, never acted on. With track, each frame’s range is centred on the previous frame’s fitted position. When a line is weak in one frame, its fit can move onto a brighter neighbouring spectral line inside that range and stay on it for all later frames. Per line, fit.metadata["diagnostics"] counts the frames that show this, and a FitQualityWarning names every line with a non-zero count:

    Counter

    Counts

    failed

    frames whose fit did not converge (nan)

    no_error

    frames whose centre error could not be estimated

    center_at_bound

    frames with the centre at its window or center_tolerance edge

    width_at_bound

    frames with the FWHM at a width bound (not checked for Voigt)

    jumps

    steps between consecutive frames larger than the line’s FWHM

    outliers

    centres more than 5 robust σ (MAD) from the line’s median

    A steadily drifting line raises none of them. Filter the warning with warnings.simplefilter("ignore", FitQualityWarning) once the counts are read.

Parameter

Default

Meaning

peaks

"auto"

"auto", or line positions in the wavelength unit

model

"gaussian"

"gaussian", "lorentzian" or "voigt"

background

"linear"

"linear" or "constant"

window

halfway to neighbours

Fitted range beyond a group’s outer lines

frames / order

all, in index order

Frames to fit, and the order to fit them in

track

True

Seed each frame with the previous frame’s fit

overlap

2.0

Grouping factor on the FWHM; 0 fits every line alone

prominence / distance

none / 1

Line detection for peaks="auto"

average

"mean"

"mean" or "median" of the frames: where lines are found and the first fit is made

tolerance

none

Relocate given positions to a local maximum this close

width / width_bounds

estimated / none

Initial FWHM and its bounds

center_tolerance

fitted range

Maximum distance of a line from its seed

nonnegative

True

Keep heights at or above zero

Outputs: fit, a SpectrumFitData, and curve, a SpectrumData. Both keep the input spectrum’s provenance and frame coordinates; the parameters enter the provenance stamp.

SpectrumFitData holds every quantity as an array (frames, peaks): center, fwhm, height, area, background_offset, background_slope, each with <name>_err, plus r_squared, with nan where a frame’s fit failed. frame names the spectrum frame of each row, x_ref the position each background offset refers to, and coordinates the rows’ sweep values. Units follow the spectrum (area in intensity × wavelength). fit["center"] reads an array; fit.peak(i) gives one line’s quantities over the frames.

Analyzers that fit something other than a spectrum stack — a line centre across a polarization sweep, say — build on the same primitives: fit_spectrum(x, y, [Peak(...), ...], model, background, sigma=...) fits several lines with bounds and per-point errors and returns a SpectrumFitResult (per line center, fwhm, height, area, each ± error; background; r_squared; evaluate(x)); fit_peak is its one-line form.