This repository contains the pipeline and corresponding tasks for the quality assurance (QA) of the Prime Focus
Spectrograph (PFS) Data Release Production (DRP)
pipeline. The QA tasks are implementations of the PipelineTask class in the LSST Science Pipelines. The tasks are run
on the output of the DRP pipeline to assess the quality of the data products.
imageQualityQa is a gate: for each image (visit × detector) it decides whether the image should be
discarded and not counted in its group, and it shows problems that span an instrument, spectrograph, arm or
detector. Its verdict is persisted, so that calibration building and the science pipeline can gate on it.
- It judges quick reductions.
drpActorreduces each image during observing (reduceExposure, itshilopipeline). That is not the reduction the science release uses.qaActorlistens todrpActorand readsdrpActor/reductions, so QA never reduces anything a second time; production QA must not re-reduce. Thresholds are therefore derived indrpActor's reduction context. Comparison mode is the one exception: it reduces only whatdrpActor/reductionslacks, withdrpActor's per-sequence-type config, into its own collection. - Two modes, one judgement. Real time:
qaActorjudges each sequence group asdrpActorfinishes it (INSTRM-3040). Comparison: a batch over every visit of a run, listed from the opdb (PIPE2D-1929; see Comparison mode). Both judge withpfs.drp.qa.metrics.gate. - Comparison works on a partial run. Run on day 10 of a 20-day run, it gives valid verdicts, and a later re-run adds the new nights' images without changing earlier verdicts: a verdict depends only on its own image, each period and drp_qa version has its own collection, and what is already judged is skipped.
- Verdicts are stored in the Butler, in
iqQaMetrics(qaStatus,qaReason). qadb (spt_qa_database) loads them later, with the long-term QA results (PIPE2D-1385). - Gate, not product assessment. Judging the quality of science products is a separate job, not yet designed; tasks here should stay on one side of that line.
- Not covered yet: cosmic-ray processing (PIPE2D-1609).
The work is tracked in epic PIPE2D-1928.
drp_qa is a pyproject.toml-based package with no compiled components, so there is nothing to build:
pip install -e . # or: uv syncWithin the LSST stack, EUPS setup still works and is the usual route — drp_stella
must be set up first:
source /path/to/stack/loadLSST.bash
setup -r ../drp_stella
setup -r .ups/drp_qa.table only manipulates PATH/PYTHONPATH; there is no SCons build step. The bin/ directory that SCons
used to generate is therefore not created, and the scripts under bin.src/ are invoked directly
(see Command-line tools).
ruff format . # formatting
ruff check --fix . # linting and import sorting
pytest tests/Ruff configuration, pytest configuration, and package metadata all live in
pyproject.toml; there are no standalone per-tool config files.
The QA pipelines is located at pipelines/drpQA.yaml, which runs all of the QA tasks.
Note: individual tasks can be specified by using the
pipelines/drpQA.yaml#extractionQAsyntax.
See documentation
for details.
Also see the example notebook examples/QA Pipelines.ipynb.
drpQA.yaml currently wires up dmLineResiduals, extractionQa, extractionQaCombined, and imageQualityQa. The
remaining tasks below are implemented and importable
but are not registered in the pipeline; run them by referencing their task class directly.
Per detector, measures every line of lines against two references (reference): detectorMap_calib (calib), the
calibration the visit is reduced against, whose offset is drift; and the visit's own detectorMap (visit), adjusted
to these lines, whose RESERVED lines test how well it predicts lines it was not fitted to, as it must on a science
frame. The lines are those the visit's detectorMap fit marked DETECTORMAP_USED or DETECTORMAP_RESERVED, less
BAD ones, with no other selection or clipping; trace centroids are kept on arcs. When the fit marked none, every
good line is measured, as UNFITTED. Trace centroids measure x, lamp lines x and y, measured minus reference.
Each row carries the frame's observation type (obsType, from the raw header's W_SEQTYP as imageQualityQa
classifies it: trace for a quartz, arc, twilight, ...; the pfsConfig header stands in when the raw's lacks the
card): trace centroids are measured on every frame, but on an
arc's faint continuum they scatter two to three times more than on a quartz, so they are a population per frame type.
Per reference, line species and line set (statusType: BOTH, USED, RESERVED, or UNFITTED): the median and
robust RMS of the x and y residuals, nLines and linesPerFiber. Judged: against calib, over BOTH (or
UNFITTED) lines, the median x offset of traces and median y offset of lamp lines (WARN 1 px, APPLY_SHIFT; FAIL 2
px, RECALIBRATE); against visit, the robust RMS of RESERVED lines (RECALIBRATE), by the thresholds file
dmLineResidualThresholds-run25.yaml (PIPE2D-1956): trace centroids on quartz frames and the Ar, Kr, Ne and Xe
lines in x, per detector, at WARN 20 % and FAIL 50 % above the detector's known-good median in Run25, and the lamp
lines in y at WARN 50 % and FAIL 150 %. Stored, not judged: the mercury and cadmium lines, whose y scatter is
intrinsically variable; trace centroids on arcs and twilights; and the sky-line species on twilights. The weighted RMS and
the softening are drp_stella's, in iqQaMetrics and iqQaSpeciesMetrics, not recomputed. Logic in the stack-free
pfs.drp.qa.metrics.lineResiduals. No plot is drawn as the task runs: plot dmLineResidualData afterwards.
It runs in pipelines/qaThresholds.yaml beside imageQualityQa, so the thresholds notebook and comparison mode
measure it on every visit they judge: comparison mode reports its verdicts beside the gate's
(Comparison mode), and the notebook derives the scatter thresholds from the validation visits
(QA thresholds), written to dmLineResidualThresholds-<run>.yaml for
thresholdsFile.
minLines: fewest lines a species needs to be measured. Default10.lineFlags:ReferenceLineStatusnames that exclude a line. Default["BAD"].maxTraceRowsPerFiber: mostUSED, and mostRESERVED, trace rows per fiber stored indmLineResidualData, evenly spaced in y;0for all. Default100. Lamp and sky lines andUNFITTEDrows are always stored; the statistics use every line, so a trace statistic recomputed from the table is an estimate.thresholdsFile: a thresholds file judged before the fields below; entries may namereference,statusType,arm,spectrograph,description,cameraSetandobsType. DefaultdmLineResidualThresholds-run25.yaml, adopted bymakeLineResidualThresholds.pybeside it; empty for the fields alone.xWarnThreshold,xFailThreshold,yWarnThreshold,yFailThreshold: on the median offsets from the calibration, in pixels. Defaults1.0,2.0,1.0,2.0.xRmsWarnThreshold,xRmsFailThreshold,yRmsWarnThreshold,yRmsFailThreshold: on the robust RMS ofRESERVEDlines against the visit's detectorMap, in pixels. Default NaN: not judged.
| DataSet Type | Dimensions | Description |
|---|---|---|
dmLineResidualMetrics |
instrument, visit, arm, spectrograph |
Long format: one row per reference, species, statusType and metric, with obsType and the metric's verdict. |
dmLineResidualSummary |
instrument, visit, arm, spectrograph |
One row: qaStatus, qaDecidedBy, qaReason, recommendedAction, fitted, obsType, and the calibration used. |
dmLineResidualData |
instrument, visit, arm, spectrograph |
One row per line measured (USED and RESERVED traces sampled), with dxCalib, dyCalib, dxVisit, dyVisit. |
dmResiduals and dmCombinedResiduals were retired on 2026-10-22 (PIPE2D-1956). Where their outputs went:
| Before | Now |
|---|---|
dmQaResidualStats, a row per status_type and species |
dmLineResidualMetrics, long format; lineResiduals.wideResiduals gives a column per metric. reference visit is what dmResiduals measured against; calib is new. |
spatial.median, spatial.robustRms |
medianDeltaX, robustRmsDeltaX |
wavelength.median, wavelength.robustRms |
medianDeltaY, robustRmsDeltaY |
weightedRms, softenFit |
not recomputed: drp_stella's own, in iqQaSpeciesMetrics |
num_lines, per fiber |
nLines (lines of the species) and linesPerFiber |
| the verdict | dmLineResidualSummary: qaStatus, qaDecidedBy, qaReason, recommendedAction, the calibration used |
dmQaResidualData |
dmLineResidualData, with a residual against each reference |
dmQaResidualPlot |
nothing per quantum: drawn from dmLineResidualData after the fact (PIPE2D-1920) |
dmQaDetectorStats, dmQaCombinedResidualPlot |
comparison mode's report, Line residuals section; bin.src/exportMetrics.py for the tables |
Measures image quality (FWHM), spatial flexure, and arc-line flag rates on a per-detector quantum
(instrument, visit, arm, spectrograph). The task classifies each visit by reading FITS headers (W_SEQTYP,
W_SEQNAM) and routes it to the appropriate measurement path. When reduceExposure logs are available it also folds
ISR, cosmic-ray, and fitDetectorMap statistics into the metrics table.
The task writes data only; plotting lives in pfs.drp.qa.plotting.iqQa and is driven after the fact by
bin.src/plotIqQaTimeSeries.py (see
Command-line tools).
| Connection | Dataset | Required | Description |
|---|---|---|---|
arcLines |
lines |
Yes | Per-line arc emission measurements from fitDetectorMap, including second moments (ixx/iyy), flux, flags, and species. |
detectorMap |
detectorMap |
Yes | Adjusted mapping from (fiberId, wavelength) to detector (x, y). Used to enrich arc lines with wavelength and trace position. |
fiberProfiles |
fiberProfiles |
Yes (calib) | Stored fiber profile shapes. Used as a last-resort fallback to derive trace-width FWHM when neither arc lines nor a calexp are available. |
detectorMapCalib |
detectorMap_calib |
Yes (calib) | Static, pre-adjustment detectorMap. Differencing its xCenter against the measured fiber positions gives the flexure diagnostic dxCenter. |
calexp |
calexp |
No | Post-ISR calibrated image. When present, trace widths are measured directly from its pixels (each fiber fitted with its neighbours) — the primary path for trace/quartz visits and a fallback for sparse arc visits. |
pfsConfig |
pfsConfig |
No | Fiber configuration for the visit. When provided alongside calexp, restricts FWHM measurement to FLUXSTD+GOOD fibers to avoid contamination from sky fibers. Also supplies W_SEQTYP metadata when calexp is absent. |
isrLog |
isr_log |
No | ISR task log. Parsed for bad-pixel counts and runtime. |
cosmicrayLog |
cosmicray_log |
No | Cosmic-ray task log. Parsed for CR counts, affected pixels, and runtime. |
reduceExposureLog |
reduceExposure_log |
No | reduceExposure log. Parsed for fitDetectorMap chi², RMS, softening, per-species and per-fiber statistics. |
All five optional connections are declared minimum=0, so the task still runs when they are absent from the input
collection.
The task classifies each visit from the W_SEQTYP FITS header and selects the best available measurement path in
descending preference order:
| Visit type | Primary path | Fallback |
|---|---|---|
Regular arc (scienceArc, all fibers) |
Arc-line second moments (ixx/iyy → FWHM) |
calexp trace widths if nGoodLines < minGoodLines |
| IIS arc (16 engineering fibers) | — | Reported as sparse (no FWHM); arc catalog does not match IIS positions |
Regular trace/quartz (scienceTrace, all fibers) |
calexp trace widths |
Fiber profile calibration widths if the calexp is absent or unusable |
| IIS trace/quartz | — | Reported as sparse; too few fibers for a reliable measurement |
Science / all-sky (scienceObject*) |
calexp trace widths of FLUXSTD+GOOD fibers |
Sparse if FLUXSTD good fraction < minFluxstdGoodFrac |
Unknown (W_SEQTYP absent) |
Heuristic: arc lines if sufficient, else calexp, else fiber profiles |
— |
For arc visits, FWHM is computed as the Gaussian-equivalent width 2√(2 ln 2) × σ from the second moments of each
arc-line profile. On the calexp, every profileYStride-th row is fitted with pfs.drp.qa.crossDispersion: each fiber
jointly with its neighbours, as pixel-integrated Gaussians sharing one width, plus a background, with no aperture
(fibers are 6.17 px apart). The fiberProfiles fallback reads each profile's width at half its maximum. Every FWHM
includes the pixel response, as the arc-line moments do; iqQaData.fwhmIntrinsic leaves it out.
The PFS trace profile is not Gaussian, so these readings were chosen by comparing them on real quartz
(docs/trace-widths.ipynb, PIPE2D-1918): the joint fit recovers the profile's half-maximum
width, a Gaussian fitted to the profile understates it by 8–10 %, and drp_stella's second moment
(calculateStatistics().width) varies between detectors whose profiles agree.
medFwhm— Median FWHM in pixels across all good measurements for the detector quantum. Reflects the cross-dispersion width of the fiber PSF. Typical good values (Run25) are 2.5–3.2 px on arcs and 3.1–3.4 px on quartz; values above 3.5 px indicate a focus or alignment problem (SM1 defocused in Run27 read 4.1–4.5 px on quartz).medDxCenter/dxCenterRms— Median and scatter of the spatial offset of the measured fiber positions from the static calibration detectorMap (measured minus calibration, as drp_stella's residuals), in pixels. This is a flexure diagnostic: a large|medDxCenter|means the whole detector has shifted relative to the calibration, while a largedxCenterRmsat small median suggests a distortion rather than a bulk shift.NaNwhen nodetectorMap_calibis available.pctFlagged— Percentage of arc lines flagged byfitDetectorMap(below S/N threshold or otherwise rejected). High flag rates on b-arm Ar/Xe/Kr arcs are expected (lamp physics — those species have very few bright lines in the blue) and do not indicate an optical problem. High flag rates on HgCd or Ne arcs, or on r/n/m arms, are more likely to reflect genuine issues.pctFlaggedis set toNaNfor IIS frames, sparse visits, and science/FLUXSTD paths where the flag rate reflects exposure depth rather than optical quality.- Flag breakdown — When
pctFlaggedis finite, the same rejections are broken out byReferenceLineStatusbit:pctNotVisible,pctBlend,pctSuspect,pctRejected,pctBroad. On the arc-line path two measurement-level categories are added:pctLowSN(flagged with no flux measured — the fit never got that far) andpctMeasFail(flagged despite a finite flux — a centroid or photometry failure). The split matters:pctLowSNis usually lamp physics,pctMeasFailusually is not. nLines— Rows of thelinesdataset, whichever path measures the FWHM: a row for every line of the lamp's list on everyGOODfiber, flagged when not measured, and the trace centroidsfitDetectorMapfound, on an arc as on a quartz. Not judged: it follows how many fibers the design lights, and on an arc the trace centroids, whose number follows the illumination and the pipeline rather than the lamp (PIPE2D-1935).linesPerFiber— The judged count: unflagged lamp lines per lit fiber on an arc, unflagged trace centroids per lit fiber on a quartz (about 4,160, one per detector row);NaNon other frames or without a pfsConfig. A design that lights one fiber group (a shaka exposure: every fourth cobra, the others parked behind their black dots; seedocs/qa-domain-notes.md) is judged like a full slit; an obstructed frame, lit by its design but dim, still fails. On Run30's daily neon, a one-group arc measures the same lines on each lit fiber as a fully lit one.nLitFibers,nFibers— The detector's fibers the pfsConfig lights (fiberStatusGOOD, engineering fibers excepted: the only oneslineshas rows for), and those it lights or hides behind a black dot (BLACKSPOT).pctFlaggedon the arc-line path is taken over the lamp lines of the lit fibers, and the calexp trace width over the lit fibers.nTraceRowscounts the trace centroids inlines.traceOnly—Truewhen FWHM comes from fiber profile calibration widths rather than live measurements; these values reflect the calibration epoch, not the current visit, somedFwhmis then neither judged nor used to derive thresholds, andpctFlaggedisNaN. A quartz falls back to them whenever its calexp gives no usable measurement.obsType/seqName— Visit classification (arc,trace,science,allsky,twilight,dark,unknown) and the rawW_SEQNAMstring (e.g."Arc: HgCd") it was derived from. Twilight and dark frames are not measured.qaStatus—PASS,WARN, orFAIL: the worst verdict of the metrics judged (see thresholds below), frompfs.drp.qa.metrics.gate.gate;UNKNOWNwhen no metric was judged.qaDecidedBynames the first metric with that verdict (empty forPASS), andqaReasongives everyWARNandFAILreason; it is also on the task log'sIQ QAline.
New thresholds are derived from the validation visits: see QA thresholds and the validation visit set.
Verdicts come from pfs.drp.qa.metrics.gate.gate, the one gating path, which also re-judges stored iqQaMetrics.
Each metric is judged by the thresholdsFile entry for its population, and by the config fields below where the file
has none. thresholdsFile is an absolute path, or one relative to pfs/drp/qa/metrics/data; empty, the config fields
judge alone.
The default, iqQaThresholds-run25.yaml, holds the thresholds derived from the validation visits, as adopted
(PIPE2D-1917, PIPE2D-1918, PIPE2D-1942, PIPE2D-1938) by makeRun25Thresholds.py beside it; each entry records its
provenance. They were derived from u/wtg/qa-thresholds/010, every validation visit reduced afresh with the pipeline
the file records (datamodel w.2026.40, obs_pfs w.2026.40, drp_stella w.2026.41-6-ge56938d2). The reference is Run25,
except that b2 and n4 quartz take Run29's detectorMap set (see QA thresholds).
medFwhmper detector and lamp: WARN 3 % and FAIL 5 % above the reference known-good median of the image's arm, spectrograph,obsTypeand lamp (species), on arcs and quartz (from the calexp width), every arm included. The spectrographs of one arm differ from each other by more than any one of them varies (r quartz: r2 3.27 px, r4 3.39 px, each within 0.03 px), and the lamps of one arm differ even more (b arcs: Argon 2.5 px, Krypton 2.85 px). b-arm Argon is not judged: under a line per lit fiber, its median width rests on a handful of lines and the known-good exposures scatter −4 to +2 % about their median, so the SM1 defocus is caught on the other lamps.medFwhmper arm, judging only an image whose detector and lamp have no reference median: arcs b 2.872/2.89, r 2.988/2.993, n 3.05/3.1, m 3.18/3.21 px; quartz b 3.33/3.34 (Run25 and Run29's b2 pooled), r 3.391/3.392, n 3.269/3.272 px, and m not judged (16 known-good values, below the 20-sample floor).linesPerFiberper arm and sequence (seqName), lower is worse (PIPE2D-1935): a tolerance below the reference known-good median, 25 %/50 % on arcs and 2.5 %/5 % on quartz. Known-good arcs reach 19 % below their median (n HgCd) and quartz 1.1 %; the obstructed Run30 frames sit 60–98 % below on arcs and 9–56 % on quartz. b Argon, under a line per lit fiber, is not judged.nLinesis not judged: it counts rows, mostly trace centroids on an arc, on only the fibers a design lights. Its Run25 thresholds also failed some known-good Run25 arcs.pctFlaggedis not judged: one entry with neither WARN nor FAIL, which also overrides the config fields. Run25's derivation had 5 visits per population, and the config's n-arm 15/20 % sits below the known-good n-arm Neon sets of Run25 (34 %) and Run30 (26–35 %), as comparison mode showed (PIPE2D-1929). It stays unjudged until there is a valid reference.
The config fields:
| Metric | WARN | FAIL |
|---|---|---|
medFwhm |
≥ 3.2 px (fwhmWarnThreshold) |
≥ 3.5 px (fwhmFailThreshold) |
|medDxCenter| |
≥ 1.0 px (dxCenterWarnThreshold) |
≥ 2.0 px (dxCenterFailThreshold) |
pctFlagged |
per-arm / per-lamp, see below | per-arm / per-lamp, see below |
pctFlagged thresholds are DictFields whose keys are either an arm letter (b) or an
arm:species pair (b:HgCd), where the species is the part of W_SEQNAM after the colon. Lookup order is
arm:species → arm → a built-in default of 15 % (WARN) / 20 % (FAIL). Set a threshold to 100 to disable it.
| Key | WARN | FAIL | Rationale |
|---|---|---|---|
b |
50 % | 60 % | Blue arm, no lamp identified |
b:HgCd |
15 % | 25 % | HgCd has excellent b-arm coverage, so flags are genuine |
b:Neon |
50 % | 60 % | Dense and crowded in the blue |
b:Krypton |
55 % | 65 % | Most b-arm Kr lines are extremely faint |
b:Xenon |
85 % | 92 % | Very faint in the blue — lamp physics, not optics |
b:Argon |
93 % | 97 % | Almost no usable b-arm Ar lines |
r, n, m |
15 % | 20 % | Full lamp coverage; high flag rates are real |
The per-lamp b-arm values are deliberately permissive: several arc species have very few or very faint lines between 400
and 650 nm, so fitDetectorMap's global S/N cut rejects almost all of them regardless of image quality. Treating that
as a failure would flag every Ar and Xe arc. See the "Arc Lamp Physics" section of
docs/qa-domain-notes.md for the underlying line counts. That section also describes a
per-species S/N option (minSignalToNoisePerSpecies) that would address the cause, but it is proposed only — it
does not exist in drp_stella, so the permissive thresholds above are the current mitigation.
imageQualityQa:minGoodLines: Minimum good arc-line measurements to trust the arc path. Default10.imageQualityQa:minPeakSN: Minimum significance of a calexp trace (fitted flux over its error). Default5.0.imageQualityQa:maxCalexpFlagRate: Max fraction of bad calexp samples before rejecting the calexp path. Default0.5.imageQualityQa:minFluxstdGoodFrac: Min fraction of good FLUXSTD samples for the stellar calexp path. Default0.10.imageQualityQa:profileHalfWidth: Deprecated and ignored; the calexp width uses no aperture.imageQualityQa:profileYStride: Row sampling interval (pixels) for calexp trace widths. Default100(about 40 rows); the fit's cost is proportional to the rows.
imageQualityQa:fwhmWarnThreshold: Median FWHM (px) above which status isWARN. Default3.2.imageQualityQa:fwhmFailThreshold: Median FWHM (px) above which status isFAIL. Default3.5.imageQualityQa:dxCenterWarnThreshold:|medDxCenter|(px) above which status isWARN. Default1.0.imageQualityQa:dxCenterFailThreshold:|medDxCenter|(px) above which status isFAIL. Default2.0.imageQualityQa:flagRateWarnThreshold:pctFlagged(%) threshold forWARN, keyed byarmorarm:species. See the threshold table above for defaults.imageQualityQa:flagRateFailThreshold:pctFlagged(%) threshold forFAIL, keyed byarmorarm:species. See the threshold table above for defaults.imageQualityQa:thresholdsFile: Thresholds file judged before the fields above. DefaultiqQaThresholds-run25.yaml; empty for the fields alone.
DictField values cannot be set with dot notation on the command line; assign the whole dict as a Python literal:
-c "imageQualityQa:flagRateWarnThreshold={'b': 50.0, 'r': 15.0, 'b:Argon': 93.0}"| DataSet Type | Dimensions | Description |
|---|---|---|
iqQaData |
instrument, visit, arm, spectrograph |
Per-line or per-trace-sample measurements: fiberId, x, y, lam, fwhm, fwhmIntrinsic (calexp only), theta, dxCenter, flux, fluxErr, flag, traceOnly, peakRatio, snr (calexp only), status, plus visit/arm/spectrograph. |
iqQaMetrics |
instrument, visit, arm, spectrograph |
Single-row summary; see below. |
iqQaSpeciesMetrics |
instrument, visit, arm, spectrograph |
Per-species fitDetectorMap statistics from the reduceExposure log, one row per species and metric: visit, arm, spectrograph, description (line species, e.g. NeI, or Trace), metric (fitXRms, fitYRms: weighted RMS; fitXSoften, fitYSoften: softening), value. |
iqQaMetrics columns, by group:
- Identity —
visit,arm,spectrograph,obsType,seqName,calexpRun(the RUN collection of the calexp judged, whosepfsArmrecords the reducing pipeline; null without a calexp) - Image quality —
medFwhm,medDxCenter,dxCenterRms,pctFlagged,nLines,traceOnly,qaStatus,qaDecidedBy,qaReason - Flag breakdown (only when
pctFlaggedis finite) —pctNotVisible,pctBlend,pctSuspect,pctRejected,pctBroad, and on the arc-line pathpctLowSN,pctMeasFail - Log-derived (zero/
NaNwhen the corresponding log is absent) —isrBadPixels,isrTime,cosmicRayCount,cosmicRayPixels,cosmicRayTime,reduceExposureTime fitDetectorMapstatistics —fitChi2,fitDof,fitXRms,fitYRms,fitXSoften,fitYSoften,fitNLines,fitTotalLines, the matchingfitReserved*columns for the reserved-line sample,fitTraceXRms,fitTraceYRms; the per-species values are iniqQaSpeciesMetrics- Per-fiber arrays —
fiberIds,fiberXRms,fiberYRms,fiberNLines
There is no iqQaPlot output: the task writes data only, and plots are produced separately by
bin.src/plotIqQaTimeSeries.py.
Determines the quality of the fiber extraction.
extractionQa:fiberWidth: Half width of a fiber region (pix), default3.extractionQa:plotMinChiMed: Minimum median Chi to plot, default-1.5.extractionQa:plotMaxChiMed: Maximum median Chi to plot, default1.5.extractionQa:plotMinChiStd: Minimum standard deviation of Chi to plot, default0.0.extractionQa:plotMaxChiStd: Maximum standard deviation of Chi to plot, default3.5.extractionQa:plotMinChiAtPeak: Minimum Chi at peak to plot, default-1.5.extractionQa:plotMaxChiAtPeak: Maximum Chi at peak to plot, default1.5.extractionQa:plotMinResFrac: Minimum residual fraction, default-5.0.extractionQa:plotMaxResFrac: Maximum residual fraction, default5.0.extractionQa:plotHistRangeScale: The scale factor for the Chi histogram range, default1.5.extractionQa:plotHistNbin: The number of bins for the Chi histogram, default100.extractionQa:targetType: Target type for which to calculate statistics, default["^ENGINEERING"].extractionQa:figureDpi: Resolution of plot for residual, default72.
| DataSet Type | Dimensions | Description |
|---|---|---|
extQaStats |
instrument, visit, arm, spectrograph |
Results of the residual analysis of extraction against fiberId are plotted (Chi_median, Chi_stddev, Chi_atProfilePeak, residual_fraction). |
extQaImage |
instrument, visit, arm, spectrograph |
Summary plot for 2D residual, chi, etc. (p.1), 2D image of chi (p.2), zoom-in of the chi image (p.3), and the histogram of residual fraction and chi (p.4) |
extQaImage_pickle |
instrument, visit, arm, spectrograph |
Statistics of the residual analysis. |
extractionQaCombined:plotMinChiMed: Minimum median Chi to plot, default-1.5.extractionQaCombined:plotMaxChiMed: Maximum median Chi to plot, default1.5.extractionQaCombined:plotMinChiStd: Minimum standard deviation of Chi to plot, default0.0.extractionQaCombined:plotMaxChiStd: Maximum standard deviation of Chi to plot, default3.5.extractionQaCombined:plotMinChiAtPeak: Minimum Chi at peak to plot, default-1.5.extractionQaCombined:plotMaxChiAtPeak: Maximum Chi at peak to plot, default1.5.extractionQaCombined:plotMinResFrac: Minimum residual fraction, default-5.0.extractionQaCombined:plotMaxResFrac: Maximum residual fraction, default5.0.extractionQaCombined:targetType: Target type for which to calculate statistics, default["^ENGINEERING"].extractionQaCombined:figureDpi: Resolution of plot for residual, default72.extractionQaCombined:footnoteSize: Fontsize of the footnote, default9.
| DataSet Type | Dimensions | Description |
|---|---|---|
extQaStatsCombined |
instrument, visit, arm |
Results of the residual analysis of extraction plotted against fiberId for all spectrographs (left) and PFI positions (right). |
Plot the fiber normalization for the given detector and visit.
fiberNormsQa:plotLower: Lower bound for plot (standard deviations from median), default 2.5.fiberNormsQa:plotUpper: Upper bound for plot (standard deviations from median), default 2.5.
| DataSet Type | Dimensions | Description |
|---|---|---|
fiberNormsPlot |
instrument, visit, arm |
Plot of the fiber normalizations for a visit. |
fluxCalQa:filterSet: Filter set to use, defaultps1.fluxCalQa:includeFakeJ: Include the fake narrow J filter, defaultTrue.fluxCalQa:fakeJoffset: Offset from the ps1 bands for the fake narrow J, default0.054.fluxCalQa:diffFilter: Filter to use for the color magnitude difference, defaultg_ps1.fluxCalQa:doAnonymize: Mask sensitive information in the plot, defaultTrue.
| DataSet Type | Dimensions | Description |
|---|---|---|
fluxCalStats |
instrument, visit |
Statistics of the flux calibration analysis. |
fluxCalMagDiffPlot |
instrument, visit |
Plot of the flux calibration magnitude difference. |
There are two tasks in the Sky Subtraction QA, the skyArmSubtractionQa and skySubtractionQa. The
skyArmSubtractionQa task is used to subtract the sky from the spectra of each arm and the skySubtractionQa task is
used to plot the results of the sky subtraction for the entire visit.
If config options are not passed, the default values come from mergeArms_config.fitSkyModel.
skyArmSubtractionQa:blockSize: Block size for the sky subtraction, defaultNone.skyArmSubtractionQa:rejIterations: Number of rejection iterations, defaultNone.skyArmSubtractionQa:rejThreshold: Rejection threshold, defaultNone.skyArmSubtractionQa:oversample: Oversampling factor, defaultNone.skyArmSubtractionQa:mask: Mask types to use, defaultNone.
| DataSet Type | Dimensions | Description |
|---|---|---|
skySubtractionQaPlot |
instrument, visit, arm |
PDF of various plots related to sky subtraction built from all the arms for the visit. |
Thresholds are derived from data, by a fixed procedure, and checked against visits whose verdict is known. The decisions behind the current set:
- Run25 is the reference run. Its calibrations were made for it, and it is the most complete stable set. Every threshold is derived from Run25's known-good visits.
- The validation set holds Run25's known-good visits, and Run29's for two detectors' quartz (below). From other runs it holds only known-bad visits, as fault examples: each is checked against the thresholds, and none derives one.
- A set is never its own reference. The gate decides whether a run's calibration set should be used to build its
calibrations, so a set counts as known-good only once the calibrations built from it have proved out (used, with good
reductions), and then only for later runs. Each detector's reference is chosen from such sets (PIPE2D-1938): reduced
with one pipeline, Runs 25–30 agree with Run25 to within 3 % in
medFwhmeverywhere but two cameras, so Run25 stays the reference except for b2 quartz (SM2's slit was refocused on 2026-03-11 and its quartz width stepped from 3.12 to 3.34 px, where it has stayed) and n4 quartz (Run25's 3.10 px is the outlier against a steady 3.22 px since), which take Run29's detectorMap set (cameras/excludeCamerasin the YAML;referenceRuns: [25, 29]). The n-arm verdicts were provisional while Run25's n-arm reductions predated the pipeline fixes; reduced afresh, the n arm agrees with Run25 like the others. Adopted 2026-10-19 fromu/wtg/qa-thresholds/010: re-judged with the new file, Runs 28, 29 and 30 go from 41, 57 and 32 flagged images to 1, 3 and 3, the rest being single arcs and n4's Neon arcs, whose width has crept from +1.5 % (Run26) to +4.7 % (Run30) of Run25's while its quartz stayed put. - Other runs are compared, not used for derivation. Comparison mode (PIPE2D-1929) judges every visit of a run against Run25's thresholds; Run30 is the first. Run27 is not a fair comparison: no calibrations were made from it, and its n-arm flag rates sit 2–5 points above Run25's.
imageQualityQajudges by the adopted Run25 file (iqQaThresholds-run25.yaml):medFwhmon arcs and quartz, andlinesPerFiber. Flag rates (pctFlagged) are not judged until there is a valid reference; quartzmedFwhmis measured from the calexp width (PIPE2D-1918); twilight is not measured. Details under Pass/Warn/Fail Thresholds.medFwhmis judged against each detector's own Run25 width, per lamp (PIPE2D-1942): WARN 3 % and FAIL 5 % above the known-good median of its arm, spectrograph,obsTypeand lamp. Per-arm percentiles put each arm's widest spectrograph, or broadest lamp, at FAIL, and per detector and lamp there are only 4–14 known-good values, too few for a tail percentile but plenty for a median. The 3 % and 5 % are set from the data, not from an image-quality requirement: Run25's known-good values sit at most 1.4 % above their median (3.3 % for Argon arcs, whose b, r and m widths scatter most), healthy detectors moved −3 to +4 % between Run25 and Run30, and the SM1 defocus reads +24 to +41 %. On Run25 no known-good arc or quartz fails and one warns (135841 m1 Argon); every known-bad one fails.dmLineResidualsjudges drift by tolerance, scatter by derived thresholds (PIPE2D-1921, PIPE2D-1956). The median offsets fromdetectorMap_calibare judged at WARN 1 px and FAIL 2 px, a physical limit short of drp_stella's 3 px line-search radius: offsets from a calibration sit near zero in the run it came from, so no percentile applies. The robust RMS ofRESERVEDlines against the visit's own detectorMap is derived in the notebook like the other percentile thresholds, per arm, species, camera set and observation type, and as a tolerance per detector, from the known-good visits of each arm's reference run, and adopted through the task'sthresholdsFile(2026-10-20, fromu/wtg/qa-thresholds/010): trace centroids on quartz frames and the Ar, Kr, Ne and Xe lines are judged in x per detector by a tolerance, WARN 20 % and FAIL 50 % above the detector's known-good median, asmedFwhmis, and the lamp lines in y at WARN 50 % and FAIL 150 % (their y moves more between runs: the healthy runs' arcs reach 1.4× Run25's medians at the 95th percentile and 2.4× at worst, Run27's faults 3–70×). A detector's quartz trace scatter repeats to about 1 % within a run and differs 20 % between detectors, so a percentile over the pooled detectors is set by the worst one and cuts through the best one's cluster, and a percentile over one run's arcs does not transfer to the next (thresholds from Run29's arcs flagged 20–70 % of Runs 25–28's healthy arcs); against the per-detector median, the regular trace sets and arcs of Runs 25, 26 and 28–30 fail at most one image a run and warn on a few percent (b1 and r1 traces at +24 % at the end of Run26, b-arm Xenon arcs), and the Run27 SM1 defocus fails at 1.7–4×. The calibration's own input visits are kept in the scatter derivation (they sit at zero against the calibration, which is the drift's concern, not against the visit's own map). Not judged, with the reasons in each entry's provenance: the mercury and cadmium lines, whose y scatter is intrinsically variable; trace centroids on arcs and twilights, which scatter two to three times more on the faint continuum; and the twilights' sky species.bin.src/judgeScatterThresholds.pyis the acceptance check, run on the comparison exports of Runs 25–30 before any adoption, engineering sequences set apart; its findings are indocs/qa-domain-notes.md.dmResiduals, which it replaced, was retired on 2026-10-22 after a comparison on Run30's fresh reductions: medians agree to under 0.002 px and robust RMS to 5–14 % (dmResidualsclipped), and the two tasks measure the same RMS on every imagedmLineResidualsflagged.- b-arm populations are not split by camera set (PIPE2D-1927). The camera set (
cameraSet, the arms a visit read) is stored withdmLineResiduals' rows and is a population column of the scatter thresholds, but its one effect is the b arm's offset fromdetectorMap_calibwhen read with m (−0.45 to −0.47 px in Runs 25 and 28–30), whichmedianDeltaXmeasures and the 1 px drift WARN absorbs. The b-arm trace scatter does not depend on it: on quartz frames, b read with m and with r agree within 3 % per detector within a run (Runs 25, 26, 28 and 29; Run30 +13 %, on 5 images read with m), so the per-detector scatter tolerances are not split by it. Pooled over runs the two differ, because the scatter moves twofold between runs (0.025–0.054 px) and the camera sets are not spread evenly over them: compare them within a run.iqQaMetrics'medDxCenteris superseded bydmLineResiduals'medianDeltaX. - Nothing measured is
UNKNOWN, notPASS. A FWHM read from thefiberProfilescalibration is not a measurement and is never judged. - Thresholds hold for the pipeline that reduced their visits (PIPE2D-1938). Two reductions of the same Run25
exposures put n-arm quartz widths up to 53 % apart, so a verdict can follow the reduction rather than the
instrument. The thresholds file records that pipeline (
pipelineVersions: datamodel, obs_pfs and drp_stella, as drp_stella'sgetPfsVersionswrites them into eachpfsArmheader). Given it, the gate marks a verdict on a reduction made with another pipeline, or an unknown one, provisional (qaProvisional, with the reason inqaPipeline); comparison mode reads each reduction's versions from thepfsArmin the RUN of the calexp judged (calexpRun), so a later reduction of the same detector can't stand in for it, and counts the provisional verdicts in its report. Metrics written beforecalexpRunread as an unknown pipeline until judged again. A file that records no pipeline makes every verdict provisional. - Re-baseline at each summit stack upgrade. The live gate judges
drpActor's reductions, made with the stack deployed at the summit, so a new stack needs thresholds of its own:- Reduce the reference sets fresh with the new stack:
qaComparison.py run --period run25 --fresh --cadence set(and the other reference runs). - Re-derive the thresholds from those reductions with
docs/qa-thresholds.ipynb, which records their pipeline. - Compare the new thresholds with the old, metric by metric, and explain any that moved.
- Adopt the new file; until then, verdicts on the new stack's reductions are provisional.
- Reduce the reference sets fresh with the new stack:
The detail:
docs/qa-thresholds.ipynb— the procedure, step by step, as a notebook run top to bottom: runimageQualityQaover the validation visits, derive the thresholds, review them and adopt them.docs/validation-visits.md— which visits are in the validation set, why, and how to change it.docs/qa-principles.md— the rules every metric and threshold follows, and the checklist for a PR that adds one.
pfs.drp.qa.guiders analyses the AG (guider) cameras' data in notebooks: guide errors, the boresight and
per-camera models, drift, focus and image sizes. It replaces pfs.drp.stella.utils.guiders, and needs pfs_utils
but not the LSST stack.
queriesreads the opdb (and, for INST-PA and early M2_OFF3, a butler). It is the only module that does.analysisfits and averages;plottingdraws. Both take DataFrames and return their results, leaving their inputs alone.coordinatesholds the conventions: positions in hardware coordinates, offsets as a center minus a named reference in microns, units in the column names. 1 AG pixel is 13 µm, or 0.14 arcsec.
from pfs.drp.qa.guiders import analysis, plotting, queries
from pfs.utils.database.opdb import OpDB
opdb = OpDB(host="pfsa-db", user="public_user")
agcData = queries.readAgcData(opdb, range(148284, 148292))
plotting.showAgcErrorsForVisits(agcData)
fit = analysis.fitGuiderModel(agcData, analysis.GuiderFitConfig(maxGuideError_um=100))
plotting.showGuiderErrors(fit, plotting.GuiderPlotConfig(guideStarFrac=0.3))
plotting.plotFocus(agcData, plotBy="agc_exposure_id", showFocusSets=True)Each plot draws on a new figure, or on the fig, ax or axes it is given, and returns a GuiderPlot holding
the figure, its panels, the artists and the colorbars. Given the colorbars of an earlier call, it updates them,
so a figure can be redrawn in place. The notebook docs/guider-plots.ipynb describes
each plot (what it shows, where its data come from, how to read it and what to look for) beside an annotated sample.
It runs on the tests' AG data or, with USE_OPDB = True, on the opdb, where it also checks the readers and fits the
plots don't use.
Coming from drp_stella (docs/guiders-migration.ipynb runs each of its calls
beside the replacement):
- Fit, then plot.
GuiderConfigis split intoanalysis.GuiderFitConfigandplotting.GuiderPlotConfig. drp_stella'sestimateGuideErrors(plot=True),plotDriftRateandcompareAGCPfsUtilsbecomeanalysis.estimateGuideErrors,analysis.fitDriftRateandanalysis.comparePfsUtilsPositions, whose resultsplotting.plotGuideErrors,plotting.plotDriftRateandplotting.plotPfsUtilsComparisondraw. - The plots take AG data, not an opdb connection. Cameras are
agcCameraIds, 0-5, rather thanAGC, 1-6, and the cursor readout takes design names by visit (designNames). - The readers become
queries.readAgcData;compatkeeps their names, deprecated.
A raster scan visit holds two pointings: once its shutters close, the telescope moves to the next position. The
boresight reference, and the plots using it, are only right for its shutter-open rows (shutter_open == 1)
(PIPE2D-1910).
bin.src/qaComparison.py gates every image of an observing run, or of the calibrations taken before it, against
the reference thresholds, and writes a report comparing the run with Run25 (pfs.drp.qa.comparison).
- The visits come from the opdb, not from the hand-written run summaries: every SpS visit between the
period's dates, with its sequence,
cmd_str, cameras, design, obslog notes and telescope status. The periods (each run's nights and its pre-run calibration nights, HST, a night running noon to noon) are incomparison/data/observingRuns.yaml, derived from gaps in SpS activity. - Calibration or science is what the exposure is, not when it was taken: every sequence type but
scienceObjectandscienceObject_windowedis a calibration, and those are science when their design has a science proposal. Twilight frames, dithers and telescope focus sweeps are on engineering designs, so they are calibrations. - Everything measurable is judged; only validated types are gated.
scienceArc,scienceTraceandscienceObjectare gated: their thresholds were derived from and checked against such visits. Every other typeimageQualityQacan measure (engineering arcs, flats, fiber profiles, focus sweeps, windowed readouts, new types, and the engineering sequences taken as a validated type: bootstraps, IISImageQualityframes, health checks, line checks and detectorMap tests, by sequence name) is judged the same way but labelled unvalidated, in its own pass and its own report section: aFAILthere may be what an engineering test expected. Biases and darks have no method yet; test exposures are listed only. - n-arm darks are listed with the last lit exposure before them, from the opdb alone: the n detectors keep an image
of a bright exposure for a while, so the darks taken soonest after an arc or quartz are the ones to check for
persistence until darks are measured (PIPE2D-1925). The report shows each night's n-arm exposures on a
timeline, colored by what lit them, beside the gap before each dark sequence;
darksLastLit-<period>.csvhas every dark. - Daily arcs and traces are reported apart from sets. A calibration set is 3 exposures per arc lamp and 10
traces; one-visit sequences of its sequence group (
group_id) belong to it. Another one-visit sequence is daily when its name recurs on 4 or more nights (from Run28, a neon arc and a trace, and IIS frames, each day, for drift), and a one-off single otherwise (positioner scans, bootstrap exposures), judged like a set. All are judged against the thresholds derived from Run25's sets. Daily ones are often taken with one fiber group lit:linesPerFiberandpctFlaggedare taken on the lit fibers, so they are judged like sets; metrics measured before that (nonLitFibers) skipnLinesandpctFlaggedon them (PIPE2D-1935). Run30's nightlyImageQualityframes are IIS, reported sparse until PIPE2D-1954. - Sky visits are labelled from the telescope status: a focus sweep (10 or more focus offsets spanning at least
0.5 mm on one field in one night) changes how much light enters the fibers but not the line widths, so its
nLines,linesPerFiberandpctFlaggedaren't judged; dithered visits are marked. - Findings are written from the data. Each
WARNorFAILimage gets the metrics that crossed, how far the problem spreads (detector, arm, spectrograph, most of the visit, the whole sequence), the setup and the opdb's notes beside it. Notes never decide a verdict. - Line residuals are judged beside the gate (PIPE2D-1956).
dmLineResidualsruns in the same passes asimageQualityQa, and the report's Line residuals section has its verdicts per detector and night, the recommended actions by arm, its problems and findings (the reference, species and metric of each crossing), and the scatter of the lines the fit held back, by arm, observation type and species (the b arm split by whether it was read with m or with r), beside the reference run's: what a scatter threshold is derived from. Tables stored beforeobsTypewas a column, or that storedunknown, take it fromiqQaMetrics.dmLineResidualSummary-<period>.parquet,dmLineResidualMetrics-<period>.parquetandlineResidualFindings-<period>.csvare written beside the report. A collection judged before the task joined the pipeline has none, and the section says so.
Four steps, each safe to repeat. Files go under --data-dir: opdb reads in opdb/, everything else in
<run>/qa/<drp_qa version>/; the report shows science visits, so keep it local.
python bin.src/qaComparison.py fetch --period run30 --data-dir ~/Projects/Subaru/PFS/data # opdb, read-only
python bin.src/qaComparison.py plan --period run30 --data-dir ~/Projects/Subaru/PFS/data # coverage, pipetask commands
python bin.src/qaComparison.py run --period run30 --data-dir ~/Projects/Subaru/PFS/data # reduce what's missing, judge
python bin.src/qaComparison.py report --period run30 --data-dir ~/Projects/Subaru/PFS/data # report-run30.htmlplan prints the pipetask commands as Python lists, for running from a notebook instead; -j sets the
pipetask processes (default 8). --cadence set (or daily, single; repeatable) judges only the arcs and traces
of those cadences, e.g. the calibration sets that thresholds are derived from; the other visits stay listed as
cadence not selected. By default the reductions in drpActor/reductions are reused, each made with the
pipeline of its day, and so are those an earlier drp_qa version's comparison of the period made beside them, its
verdicts being judged again; --fresh reduces everything
with the current pipeline instead, so runs are compared on a level field (their calibrations are still each
run's own). Its collection and files are named after that pipeline, <period>/<version>/pfs-<weekly> (from
drp_stella's getPfsVersions, each product's version if they differ; --pipeline-version names it), so a new
pipeline starts afresh while a new drp_qa version reuses the same pipeline's reductions and only judges again. Since
the label comes from the stack set up in the shell, run --fresh stops when its label has no reductions of the
period yet while another label has (the whole reduction again, where a judge pass was meant); --new-pipeline
reduces anyway, plan and --dry-run only warn, and a run resumed under its own label is not stopped. Passes are
calibration,
sky and unvalidated-calibration, gated first, each split by sequence: sequences with an image to reduce get the
whole pipeline, and sequences already reduced by drpActor only the QA tasks, imageQualityQa and dmLineResiduals
(a -judge pass, e.g. sky-judge;
--pass sky selects both). Otherwise pipetask reruns cosmicray wherever a reduction lacks the cosmicray_log
that imageQualityQa optionally reads, though the reduction is reused; the judge-only images then have no
cosmic-ray counts, which are not gated. run --dry-run builds
each pass's graph (pipetask qgraph) and prints its quanta without writing anything; --pass calibration
or --pass sky limits run to one pass. Visits with no pfsConfig in the inputs can't be reduced and are
reported, not scheduled. A failed quantum fails its pass but not the others; run lists the failures at the end and
exits 1. Images whose quanta failed in an earlier run (read from the period's pipetask logs) are failed, listed
in the report and not rescheduled, since they fail the same way each time; --retry-failed retries them. The
output collection is
u/$USER/comparison/<period>/<version>, the version being git describe of the last commit that could change what
the pipeline writes (not the comparison driver, its report, tests or docs), so a change to the driver keeps the
collection; uncommitted changes to the rest are refused. Report the reference
(--period run25) first: its stored metrics are what each other run is compared with.
Scripts live in bin.src/ and are run directly — there is no SCons step to copy them into a bin/ directory on PATH:
python bin.src/<script>.py --helpComparison mode; see Comparison mode.
The acceptance check for adopting line-scatter thresholds (PIPE2D-1956): judges the reserved-line scatter that
comparison mode exported for each run (dmLineResidualMetrics-<period>.parquet beside the report, with
iqQaMetrics-<period>.parquet for the observation type of tables stored before obsType was a column, or that
stored unknown) with a
thresholds file, and prints WARN and FAIL counts per run, arm, observation type and species. Thresholds that fail a
run's healthy frames in bulk describe the reference visits' night, not the instrument. Stack-free.
Thin entry point for pfs.drp.qa.fiberNormsQa.main, which plots fiber normalizations from a Butler collection.
Scripts live in bin.src/ and are run directly — there is no SCons step to copy them into a bin/ directory on PATH:
python bin.src/<script>.py --helpParses pipetask run log files from the detectorMap pipeline and reports a per-quantum
OK / WARN / BAD verdict for each fitDetectorMap execution: fit RMS, softening, line counts, per-species
statistics, and slit-offset failures. Exits non-zero if any quantum is BAD, so it can be used as a gate in a reduction
script.
Requires only the Python standard library — no LSST stack — with matplotlib
needed just for --plot. Quanta are keyed off the (fitDetectorMap:{...}) label that
pipetask writes into the logger context, so logs must be captured with that context intact.
python bin.src/fitDetectorMapLogQa.py run28-dm-02.log run28-dm-03.log
python bin.src/fitDetectorMapLogQa.py --warn-yrms 0.12 --plot run28-dm-03.logThresholds default to yRMS 0.10 px (WARN) and xRMS 0.05 px (WARN), overridable with
--warn-yrms / --bad-yrms. A quantum is also flagged when its arc-line total falls below 25 % of the best peer in the
same spectrograph module.
Note: this parser and
drp_stella'sbin.src/parse_detectormap_log.pyread the same log strings independently, sofitDetectorMap's log format is effectively an API with two consumers. Both the old format and the current one — which carries an inlinearm=<a> spectrograph=<n>prefix as ofdrp_stella16174310— are accepted here.
Builds a per-visit image quality report from reduceExposure and imageQualityQa logs, or by querying the Butler
directly. Produces a diagnostic dashboard plot, a markdown report, and a JSON dump of the parsed state. numpy and
matplotlib are only needed for
--plot-dir.
# From log files
python bin.src/imageQualityLogQa.py run28.log --plot-dir plots/ --report-out qa.md
# From a butler collection
python bin.src/imageQualityLogQa.py \
--butler-repo /path/to/butler --collection u/user/run28 \
--visit 140005 --spectrograph 1 --arms b r--json-out writes the parsed state, and --json-in replays it, so plots and reports can be regenerated without
reparsing.
Reads iqQaMetrics datasets across a collection and produces the multi-panel time-series figure (FWHM, flexure, flag
breakdown, pass/fail heatmap) implemented in
pfs.drp.qa.plotting.iqQa.plotIqTimeSeries. Requires the LSST stack for the Butler query; --csv bypasses it.
python bin.src/plotIqQaTimeSeries.py \
-b /path/to/butler -c u/user/run28 -o iq_timeseries.png
python bin.src/plotIqQaTimeSeries.py --csv metrics.csv --arm b,r --obs-type arcFilters: --arm, --spectrograph, --obs-type (all comma-separated), plus --where
for an arbitrary Butler query expression.
Thin entry point for pfs.drp.qa.fiberNormsQa.main, which plots fiber normalizations from a Butler collection.