SED data¶
Getting an SED¶
client.sed.get() returns the SED at a sky position: the source (name,
coordinates, redshift, synchrotron peak), the job that built it and every point.
Most positions are cached and come back in one request; a new position runs the
pipeline over ~60 catalogues, which takes up to a few minutes. Any job within 2″
is reused.
sed = client.sed.get(ra=166.113808, dec=38.208833, name="Mrk 421")
sed.source.redshift # 0.0308 (None when unknown)
sed.source.w_peak # None when unknown; else .log_nu (log10 Hz), .limit, .text ("> 17.5")
sed.catalogs # [SEDCatalog(name="NVSS", band="radio_waves", reference=..., color=...), ...]
sed.missed_catalogs # catalogues that could not be queried in this run, e.g. ["ZTF"]
# Points as columns (plain lists, None where missing)
sed.freq_hz, sed.nufnu, sed.nufnu_err, sed.is_ul, sed.mjd_start, sed.mjd_end
sed.catalog # catalogue name of every point
sed.rows() # one dict per point
sed.table # pandas DataFrame (needs pandas)
Filter, convert, save and plot on the client:
recent = sed.between(58000, 58400) # MJD overlap; undated points kept unless undated=False
radio = sed.select(["NVSS", "FIRST"]) # or sed.select(exclude=["ZTF"])
sed.to_csv("mrk421.csv") # same columns as the server's CSV; returns the text
sed.converted(x="eV", y="Jy") # {"x": [...], "y": [...], "y_err": [...]}
recent.plot("mrk421.png", y="Jy Hz") # needs astro-mmdc[plot]; returns the matplotlib Axes
Units are the website's: x is Hz or eV; y is erg cm-2 s-1 (default),
TeV cm-2 s-1, norm (the website's dN/dE axis), Jy Hz, W m-2 or Jy (F(ν)).
The website's axis keys (freq_ev, flux_jyhz, nufnu_fnu_jy, ...) work too.
The MJD and catalogue filters can also be applied by the server:
client.sed.get(..., mjd_start=58000, mjd_end=58400, catalogs=["NVSS"]).
Progress. progress is called with the job after every answer from the
server; progress=True prints the job's log to stderr:
sed = client.sed.get(194.046527, -5.789312, "3C 279",
progress=lambda job: print(job.status, job.progress, len(job.new_events)))
Refresh. refresh=True runs the pipeline again. The old SED stays in place
until the new run succeeds. If the new run fails (including empty_refresh,
when it found no data where the old SED had points), get() returns the old SED
with sed.stale == True and the failed run in sed.refresh, and emits a
SEDStaleWarning naming the error code. It raises SEDJobFailed only when
there is no previous SED.
Positions are degrees as floats; astropy Angles and a SkyCoord
(client.sed.get(coord)) work too.
Source only. The redshift and synchrotron peak without the points:
Many sources. get_many runs up to max_concurrency at a time and keeps
the input order. Sources are (ra, dec) / (ra, dec, name) tuples, dicts or
objects with ra, dec and name:
seds = client.sed.get_many(
[(166.113808, 38.208833, "Mrk 421"), (253.467569, 39.760169, "Mrk 501")],
return_exceptions=True, # a failed source gives its exception instead of raising
)
Without return_exceptions, the first failure is raised at once and the
remaining sources are not started.
Jobs. submit returns at once with an SEDJob handle:
job = client.sed.submit(ra, dec, name="PKS 2155-304")
job.id, job.status # queued | running | done | no_data | failed
job.progress # SEDProgress(stage="phase2", done=17, total=59, elapsed_s=8.9)
job.refresh() # one request: new status and only the new events
job.events # every event so far; event.render() gives the log line
sed = job.result(timeout=300)
# Later, from the id alone
client.sed.job(job.id) # status and all events
client.sed.fetch(job.id) # the SED, waiting if the job still runs
client.sed.csv(job.id, "out.csv") # the server's CSV
Errors. get, fetch and result raise SEDNoData when the pipeline
found nothing (the empty SED, with its source, is on .sed), SEDJobFailed
with a stable .code (pipeline, timeout, abandoned, enqueue,
internal, empty_refresh) and .retry_after_s, and SEDTimeoutError with
the job .id when timeout (default 900 s) runs out (retries of a busy
server never run past it); the job keeps running
and client.sed.fetch(id) collects it later.
Older API¶
Legacy
The methods below use the older endpoints and keep working unchanged. New code should use client.sed.get() and the jobs described above.
Preparation¶
SED preparation fetches multi-wavelength observational data from external catalogs for a given sky position.
job = client.sed.prepare_and_wait(
ra=187.2779, # Right Ascension in degrees
dec=2.0524, # Declination in degrees
database_name="3C273", # Database identifier
source_name="3C 273", # Optional display name
)
print(job.status) # "done", "no_data", or "error"
print(job.uuid) # Use this UUID for all subsequent data retrieval
An error status is returned, not raised; pass raise_on_error=True to get
SEDJobFailed instead. A timeout raises PollingTimeoutError with the job's uuid.
If data already exists for a sky position, MMDC returns the cached result. Use force=True to re-fetch:
Retrieving SED Data¶
Once a job is complete, retrieve the frequency/flux data:
data = client.sed.get_data(job.uuid)
# data.uuid, data.ra, data.dec, data.source_name
# data.data — nested dict of frequency ranges, catalogs, and flux measurements
Filter by time range, catalogs, or convert units:
data = client.sed.get_data(
job.uuid,
mjd_start=50000.0, # Filter by MJD time range
mjd_end=60000.0,
exclude_catalogs=["WISE", "2MASS"], # Exclude specific catalogs
exclude_freq_ranges=["radio"], # Exclude frequency ranges
x_axis="freq_ev", # Convert frequency to eV
y_axis="flux_ev", # Convert flux to eV/cm²/s
)
Available axis conversions:
x_axis |
Description |
|---|---|
"freq_ev" |
Hz to eV |
y_axis |
Description |
|---|---|
"flux_ev" |
erg/cm²/s to eV/cm²/s |
"flux_norm" |
Normalized eV/cm²/s |
"flux_jyhz" |
Jy·Hz |
"flux_wm2" |
W/m² |
"nufnu_fnu_jy" |
Fν in Jy |
Source Metadata¶
info = client.sed.get_info(job.uuid)
print(info.source_name) # "3C 273"
print(info.ra, info.dec) # Coordinates
print(info.redshift) # Redshift (float or None)
print(info.gal_lat) # Galactic latitude
print(info.gal_long) # Galactic longitude
print(info.W_peak) # Peak frequency indicator
Download as CSV¶
client.sed.download_csv(job.uuid, "sed_data.csv")
# With filters (same options as get_data)
client.sed.download_csv(
job.uuid, "filtered.csv",
mjd_start=55000.0,
exclude_catalogs=["WISE"],
)
CSV columns: frequency, flux, flux_err, MJD_start, MJD_end, flag, catalog, reference