Hinode/EIS Level-0 Spectra Visualization

beginnerPythonastropymatplotlibnumpyrequestsHinodeEISEUV Imaging Spectrometersolar coronaEUVspectroscopyFITS

Hinode/EIS Level-0 Spectra Visualization

Hinode EUV Imaging Spectrometer (EIS) Level-0 data contain raw EUV spectra of the solar corona
across multiple emission line windows (2006–present).
Each file covers one raster observation and stores spectra from up to ~25 emission lines
as a binary table in gzip-compressed FITS format.
The dataset page is at DARTS.


Data Files

Files are organized by observation date under:

https://data.darts.isas.jaxa.jp/pub/hinode/eis/level0/{YYYY}/{MM}/{DD}/

Each file covers one raster sequence:

eis_l0_{YYYYMMDD}_{HHMMSS}.fits.gz

The FITS file contains a Primary HDU (metadata only) and a DATA binary table extension.
Each row in the table corresponds to one slit position in the raster (NEXP rows total).
Spectral windows are stored as 2D arrays [n_wavelength × n_spatial] per row.

Key Primary HDU header keywords

Keyword Example value Description
DATE_OBS 2017-10-12T06:13:19 Observation start time (UTC)
DATE_END 2017-10-12T06:16:15 Observation end time (UTC)
XCEN -203.9 FOV center Solar-X (arcsec)
YCEN 43.1 FOV center Solar-Y (arcsec)
FOVX 479.2 Field of view in X (arcsec)
FOVY 488.0 Field of view in Y (arcsec)
SLIT_ID 40" Slit/slot width
NWIN 10 Number of spectral windows
NEXP 15 Number of raster slit positions
OBSTITLE HOP323 (plage) Observation title
TARGET Active Region Observation target
DATA_LEV 0 Processing level

Binary table columns (DATA extension)

Each spectral window column is named after the emission line (e.g., Fe XII 195.120)
and has shape (n_wavelength, n_spatial) — typically (40, 488) pixels.
Additional scalar columns per row include:

Column Description
TI_1 Exposure start time (seconds from DATE_OBS)
exp_dur Exposure duration (seconds)
XCEN_TI1 Solar-X center at exposure start (arcsec)
YCEN_TI1 Solar-Y center at exposure start (arcsec)
fmirr Fine mirror position (scan step)

Visualization with Python

Requirements

pip install astropy matplotlib numpy requests

Helper functions

import re
import gzip
import io
import requests
import numpy as np
import matplotlib.pyplot as plt
from astropy.io import fits

BASE = "https://data.darts.isas.jaxa.jp/pub/hinode/eis/level0"

def list_eis_files(year, month, day):
    """Return list of file URLs for a given date."""
    date_url = f"{BASE}/{year:04d}/{month:02d}/{day:02d}/"
    resp = requests.get(date_url, timeout=30, verify=False)
    if resp.status_code != 200:
        return []
    names = re.findall(r'href="(eis_l0_\w+\.fits\.gz)"', resp.text)
    return [date_url + n for n in names]

def load_eis(url):
    """Download and open a gzip-compressed EIS Level-0 FITS file."""
    r = requests.get(url, timeout=120, verify=False)
    r.raise_for_status()
    return fits.open(io.BytesIO(gzip.decompress(r.content)))

Example 1: List spectral windows and print observation summary

urls = list_eis_files(2017, 10, 12)
print(f"{len(urls)} files found")

with load_eis(urls[0]) as hdul:
    h = hdul[0].header
    print(f"Date:    {h['DATE_OBS']}  →  {h['DATE_END']}")
    print(f"Title:   {h['OBSTITLE']}")
    print(f"Target:  {h['TARGET']}")
    print(f"Center:  ({h['XCEN']:.1f}\", {h['YCEN']:.1f}\")")
    print(f"FOV:     {h['FOVX']:.1f}\" × {h['FOVY']:.1f}\"")
    print(f"Slit:    {h['SLIT_ID']}  |  {h['NEXP']} raster positions")
    print()
    print("Spectral windows:")
    for col in hdul[1].columns:
        if col.dim:   # spectral windows have dim set
            print(f"  {col.name:25s}  shape={col.dim}")

Example 2: Plot a spectrum for one emission line at one slit position

with load_eis(urls[0]) as hdul:
    h = hdul[0].header
    data = hdul[1].data
    spec_cols = [col.name for col in hdul[1].columns if col.dim]

    # Pick the first available Fe XII window (name varies slightly between obs)
    line = next((c for c in spec_cols if "Fe XII" in c), spec_cols[0])
    cube = np.array([row[line] for row in data])  # shape: (NEXP, n_wave, n_spatial)

    # Mean spectrum averaged over all slit positions and spatial pixels
    mean_spec = cube.mean(axis=(0, 2))

    fig, ax = plt.subplots(figsize=(8, 4))
    ax.plot(mean_spec)
    ax.set_xlabel("Wavelength pixel")
    ax.set_ylabel("Intensity (raw counts)")
    ax.set_title(f"Hinode/EIS  {line}  —  {h['DATE_OBS'][:19]}")
    plt.tight_layout()
    plt.savefig("hinode_eis_spectrum.png", dpi=150, bbox_inches="tight")
    plt.close()

Example 3: Build a spatial intensity map (sum over wavelength)

with load_eis(urls[0]) as hdul:
    h = hdul[0].header
    data = hdul[1].data
    spec_cols = [col.name for col in hdul[1].columns if col.dim]

    line = next((c for c in spec_cols if "Fe XII" in c), spec_cols[0])
    cube = np.array([row[line] for row in data])  # (NEXP, n_wave, n_spatial)

    # Integrate over wavelength axis → (NEXP, n_spatial) = (scan, slit)
    intensity_map = cube.sum(axis=1).T   # (n_spatial, NEXP)

    fig, ax = plt.subplots(figsize=(6, 8))
    vmin, vmax = np.percentile(intensity_map, [1, 99])
    im = ax.imshow(intensity_map, origin="lower", cmap="hot",
                   vmin=vmin, vmax=vmax, aspect="auto")
    fig.colorbar(im, ax=ax, label="Intensity (raw counts)")
    ax.set_xlabel("Raster position (slit step)")
    ax.set_ylabel("Spatial pixel (along slit)")
    ax.set_title(f"Hinode/EIS  {line}\n{h['DATE_OBS'][:19]}")
    plt.tight_layout()
    plt.savefig("hinode_eis_map.png", dpi=150, bbox_inches="tight")
    plt.close()

Example 4: Compare multiple emission lines side by side

with load_eis(urls[0]) as hdul:
    h = hdul[0].header
    data = hdul[1].data
    spec_cols = [col.name for col in hdul[1].columns if col.dim]

    n = len(spec_cols)
    fig, axes = plt.subplots(2, (n + 1) // 2, figsize=(14, 6))
    for ax, line in zip(axes.flat, spec_cols):
        cube = np.array([row[line] for row in data])
        imap = cube.sum(axis=1).T
        vmin, vmax = np.percentile(imap, [1, 99])
        ax.imshow(imap, origin="lower", cmap="hot", vmin=vmin, vmax=vmax, aspect="auto")
        ax.set_title(line, fontsize=8)
        ax.set_axis_off()

    for ax in axes.flat[n:]:
        ax.set_visible(False)

    fig.suptitle(f"Hinode/EIS — {h['DATE_OBS'][:19]}")
    plt.tight_layout()
    plt.savefig("hinode_eis_all_lines.png", dpi=150, bbox_inches="tight")
    plt.close()

Notes

  • Level-0 data are raw telemetry with no radiometric calibration. For calibrated spectra (Level-1), use the IDL eis_prep routine from Solar Software (SSW) or the Python package eispac.
  • The spectral window names in the binary table correspond to the dominant emission line in each window. Each window contains n_wavelength spectral pixels (typically 24–40) × n_spatial slit pixels (up to 512).
  • The SLIT_ID keyword identifies the slit used: 1", 2", 40" (slot), or 266" (slot). The 40" and 266" slots produce context images rather than true spectra.
  • SSL certificate verification may fail for data.darts.isas.jaxa.jp; pass verify=False to requests.get as a workaround.
  • For absolute wavelength calibration, a dispersion of approximately 0.0223 Å/pixel applies to the short-wavelength channel (SW: 170–210 Å) and 0.0446 Å/pixel to the long-wavelength channel (LW: 250–290 Å).

Acknowledgement

When using this data in publications, please follow the Hinode data use guidelines and cite:

References

Related Datasets