Skip to main content

PyDoppler

PyDoppler is a light-weight Python package for Doppler tomography of single emission-line datasets, based on Henk Spruit's maximum-entropy Doppler tomography code. It can produce a trail spectrum of a dataset and compute and plot Doppler tomography maps.

Version 1.0 is a pure Python (NumPy/SciPy) implementation. No Fortran compiler is needed any more. A full run on the test dataset takes about 5 seconds.

The code is able to:

  • Load and normalise spectra
  • Plot a trail spectrum at the initial phase resolution and binned
  • Run Doppler tomography and plot the reconstructed spectra
  • Run entirely in Python, with no Fortran compiler
  • Auto-save figures
  • Perform initial spectra binning into user-defined phase bins
  • User-friendly functions

The original code and IDL interface can be found at:

If you have any queries, comments or bug reports, please send an e-mail to:

  • jvhs1 (at) st-andrews.ac.uk

Acknowledgment

If you make use of this software, please acknowledge the original code and this repository with the following citations:

Requirements & Installation

Python >= 3.8 is required, together with numpy, scipy and matplotlib (version 3.5 or newer). These are installed automatically.

You can download and install PyDoppler via pip. In a terminal command line, just type:

pip install pydoppler

If you need to upgrade the package to the latest version, you can do this with

pip install --upgrade pydoppler

To install the development version from a clone of this repository:

pip install -e .

Section 1: Usage

There are two ways to learn the package:

  • Tutorial notebook: notebooks/PyDoppler_tutorial.ipynb walks through every step with explanations, all the parameters and the outputs.
  • Sample script: pydoppler.test_data() copies sample_script.py into your working directory together with the test data. It runs all the commands shown in sections 2 and 3. From a terminal:
    python sample_script.py
    
    or in an (i)python console:
    run sample_script.py
    
    This reads all the files, normalises the spectra, performs Doppler tomography and saves the results. The following sections briefly explain each main routine.

Section 2: How to load data

Section 2.1: Test case - the accreting white dwarf - U Gem

You can start testing PyDoppler with a test dataset kindly provided by J. Echevarria and published in Echevarria et al. 2007, AJ, 134, 262 (https://ui.adsabs.harvard.edu/abs/2007AJ....134..262E/abstract). To copy the data to your working directory, open a (i)python console and run the following commands:

import pydoppler

pydoppler.test_data()

This creates a subdirectory called ugem99 in your current working directory. It contains a text file for each spectrum (txhugem40*), and also copies sample_script.py. Each spectrum file has two columns: Wavelength and Flux.

  • Wavelength is assumed to be in Angstrom.
  • Don't use headers in the files, or start the line with a # so it is ignored.

In addition, a phase file (ugem0all.fas) is placed inside the ugem99 directory. It lists the name of each spectrum file and its orbital phase, in two columns with the following format:

  txhugem4004 0.7150
  txhugem4005 0.7794
         .
         .
         .

Section 2.2: Load your data

I recommend sticking to the same file format as the test dataset:

  • Individual spectra. Two-column files, space separated: Wavelength Flux
  • Phase file. Two-column file, space separated: Spectrum_name Orbital_Phase

and the following directory tree, so that PyDoppler works properly:

wrk_dir
└── data_dir (your target)
    │
    ├── individual_spectra (N spectra)
    │
    └── phases_file

Section 3: Doppler tomography tutorial

Before running any routines, make sure you have added all the relevant parameters to the PyDoppler object.

import pydoppler
import matplotlib.pyplot as plt

# Load base object for tomography
dop = pydoppler.spruit()

# Basic data for the tomography to work
dop.object = 'U Gem'
dop.base_dir = 'ugem99'     # Base directory for input spectra
dop.list = 'ugem0all.fas'   # Name of the phase file
dop.lam0 = 6562.8           # Wavelength zero in units of the original spectra
dop.delta_phase = 0.003     # Exposure time in terms of orbital phase
dop.delw = 35               # Size of the Doppler map in wavelength, centred at lam0
dop.overs = 0.3             # Between 0-1, undersampling of the spectra. 1 = full resolution
dop.gama = 36.0             # Systemic velocity in km/s
dop.nbins = 28              # Number of bins. Only the number of spectra is supported at the moment

Section 3.1: Read the data

This routine reads in the raw data and prepares it for further processing.

# Read in the individual spectra and orbital phase information
dop.Foldspec()
→  Found 28 spectra in ugem99/ugem0all.fas
→  Phase bins: 28
[██████████████████████████████] 100%  txhugem4031  φ=0.1372  npix=2048
✓  Loaded 28 spectra, 2048 pixels each

Section 3.2: Normalise the data

You need to define a continuum band on each side of the emission line, which is used to fit and subtract the continuum.

# Normalise the spectra
dop.Dopin(continnum_band=[6500, 6537, 6591, 6620],
          plot_median=False, poly_degree=2)

This saves Average_Spec.png (the average spectrum, continuum fit and the velocity window of the map) and Trail.png (the trail spectrum).

Section 3.3: Run the Doppler tomography

The maximum-entropy reconstruction runs in Python.

# Perform tomography
dop.Syncdop()
Syncdop — MEM Doppler tomography  (native Python)
────────────────────────────────────────────────────────────
→  Map grid:    144 × 144 pixels
→  Input data:  28 phases × 477 velocity channels
→  Projection matrix: sparse, ~29 MB

Alpha step           α     nit         χ    target
──────────  ──────────  ──────  ────────  ────────
✓  α=2.00e-03  nit=  28  χ=1.5233  (target ≤ 1.60)

✓  Converged!  χ = 1.5233  (target ≤ 1.60)  [0.7 s]
→  Final α = 2.00e-03   iterations = 28
→  Entropy S = -2.5016e+04

The map size is set automatically from dop.overs. Pass nv= to Syncdop() to choose it yourself (e.g. dop.Syncdop(nv=100)).

Section 3.4: Plot the tomography map

This routine displays the result of the Doppler tomography. You can overplot contours and streams.

# Read and plot the map (defaults: linear stretch, magma_r colour map)
fig, data = dop.Dopmap(colorbar=True)

# Overplot the donor contours, keplerian and ballistic streams
qm = 0.35             # mass ratio M_2 / M_1
k1 = 107              # semi-amplitude of the primary in km/s
inc = 70              # inclination angle, in degrees
m1 = 1.2              # mass of the primary in solar masses
porb = 0.1769061911   # orbital period in days

pydoppler.stream(qm, k1, porb, m1, inc,
                 tidal_radius=True, resonance_32=True, resonance_21=True)

Useful Dopmap options: stretch='linear' | 'sqrt' | 'log', cmaps=plt.cm.afmhot (or any matplotlib colour map), limits=[lo, hi], percentile_lo / percentile_hi and smooth=True. With dark colour maps, pass colors='white' to stream().

Section 3.5: Spectra reconstruction

Always check that the reconstructed spectra look like the original ones. A good rule of thumb: "If a feature on the Doppler tomogram is not in the trail spectrum, most likely it's not real!"

# Plot the observed and reconstructed trail spectra
fig, dm, dmr = dop.Reco(colorbar=True, limits=[0.05, 0.95])

Here fig is the figure, and dm and dmr hold the input and reconstructed trail spectra (phase × velocity, baseline removed), respectively.

Section 4: Extra commands

Many other parameters control the Doppler tomogram. As usual, you can set them on the pydoppler.spruit object, as in Section 3. The configurable parameters are:

dop.ih = 0            # type of likelihood function (ih=1 for chi-squared)
dop.iw = 0            # iw=1 if error bars are to be read and used
dop.pb0 = 0.95        # range of phases to be ignored, between 0 and 1
dop.pb1 = 1.05        # range of phases to be ignored, between 1 and 2
dop.ns = 7            # smearing width in default map
dop.ac = 8e-4         # accuracy of convergence
dop.nim = 150         # max no of iterations
dop.al0 = 0.002       # starting value of alpha
dop.alf = 1.7         # factor between successive alphas
dop.nal = 0           # max number of alphas (0 = single alpha)
dop.clim = 1.6        # 'C-aim', target reduced chi
dop.norm = 1          # norm=1 for normalisation to a flat light curve
dop.overs = 0.3       # map oversampling (sets the map size)

What's new in version 1.0

  • No Fortran. The Fortran dop.f engine and the make/gfortran step are replaced by pydoppler/mem_engine.py, a NumPy/SciPy implementation of the same algorithm (Lucy-accelerated Richardson-Lucy iteration with a floating-default maximum-entropy regularisation).
  • Fast. The projection matrix is built in vectorised form and stored as a sparse matrix. On the U Gem dataset, the tomography step takes about 0.7 s and the complete sample script about 5 s.
  • Checked against Spruit's dop.f. The projection geometry, default-map smoothing, entropy and its derivatives, baseline handling and alpha schedule follow the Fortran routines (prom, chimg, entr, lamun, basel, alfs). A point-source test recovers the line-of-sight velocities to 0.000 km/s, with no velocity-channel offset.
  • Safer alpha schedule. When the fit stops improving (at the noise floor of the data), the alpha sequence falls back to geometric steps instead of diverging.
  • Plots. New defaults (linear stretch, magma_r), phases increase upwards in the reconstruction plot, and all routines save their figures.

Development notes

Version 1.0, including the conversion from Fortran to native Python, the performance optimisation and the line-by-line validation against Spruit's dop.f, was developed with the help of Claude, Anthropic's AI assistant, using Claude Opus 5.5 (model claude-opus-5-5) in Claude Code.

Section 5: Troubleshoot

If you find a bug or need a feature, I will try my best to work it out. If you think you can add to this package, I encourage you to push new changes and contribute to it.

Metadata

Release files for pydoppler 1.0.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 pydoppler 1.0.0
File Size Uploaded Provenance Actions
pydoppler-1.0.0.tar.gz 746.0 kB Without provenance Details for pydoppler-1.0.0.tar.gz

Built distribution (wheel)

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

Total release size: 1.5 MB

Release files / pydoppler-1.0.0.tar.gz

Download URL pydoppler-1.0.0.tar.gz
Size 746.0 kB
Tags Source
SHA-256 checksum
How to use checksums
d37194d4dc3da696a41b7dbd36ff4ccddaa224a49690288720e09a693ca82a9d
BLAKE2b-256 checksum
How to use checksums
31b14f18d9a063e85e3d5a353b9a1e6b526186699e9c4dc17f387ddad0d4f24d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.18

Release files / pydoppler-1.0.0-py3-none-any.whl

Download URL pydoppler-1.0.0-py3-none-any.whl
Size 756.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d514b76afd736dff9be7dfa2955f687ef5eafd520bf7cf699855b6b406befe9d
BLAKE2b-256 checksum
How to use checksums
b36b103bb4e7b33c777f99c8070b6b6060943a64397bd75ffcb0d15b9dc759f2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.18

Security for pydoppler 1.0.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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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.

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