Skip to main content

enchase

Calibration and flare analysis for CHASE/HIS solar spectroscopy. Install one package, point it at data, get aligned cubes, animations, contrast profiles, and temperature maps.

tests PyPI python license

Stabilised 2-panel animation of the 2023-03-29 X1.2 flare
The 2023-03-29 X1.2 flare, stabilised by this pipeline. Photosphere left, chromosphere right.

Install

pip install enchase

That gives you the chase module, the chase command, and the chase-tui interactive app. Extras: pip install 'enchase[tui]' (TUI), 'enchase[atlas]' (absolute Fe I calibration), 'enchase[all]' (everything).

Get data

Two ways.

Option 1: our hosted dataset. We host ready-to-use CHASE observations on an S3 server, including the 2026-07-04 M3.2 flare (48 files, 8.65 GiB). No portal account, no expiring links:

import boto3
s3 = boto3.client("s3", endpoint_url="https://s3.preservemy.world",
                  aws_access_key_id=ACCESS_KEY, aws_secret_access_key=SECRET_KEY,
                  region_name="garage")
s3.download_file("chase-data",
                 "events/2026/07/04/M3.2/raw/HA/RSM20260704T132940_0000_HA.fits",
                 "data/RSM20260704T132940_0000_HA.fits")

Read credentials are free for research use: see docs/S3_DATA.md for the full guide (bucket layout, Colab setup, s3fs) and how to get keys.

Option 2: the CHASE portal. The portal (ssdc.nju.edu.cn) has a captcha, so you log in once in a real browser; after that everything is command line:

pip install enchase[portal] && playwright install chromium

chase-portal login          # opens the portal once; log in, close the window
chase-portal search --start "2023-03-29 02:00" --end "2023-03-29 03:00"
chase-portal urls   --start "2023-03-29 02:00" --end "2023-03-29 03:00" -o links.txt
chase links.txt             # downloads the files and runs the pipeline

urls writes one signed link per line (they expire in ~24h, so mint right before downloading). chase also accepts a single URL or a folder of already-downloaded *.fits. Walkthrough: docs/DATA_ACQUISITION.md.

Run it

Run this:

chase ./data --patch 940 1080 1870 2040 --contrast --temperature halpha

What happens: loads every scan, tracks the patch across frames, resamples wavelengths onto one grid, stabilises with optical flow, then writes results to ./chase_out/:

You get What it is
quicklook.gif photosphere / chromosphere animation
contrast_profile.gif + .npy wavelength-vs-time flare contrast
ha_temp_maps.npy + temperature.gif chromospheric temperature maps
aligned_data.npz the aligned cubes (also the resume checkpoint)
diag_*.png patch overlay, quiet-Sun spectrum

No patch coordinates yet? Run with --full-fov --no-optical-flow --no-track first, look at diag_qs_spectrum.png and the GIF, then pick a [y0 y1 x0 x1] box.

Rerun just one thing

The first run saved a checkpoint, so reruns skip the slow loading and alignment:

chase ./data --out ./chase_out --only gif           # re-render the animation. Takes seconds
chase ./data --out ./chase_out --only contrast      # recompute contrast only
chase ./data --out ./chase_out --only temperature --temperature fe_voigt

Turn things off

Every stage has a switch. Mix freely:

Flag What it disables
--no-track cross-correlation patch tracking
--no-resample per-frame wavelength resampling
--no-drift spectral drift correction in the contrast stage
--no-optical-flow optical-flow stabilisation
--no-gif, --no-npz, --no-diagnostics those outputs

--no-resample --no-drift together = completely untouched wavelength axis.

Alignment options (new in 2.2)

The defaults are the same as in 2.1: the patch is tracked by cross-correlation against scan 0, and the optical flow runs on the last Fe I wavelength (or the last Hα wavelength when there are no Fe I files). The options below are off unless you ask for them.

Option (Config / TOML) CLI flag Values What it does
align_mode --align-mode xcorr (default), header, header+xcorr header takes each scan's crop shift from the disc-centre drift in the headers (CRPIX1/CRPIX2) plus the differential rotation of the patch (Snodgrass & Ulrich 1990), with no image correlation. header+xcorr adds a correlation residual on top (rejected above max_residual, 3 px).
disc_center --disc-center crpix (default), limb, auto Disc-centre source for the header modes. limb uses sub-pixel limb fits. auto checks the header against limb fits of three scans and warns when the header is unreliable.
rotation --no-rotation True (default) Differential rotation in the header modes.
subpixel --subpixel False (default) Apply the fractional part of the crop shift with a cubic spline instead of rounding.
reference --reference first (default), previous, running Correlation reference: scan 0, the previous scan, or the mean of the last reference_window (default 3) aligned scans.
drift_control --drift-control none (default), header Ties chained correlation steps back to the header pointing by least squares, so they do not random-walk.
shift_method --shift-method phase (default), windowed Correlation estimator. windowed is the Hann-windowed correlation used by chase.analysis.residual_pointing.
flow_reference --flow-reference default, stable, masked, stable+masked Image the optical flow is computed on. stable picks the far-wing channel(s) (flow_channels, default 1) that change least; masked ignores pixels that change by more than flare_threshold (default 0.1).

load_flare_sequence also takes max_residual, header_sigma and step_sigma (SDK only).

# Hα-only quiet Sun, filaments, near-limb or off-limb fields
chase ./data --patch 1520 1720 1600 1800 --align-mode header --subpixel --no-optical-flow

# long series (> 15 min) where image structure changes
chase ./data --patch ... --reference running --drift-control header

# flare ROI
chase ./data --patch ... --align-mode header+xcorr --flow-reference stable

What we measured (Solar Physics referee revision, real CHASE data; tracking error against per-scan limb-fit pointing):

  • Hα-only fields (quiet Sun, filaments, near-limb and off-limb boxes in three data sets): header gives 0.2-0.5″ rms, against 1.6-13″ for the 2.1 continuum fallback (up to 19″ over a 115-min series). The scan-to-scan Hα core rms drops from 2.8-10.8 % to 2.6-3.5 %.
  • After 15 min the 2.1 tracking reaches 3.3-22″ rms, also with Fe I. header, and reference="running" with drift_control="header", stay at 0.3-0.6″. Without the header tie a chained reference drifts by 0.7-5.2″.
  • X9.0 flare: flow_reference="stable" lowers the peak residual after flow from 1.10 px (1.15″) to 0.92 px (0.96″). About 1 px remains at the flare peak with every option. masked is worse at the peak (2.2 px), so do not use it there. On the flare ROI plain header (1.64″) is worse than correlation (1.24″) before flow; header+xcorr is best (0.95″).

When not to use them:

  • Turn optical flow off for on-disc Hα-only fields after header recentring. The flow is computed against scan 0 and raises the core rms from 2.6-3.5 % to 4.1-5.7 %.
  • Do not use the header modes on the 2023-03-29 Level-1 data (X1.2). Its CRPIX is 30-42 px off the limb, and odd and even scans have field-dependent offsets of 8-11 px that no disc centre follows. Use the default Fe I correlation there (0.42″ tracked, 0.06″ after flow). disc_center="auto" warns in this case.
  • Sit-and-stare data are not supported: the public archive has none to test on.

Limb fit and centre-to-limb calibration (new in 2.2)

from chase.calib import fit_limb, limb_disk_center, limb_shifts, mu_map
from chase.calib import atlas_calibrate, download_clv_table, load_clv_table

cx, cy, r = limb_disk_center(full_disc_image)                 # sub-pixel, about 1 s per frame
shifts = limb_shifts(full_disc_images, model="ellipse")       # per-scan recentring
table = load_clv_table(download_clv_table(dest_dir="."))      # Pietrow et al. 2023 Hα CLV (CDS)
mu = mu_map(full_disc_image.shape, (cx, cy), r)
ifact, woff = atlas_calibrate(wav, box_cube, mu=mu[y0:y1, x0:x1], clv_table=table)  # near-limb box -> disc-centre factor
  • fit_limb (circle or rotated ellipse) takes 0.6-2 s per frame against 70-120 s for hough_disk_center. Residual full-disc pointing: 2.7″ (X1.2) and 0.6″ (M3.2), against 5.0″ and 1.0″ with Hough. What is left on X1.2 is a distortion of every second raster in the 2023 Level-1 data, which recentring the whole disc cannot remove.
  • atlas_calibrate(..., mu=, clv_table=) corrects the quiet-Sun centre-to-limb variation (Neckel & Labs 1994 continuum plus the Pietrow et al. 2023 Hα table). On a near-limb box the calibration error drops from 12-73 % to 3-10 %. The table covers ±2 Å only; further out the correction is 4-19 % off. Stray light is not corrected. The default mu=1.0 keeps the 2.1 behaviour.

The same thing in Python

from chase import Config, run_pipeline

results = run_pipeline(Config(fits_dir="./data", patch=[940, 1080, 1870, 2040],
                              contrast=True, temperature="halpha"))

results["aligned"]["ha"].shape    # (nframes, 118, H, W) stabilised float32
results["contrast"].shape         # (nframes, 118)
results["gif"]                    # './chase_out/quicklook.gif'

Rerun one step from the checkpoint: add resume=True and only the toggles you want.

Or skip the pipeline and call single functions:

import chase

cube, hdr, wav = chase.load_cube("RSM..._HA.fits")          # one cube: (118, H, W) + wavelengths
seq = chase.load_flare_sequence("./data", patch=[...])      # tracked + resampled sequence
ha_al = chase.optical_flow_align(seq["ha_cubes"], reference=seq["align_ref"])
contrast, _ = chase.contrast_profile(ha_al, background=seq["ha_bg"])
T, width = chase.halpha_width_temperature(ha_al[12], seq["wavelength_ha"])

Each line works on its own. Full list with snippets: docs/SDK.md.

Check how well each step worked (plain numbers, ready for a table):

from chase.analysis import residual_pointing, residual_wavelength, frame_rms
from chase.analysis import fit_limb_ellipse, offlimb_profile, psf_stray_light, throughput_trend
from chase.calib import atlas_calibrate, atlas_sensitivity, activity_mask

residual_pointing(fe_continuum_stack)["global_rms_arcsec"]    # leftover jitter after tracking/flow
residual_wavelength(qs_profiles, per_scan_grids)["std_mA"]   # line-core scatter between scans
atlas_calibrate(wav, dc_cube, mask=activity_mask(cont, ha_core), return_quality=True)
atlas_sensitivity(wav, dc_cube)                                # ifact vs wing threshold and mask
p = offlimb_profile(full_disc_image)                           # off-limb level, % of disc centre
psf_stray_light(full_disc_image, p)["s_dc_range"]              # scattered fraction at disc centre
throughput_trend(dates, ifact_per_date)["throughput_pct_per_yr"]

The pipeline does not correct stray light. For CHASE it is about 6–10 % of the disc-centre intensity, which makes small dark features (umbrae, filaments) look brighter than they are. The atlas factor changes by about 12 % per year, so fit it for each observation (the pipeline does this by default) rather than reusing one from another date. For Hα-only data use wing_continuum_frac=0.75: no Hα channel passes the 0.85 default, and atlas_calibrate warns about this.

Prefer clicking to typing?

chase-tui

End-to-end chase-tui walkthrough

Every checkbox is one of the switches above. There is also a one-file config workflow: chase --config my.toml, keys mirror the flags (examples/config.toml).

Why this exists (statement of need)

CHASE (the Chinese Hα Solar Explorer) scans the full solar disk in Hα (118 wavelengths, 6559.4 to 6565.1 Å) and Fe I (46 wavelengths near 6569 Å). The portal ships full-disk cubes with no co-alignment and a wavelength zero-point that drifts by ~0.8 channels per frame. Getting a science-ready region out of that takes real calibration work, which is why the data is under-used. Earlier scripts (the original chase-pipeline, Finlay Davis's satprocess) each solved part of the problem. This package merges them into one installable tool that preserves the physics: shift-then-crop tracking, per-frame wavelength resampling, alignment on a flare-free channel, absolute atlas calibration, and the correct temperature inversion per line. The reasoning behind each rule is in docs/CALIBRATION.md.

What lives where

Subpackage Does
chase.io resumable download, FITS discovery, cube + sequence loading
chase.calib.spatial / .align shift-then-crop tracking, optical-flow stabilisation
chase.calib.wavelength per-frame resampling onto a common grid, spectral-drift xcorr
chase.calib.intensity quiet-Sun norm, integral scaling, FTS-atlas absolute calibration
chase.analysis.contrast wavelength-vs-time flare contrast
chase.analysis.temperature Hα width to T (Molnar), Fe I to T (Planck / EB / Voigt)
chase.analysis.validation residual pointing (arcsec), wavelength scatter (mÅ, km/s), frame-to-frame RMS (%, cgs)
chase.analysis.systematics stray light from the off-limb profile, throughput trend of the atlas factor

Double temperature map animation
Double temperature map: chromosphere from Hα width (~10⁴ K), photosphere from Fe I Planck inversion (~5100 K).

Documentation

Development

git clone https://github.com/Majboor/enchase.git
cd enchase
pip install -e '.[all]'
pytest -q            # 27 tests, synthetic FITS fixtures, no big data needed

Acknowledgements

Built with the guidance and corrections of Dr. Alexander Pietrow (AIP) and Dr. Malcolm Druett. The Hough-circle limb centring, spectral cross-correlation, integral intensity scaling, and CSV shift-cache are adapted from Finlay Davis's satprocess (BSD-3-Clause; see NOTICE). Affiliation support from Prof. Dr. Sayed Amer Mahmood (University of the Punjab). Data courtesy of the CHASE/HIS mission and the Solar Science Data Center, Nanjing University.

Citing

Author: Waleed Ajmal, ORCID 0009-0006-8222-1983. Software citation metadata is in CITATION.cff.

If you use this pipeline, cite the methods it builds on:

  • Molnar et al. 2019, ApJ 881, 99. Hα width to temperature (10.3847/1538-4357/ab2ba3)
  • The Eddington-Barbier photospheric inversion (A&A 2013, aa21259-13)
  • The ISPy FTS solar-atlas calibration (ISP-SST/ISPy)

License

MIT. Ported satprocess code stays BSD-3-Clause, listed in NOTICE.

Metadata

Release files for enchase 2.2.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 enchase 2.2.0
File Size Uploaded Provenance
enchase-2.2.0.tar.gz 113.8 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 219.5 kB

Release files / enchase-2.2.0.tar.gz

Download URL enchase-2.2.0.tar.gz
Size 113.8 kB
Tags Source
SHA-256 checksum
How to use checksums
e5296cffe09c984d7047b25f225e2e6ae93742eb603de43e6b29e8e77c69893c
BLAKE2b-256 checksum
How to use checksums
a9fac6a063429607e02e0bf11f6405b5c49e512491520b325705e63613b24ff6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Release files / enchase-2.2.0-py3-none-any.whl

Download URL enchase-2.2.0-py3-none-any.whl
Size 105.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f8dc7a3b570ca469153f0038a631362e360a95564e791b46b3e30ab09e2c37cd
BLAKE2b-256 checksum
How to use checksums
64cb4005954bbf0ac7922ea0a44d3b4fb9953403202918b351dda343d05e0400
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.2

Security for enchase 2.2.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.

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