Comparing the diaSources of two runs#
compare_dia_sources matches the diaSources of two pipeline runs and explains
every source found in only one of them.
It reads the butler, so it works for runs that skipped association and have
no APDB; compare_sources needs an APDB for each run.
from lsst.daf.butler import Butler
from lsst.analysis.ap import compare_dia_sources, plot_dia_source_comparison
ref = Butler("/repo/main", collections="u/me/main_run")
new = Butler("/repo/main", collections="u/me/ticket_run")
result = compare_dia_sources(ref, new)
result.summary # band and source counts per visit
result.reason_counts() # why sources are missing from the other run
plot_dia_source_comparison(result, labels=("main", "ticket"))
The two butlers’ default collections select the runs; they can be the same
repository.
Pass visits= to compare only some visits.
Where the sources come from#
Each run’s diaSources are read from the visit table that
consolidateDiaSourceTable writes (dia_source_visit; pass
dataset="diaSourceTable" for DRP).
That task is in the afterburner subset of ApPipe.yaml, so a run of the
prompt subset alone has no visit table; for any visit without one, the
detector tables it would have consolidated (dia_source_detector) are read
instead.
summary gives the band of each visit and records which table each run’s
sources came from.
These tables hold only the sources that passed every filter, including the reliability cut. A source can be missing from one run because it was removed, not because it was not detected; see Why a source is unmatched.
Matching#
diaSource IDs differ between runs, so sources are matched by position.
Within each visit, every pair closer than radius (0.5 arcsec by default)
is a candidate, and pairs are taken in order of increasing separation,
skipping any whose sources are already paired; this is
match_catalogs with one_to_one=True.
Matching across the whole visit lets a source that falls on a neighboring
detector in the other run still match.
The sign of a source is not used, so a source detected as positive in one run
and negative in the other still matches; flags_changed then lists
isNegative.
The results are tables:
matchedOne row per pair: both IDs and detectors, the separation and pixel offset,
psfFlux,psfFluxErr,apFlux,scienceFlux,snrandreliabilityfrom each run (suffixes_refand_new),delta_psfFlux_sigma, andflags_changed, the boolean columns whose values differ.flag_changesFor each boolean column that changed in any pair, how many pairs have it set in one run only.
ref_only,new_onlyThe unmatched sources with all their columns, an
instrumentcolumn, and thereasonthey are unmatched. These can be passed tocutout_gridorPlotImageSubtractionCutoutsTaskto look at the sources.
Why a source is unmatched#
For each unmatched source, the other run’s catalogs are searched at its
position, on the same visit and detector.
The first reason in the table below that applies is recorded, and
other_diaSourceId, other_separation_arcsec and other_reliability
describe the source it refers to.
|
The other run |
|---|---|
|
has a source there, paired with a closer one (a detection split in two, or two merged into one). |
|
removed it below the reliability cut
( |
|
removed it in |
|
removed it as a long trail
( |
|
detected it ( |
|
did not detect it. |
|
wrote none of these catalogs for the detector and has no sources on it. |
|
did not write |
upstream_datasets and detection_dataset change the catalogs searched.
The catalogs are read only for detectors with unmatched sources, and only
until every source there has a reason.
The first catalog of removed detections (the reliability cut, by default) is
searched by position, since it explains most unmatched sources.
Any sources still unexplained are then found in dia_source_unfiltered;
one that is not there was not detected, and one that is was removed by
whichever catalog lists its detection, which keeps its ID.
Pass explain_unmatched=False to skip the search.