Skip to main content

tiedfit

Doppler velocities from several emission lines of one ion, fitted together.

All the lines of one ion are formed at practically the same temperature, so they share one Doppler shift. Fitting them with one velocity measures that shift more precisely than the best line alone.

Installation

pip install tiedfit

Optional extras: tiedfit[eis] adds EISPAC for reading Hinode/EIS files, and tiedfit[chianti] adds fiasco for line lists from CHIANTI. fiasco needs a copy of the CHIANTI database; see its documentation.

The method

Each spectral window holds a target line and, where needed, nearby lines that blend with it. Each line is a Gaussian on a constant background. By default the lines in a window share one fitted width. Lines of different ions have different thermal widths, but in instruments such as Hinode/EIS the instrumental width is the larger part of the observed width, so one width per window usually describes the lines well enough to fit the target's position. Where it does not, a line other than the target can be given a fixed width, Line(..., width=0.02) in Angstrom, or a width fitted for it alone, Line(..., width="free"). A free width is best kept for blends that are clearly separated from the target, as a close one can trade width with the target's line.

  • In a single-line fit, each window is fitted alone with its own velocity.
  • In a tied fit, the windows of one ion are fitted at once with one velocity. Each window keeps its own width, background and line amplitudes.
  • Lines of the target's ion, in any window, move with the shared velocity. Blends of other ions move independently, so each gets its own wavelength shift. The shift is limited to max_blend_shift (0.05 Angstrom by default) so that a blend stays near its own rest wavelength and cannot move onto the target line, where the two would no longer be distinguishable.
  • A line of the target's ion can have its intensity fixed as a ratio of the target's, for example a line too close to the target to be fitted on its own.

The fit is weighted least squares (scipy.optimize.curve_fit) with the data uncertainties as weights. The velocity is a parameter of the fit, so its uncertainty comes from the covariance matrix.

A tied fit needs the rest wavelengths of its lines to agree to better than the precision of the velocity. In practice the measured line positions can be offset from one another, by uncertainties in the atomic data, by unresolved blends that move a line's apparent centre, or by errors in the instrument's wavelength calibration across the detector. tiedfit therefore measures these offsets from the data. It fits each line alone and takes the line with the smallest median uncertainty as the reference. The offset of each other line is the median difference between its velocity and the reference's, over the pixels where both are measured to better than 5 km/s. It then moves each line's rest wavelength by its offset and repeats the fits.

The velocities are relative to the reference line's rest wavelength. For instruments without an absolute wavelength reference, zero_along_slit subtracts the median velocity along the slit at each slit position.

Example: Hinode/EIS

from tiedfit import Line, fit_ion
from tiedfit.eis import read_windows

# Five Fe XII lines, with the Fe VIII line that blends with 186.887
fe12 = [
    [Line(186.887, "Fe XII"), Line(186.598, "Fe VIII")],
    [Line(192.394, "Fe XII")],
    [Line(193.509, "Fe XII")],
    [Line(195.119, "Fe XII")],
    [Line(196.640, "Fe XII")],
]

if __name__ == "__main__":
    windows = read_windows("eis_20071211_102542.data.h5", fe12, half_width=0.35)
    result = fit_ion(windows, n_jobs=-1)

    result.calibration.offsets     # rest-wavelength offsets in km/s
    result.tied.velocity           # velocity map in km/s, shape (n_y, n_x)
    result.tied.sigma_velocity     # its uncertainty
    good = result.tied.good()      # reduced chi-squared <= 10, uncertainty <= 200 km/s

n_jobs=-1 fits the pixels in parallel on every CPU the script may use. The extra Python processes import the script, so in a script the fit must sit under if __name__ == "__main__": as here. This is not needed in a notebook or with the default n_jobs=1.

read_windows crops each window to half_width (in Angstrom, default 0.25) either side of its target, and every listed line must lie inside that range. Fe VIII 186.598 is 0.29 Angstrom from Fe XII 186.887, hence half_width=0.35 here.

result.singles holds the single-line fits, and result.inverse_variance() gives the inverse-variance weighted mean of the single-line velocities for comparison.

Example: other instruments

Build a Window for each line from arrays. The data can have any number of leading axes, for example (n_y, n_x) for a raster or (n_t, n_y) for a sit-and-stare series. The wavelength can be one array of shape (n_wavelength,) for every pixel, or have the same shape as the data.

from tiedfit import FitOptions, Line, Window, fit_ion

windows = [
    Window(wavelength_1, counts_1, uncertainty_1, [Line(195.119, "Fe XII")]),
    Window(wavelength_2, counts_2, uncertainty_2, [Line(193.509, "Fe XII")]),
]
options = FitOptions(instrument_fwhm=2.0, max_velocity=100.0)
result = fit_ion(windows, options=options)

Wavelengths are in Angstrom. The data can be in any units, but the uncertainties must be in the same units and should include every source of noise, as they set both the weights and the velocity uncertainty. Spectral pixels whose data or uncertainty are not finite are left out of the fit.

FitOptions sets the instrumental FWHM in pixels (for the starting width), the limits of the line width, the largest velocity and the largest blend shift.

To fit without the calibration, or with your own rest wavelengths, call fit_single and fit_tied directly.

Line lists from CHIANTI

tiedfit.chianti.window_lines lists the lines of an ion and of possible blends near a target line, with their intensities relative to the target from a DEM:

from tiedfit import merge_unresolved
from tiedfit.chianti import read_dem, window_lines

log_t, dem = read_dem("quiet_sun", log_t_min=5.5)
lines = window_lines("Fe XII", 186.887, 0.35, log_t, dem, min_strength=0.05)
lines = merge_unresolved(lines, min_separation=0.035)

merge_unresolved removes lines the instrument cannot separate from a stronger one: blends of other ions within min_separation of the target, and the weaker of any two other lines closer than that. Lines with a ratio are kept. Half the instrumental FWHM is a reasonable min_separation. To keep a close line of the target's ion with its intensity fixed relative to the target instead, give it a ratio, e.g. dataclasses.replace(line, ratio=line.strength), before merging. Use the same half_width here as when reading the data.

By default every ion in CHIANTI is searched for blends. Pass blend_ions to search only some.

Citation

If you use tiedfit in published work, please cite McKevitt and Matthews (in preparation).

Licence

MIT. See LICENSE.

Metadata

Release files for tiedfit 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tiedfit 0.1.0
File Size Uploaded Provenance Actions
tiedfit-0.1.0.tar.gz 29.7 kB With provenance Details for tiedfit-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for tiedfit 0.1.0
File Interpreter ABI Platform Actions
tiedfit-0.1.0-py3-none-any.whl Python 3 none any Details for tiedfit-0.1.0-py3-none-any.whl

Total release size: 55.7 kB

Release files / tiedfit-0.1.0.tar.gz

Download URL tiedfit-0.1.0.tar.gz
Size 29.7 kB
Tags Source
SHA-256 checksum
How to use checksums
55920f545dbdc1c4bef8f65e33bb254d9928cbb944e47b91dbefcac9e3213dcd
BLAKE2b-256 checksum
How to use checksums
3a6cce48858204456cc0c4d30c37ff71b63e894c9f83d7f4799d00f1c0fc3ba9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Release files / tiedfit-0.1.0-py3-none-any.whl

Download URL tiedfit-0.1.0-py3-none-any.whl
Size 26.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
99e171188d2b0def6e72d27a948611fa2968aa79a68697558dff4ebc16b3cdf6
BLAKE2b-256 checksum
How to use checksums
60ce95c3c0d4c26bc8195a115a2fa8b0864681d194329097388f38b7dfb2c8c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

Security for tiedfit 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for tiedfit-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page