EXPRES

What is the EXPRES solar feed?

EXPRES, the EXtreme PREcision Spectrograph, is a high-precision radial velocity (RV) instrument on the Lowell Discovery Telescope. A small auxiliary telescope feeds sunlight into it during the day so it can observe the Sun as if it were a distant star (the solar feed). The Sun is a bright, well-understood “star”, so this dense, high-cadence RV series is ideal for studying the activity signals — granulation, oscillations, magnetic activity — that limit precision RV planet searches everywhere else.

This client exposes only the EXPRES solar feed, which NExScI hosts on the same IPAC TAP service as NEID, at the solar L1 and L2 levels. The L2 radial velocities are the main product.

from nexsci_rv import EXPRESArchive
expres = EXPRESArchive()

Data levels

data_level

Table

Contents

solarl1

expressolarl1

Extracted, wavelength-calibrated solar spectra

solarl2

expressolarl2

Solar RV products (v, e_v, dvrms, extsnr)

Which one do I want? Most users want solarl2 — the radial velocities the pipeline already measured. Reach for solarl1 only if you want the spectra themselves to run your own line or activity analysis. See Data levels for the broader picture.

The columns you’ll actually use

L2 rows are produced by a CCF (cross-correlation function) — the spectrum is cross-correlated against a template of known lines to recover the Doppler shift. The headline solar L2 columns:

Column

Meaning

v / e_v (EXPRES) (v)

The measured radial velocity, m/s.

v / e_v (EXPRES) (e_v)

Formal uncertainty on v, m/s.

dvrms

Photon-limited RV precision predicted for the exposure, m/s.

extsnr

Extracted spectrum signal-to-noise; a natural quality cut.

obsdate

UTC observation time stamp; what date queries filter on.

mjd

The same time as a Julian Date (JD / MJD) (Modified JD), for time series.

filename / filepath / filehand (l2filename, l2filepath)

The FITS file’s name and server path; download needs both.

If you port a NEID workflow over, swap ccfrvmod → v and ccfnoise → e_v. For a full, plain-language column tour see Understanding your results.

Time columns

Three time columns appear in the L2 table and it helps to know which is which:

  • obsdate — the UTC calendar time stamp. This is the column query_datetime filters on; you never type it yourself for date queries.

  • mjd — Modified Julian Date (JD − 2400000.5), a compact numeric time good for plotting and fitting.

  • obsjd — the full Julian Date for the same exposure.

Querying solar data

For solar data you filter by time — and optionally by quality — not by sky position or target name. There is no star to point at, so query_datetime is the right entry point. See Choosing a query for the general guide.

tbl = expres.query_datetime(
    "solarl2",
    ("2024-11-01", "2024-11-06"),
    columns=["l2filename", "obsdate", "v", "e_v", "dvrms", "extsnr"],
)
print(tbl)

# Combine a date window with a quality cut, in one query:
tbl = expres.query_criteria({
    "data_level": "solarl2",
    "datetime": ("2024-11-01", "2024-11-06"),
    "where": "extsnr > 200",
})

# Download the L2 FITS files for the rows you selected:
paths = expres.download(tbl, outdir="solar_l2/", workers=4)

query_program exists, but every solar exposure shares the single program string 'EXPRESSOLAR' (the proposal/campaign identifier the archive groups exposures under), so it is a sanity check rather than a filter. query_pi works on solarl1, where it matches the recorded observer string — though since the solar feed runs automatically there is no human PI per observation, so it is rarely useful; solarl2 has no observer column and so raises NotApplicable.

Why position and object searches don’t apply

A solar observation points at the Sun, not at a catalog target, so the rows carry no real sky coordinate (the query coordinates (qra / qdec / qrad / qdecd) are sentinel values of -99.9). Any search that would match on a celestial position therefore raises NotApplicable:

  • query_position(...) — no sky coordinate to search.

  • query_object(...) — no celestial target to resolve.

  • query_qobject(...) — no real catalog ID.

The same applies inside query_criteria: passing "position", "object", or "qobject" keys (or "pi_name" with data_level="solarl2") raises the same NotApplicable.

Downloading

The query returns metadata; the data products are FITS files you fetch with download, which retrieves the L1 or L2 files for the rows you pass it and returns a list of pathlib.Path objects. Keep the filename column if you trim the result with columns=. See Downloading FITS files and plotting a spectrum for a full query-download-plot walkthrough.

Authentication

EXPRES solar data is fully public — no account, no login, and no cookie written to disk (in contrast to proprietary NEID data). There is no login endpoint, so expres.login() raises NotApplicable.

Useful columns

For the authoritative list, with units and descriptions, call expres.describe_table("expressolarl2"). The common columns:

solarl2: l2filename, l2filepath, l2filehand, obsdate, mjd, obsjd, exptime, airmass, v, e_v, dvrms, extsnr, ccfrvmod, ccfjdsum, swversion, l2procversion.

solarl1: l1filename, l1filepath, l1filehand, obsdate, mjd, obsjd, exptime, airmass, extracttype, observer, obstype, swversion, l1procversion.

Production-only

EXPRES tables exist only on the production server (https://neid.ipac.caltech.edu). The test server returns ORA-00942: table or view does not exist. EXPRESArchive() defaults to production, so you do not normally need to think about this.

Table name spelling

The TAP table names use a single s between expres and solar: expressolarl1, expressolarl2. Double-s spellings (expresssolarl1) return ORA-00942. The typed methods always use the correct spelling; only raw query_adql callers need to.