Data¶
Getting the dataset¶
The built training set is on the Hub:
from huggingface_hub import hf_hub_download
path = hf_hub_download("aryana-haghjoo/romansr-data",
"ou2024_h10307_dataset.npz", repo_type="dataset")
36,404 extracted spectra of 15,434 galaxies, healpix 10307, 0.06 < z < 3.1.
The tutorial subset¶
A 3.8 MB, 512-row sample sits in the same repo under tutorial/, for the
getting-started notebook and for any
quick check that does not need 271 MB:
path = hf_hub_download("aryana-haghjoo/romansr-data",
"tutorial/ou2024_h10307_tutorial.npz", repo_type="dataset")
Same schema, plus a source_row column recording where each row came from in
the full file. It is drawn from the held-out side of the canonical split
and sampled uniformly within it — so out-of-sample metrics computed on it are
honest, and the mix of recoverable and undetectable lines is the population’s
own rather than a flattering selection. Rebuild it with
python scripts/make_tutorial_dataset.py.
Schema¶
One npz, all rows on two shared grids:
Key |
Shape |
Meaning |
|---|---|---|
|
(N, 864) |
extracted grism spectrum on |
|
(N, 2500) |
ground-truth SED on |
|
(N,) |
true redshift |
|
(N,) |
OU2024 |
|
(N, 14) |
catalogue fluxes, order fixed by |
|
(N,) |
selection and quality columns |
|
(N,) |
provenance |
|
(864,), (2500,) |
the shared grids |
flux_high has no error column: the targets are noiseless simulated SEDs. The
dataset returns a zero error array anyway, so the loss signature matches the
JWST version where targets are real grating spectra.
Splitting¶
Split by object, never by row. The same galaxy appears in many visits as independent noise realisations; a row-wise split puts one realisation in train and another in test and measures memorisation.
from specsr_roman.data import get_or_make_group_split
train_idx, test_idx, _ = get_or_make_group_split(path, data["ids"])
Membership is a pure hash of the object id, which buys two things: every stage derives the same split without passing a file around, and the split is stable under dataset growth — adding SCAs never moves an existing galaxy across the boundary, so a model trained on an older build stays evaluable on the newer test set.
Photometric tiers¶
Feed the redshift head only bands that ship with the grism:
from specsr_roman.grids import ROMAN_MEDIUM_BANDS # (8, 9, 11) — F106, F129, F158
from specsr_roman.data import RomanFixedGridDataset
ds = RomanFixedGridDataset(path, with_phot=True, phot_tier="medium")
Tiers are medium, deep, all, or an explicit "8,9,11".
all includes LSST ugrizy. LSST coverage over Roman’s grism footprint is
external, partial, and not guaranteed at first data release — and with all 14
noiseless bands the photometry is an effectively complete SED from which the
redshift can be read without the spectrum contributing anything. It is a
diagnostic configuration, never one to deploy.
Warning
OU2024 uses the old Roman band names, which collide with the current WFI
scheme: OU2024 R062 is 0.62 µm (current F062), and OU2024 W146 is the
current wide filter R062. Always map by central wavelength, never by name.
grids.PHOT_BANDS fixes the index order.
Building it yourself¶
Requires the extract extra.
specsr-roman extract run --max-scas 60 --workers 4
specsr-roman extract merge
Resumable: each (visit, SCA) caches its result, including a stub recording why a pointing produced nothing, so re-running skips completed work. Each runs in its own subprocess because grizli’s numba disperser corrupts the heap when thousands of sources go through one process — a crash costs one SCA, not the run.
Add --cleanup to delete raw and prepared FITS as it goes; the full set is
tens of gigabytes.
What it does per SCA¶
Prepare a grizli direct image plus an empty grism shell (
INSTRUME='WFI', so grizli loadsRoman.G150.conf).Detect sources, KD-tree match to the truth index.
Disperse every source brighter than
--ab-scenewith its true SED.Add Poisson (source + zodiacal) and read noise at 301 s.
Per target: re-disperse it alone, subtract from the scene for exact contamination, optimally extract, calibrate against a flat-f_λ pass.
Because the same configuration disperses and extracts, input and target are self-consistent by construction — any residual is noise, blending or the extraction, not a mismatched instrument model.
The merge step refuses to write if more than 1 % of rows have a constant
flux_low. A constant spectrum carries no information and would train a model
straight into the prior mean while every loss curve looked healthy.