Skip to content

Commit ee7824c

Browse files
committed
Merge branch 'tickets/DM-50988'
2 parents ddd5b90 + cee2b5c commit ee7824c

3 files changed

Lines changed: 91 additions & 1 deletion

File tree

python/lsst/ip/diffim/detectAndMeasure.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from lsst.ip.diffim.utils import (evaluateMaskFraction, computeDifferenceImageMetrics,
3333
populate_sattle_visit_cache)
3434
from lsst.meas.algorithms import SkyObjectsTask, SourceDetectionTask, SetPrimaryFlagsTask, MaskStreaksTask
35+
from lsst.meas.algorithms import FindGlintTrailsTask
3536
from lsst.meas.base import ForcedMeasurementTask, ApplyApCorrTask, DetectorVisitIdGeneratorConfig
3637
import lsst.meas.deblender
3738
import lsst.meas.extensions.trailedSources # noqa: F401
@@ -138,13 +139,21 @@ class DetectAndMeasureConnections(pipeBase.PipelineTaskConnections,
138139
dimensions=("instrument", "visit", "detector"),
139140
name="{fakesType}{coaddName}Diff_streaks",
140141
)
142+
glintTrailInfo = pipeBase.connectionTypes.Output(
143+
doc='Dict of fit parameters for glint trails in the catalog.',
144+
storageClass="ArrowNumpyDict",
145+
dimensions=("instrument", "visit", "detector"),
146+
name="trailed_glints",
147+
)
141148

142149
def __init__(self, *, config):
143150
super().__init__(config=config)
144151
if not (self.config.writeStreakInfo and self.config.doMaskStreaks):
145152
self.outputs.remove("maskedStreaks")
146153
if not (self.config.doSubtractBackground and self.config.doWriteBackground):
147154
self.outputs.remove("differenceBackground")
155+
if not (self.config.writeGlintInfo):
156+
self.outputs.remove("glintTrailInfo")
148157

149158

150159
class DetectAndMeasureConfig(pipeBase.PipelineTaskConfig,
@@ -260,6 +269,15 @@ class DetectAndMeasureConfig(pipeBase.PipelineTaskConfig,
260269
doc="Record the parameters of any detected streaks. For LSST, this should be turned off except for "
261270
"development work."
262271
)
272+
findGlints = pexConfig.ConfigurableField(
273+
target=FindGlintTrailsTask,
274+
doc="Subtask for finding glint trails, usually caused by satellites or debris."
275+
)
276+
writeGlintInfo = pexConfig.Field(
277+
dtype=bool,
278+
default=True,
279+
doc="Record the parameters of any detected glint trails."
280+
)
263281
setPrimaryFlags = pexConfig.ConfigurableField(
264282
target=SetPrimaryFlagsTask,
265283
doc="Task to add isPrimary and deblending-related flags to the catalog."
@@ -455,6 +473,8 @@ def __init__(self, **kwargs):
455473
if self.config.doMaskStreaks:
456474
self.makeSubtask("maskStreaks")
457475
self.makeSubtask("streakDetection")
476+
self.makeSubtask("findGlints")
477+
self.schema.addField("glint_trail", "Flag", "DiaSource is part of a glint trail.")
458478

459479
# To get the "merge_*" fields in the schema; have to re-initialize
460480
# this later, once we have a peak schema post-detection.
@@ -492,6 +512,7 @@ def runQuantum(self, butlerQC: pipeBase.QuantumContext,
492512
measurementResults.subtractedMeasuredExposure,
493513
measurementResults.diaSources,
494514
measurementResults.maskedStreaks,
515+
measurementResults.glintTrailInfo,
495516
log=self.log
496517
)
497518
butlerQC.put(measurementResults, outputRefs)
@@ -715,6 +736,13 @@ def processResults(self, science, matchedTemplate, difference, sources, idFactor
715736
initialDiaSources = initialDiaSources.copy(deep=True)
716737

717738
self.measureDiaSources(initialDiaSources, science, difference, matchedTemplate)
739+
740+
# Add a column for glint trail diaSources, but do not remove them
741+
initialDiaSources, trail_parameters = self._find_glint_trails(initialDiaSources)
742+
if self.config.writeGlintInfo:
743+
measurementResults.mergeItems(trail_parameters, 'glintTrailInfo')
744+
745+
# Remove unphysical diaSources per config.badSourceFlags
718746
diaSources = self._removeBadSources(initialDiaSources)
719747

720748
if self.config.run_sattle:
@@ -835,6 +863,39 @@ def _removeBadSources(self, diaSources):
835863
self.log.info("Removed %d unphysical sources.", nBadTotal)
836864
return diaSources[selector].copy(deep=True)
837865

866+
def _find_glint_trails(self, diaSources):
867+
"""Define a new flag column for diaSources that are in a glint trail.
868+
869+
Parameters
870+
----------
871+
diaSources : `lsst.afw.table.SourceCatalog`
872+
The catalog of detected sources.
873+
874+
Returns
875+
-------
876+
diaSources : `lsst.afw.table.SourceCatalog`
877+
The updated catalog of detected sources, with a new bool column
878+
called 'glint_trail' added.
879+
880+
trail_parameters : `dict`
881+
Parameters of all the trails that were found.
882+
"""
883+
trailed_glints = self.findGlints.run(diaSources)
884+
glint_mask = [True if id in trailed_glints.trailed_ids else False for id in diaSources['id']]
885+
diaSources['glint_trail'] = np.array(glint_mask)
886+
887+
slopes = np.array([trail.slope for trail in trailed_glints.parameters])
888+
intercepts = np.array([trail.intercept for trail in trailed_glints.parameters])
889+
stderrs = np.array([trail.stderr for trail in trailed_glints.parameters])
890+
lengths = np.array([trail.length for trail in trailed_glints.parameters])
891+
angles = np.array([trail.angle for trail in trailed_glints.parameters])
892+
parameters = {'slopes': slopes, 'intercepts': intercepts, 'stderrs': stderrs, 'lengths': lengths,
893+
'angles': angles}
894+
895+
trail_parameters = pipeBase.Struct(glintTrailInfo=parameters)
896+
897+
return diaSources, trail_parameters
898+
838899
def addSkySources(self, diaSources, mask, seed,
839900
subtask=None):
840901
"""Add sources in empty regions of the difference image

tests/test_detectAndMeasure.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ def test_filter_satellites_all_allowed(self):
796796
output = detectionTask.run(science, matchedTemplate, difference, sources,
797797
idFactory=IdFactory.makeSimple())
798798

799-
## Output should be all sources that went in. 20 go in, 20 should come out
799+
# Output should be all sources that went in. 20 go in, 20 should come out
800800
self.assertEqual(len(output.diaSources), 20)
801801

802802
self.assertEqual(set(output.diaSources['id']), set(allowed_ids))
@@ -817,6 +817,34 @@ def test_fail_on_sattle_misconfiguration(self):
817817
with self.assertRaises(pexConfig.FieldValidationError):
818818
self._setup_detection(run_sattle=True)
819819

820+
def test_trailed_glints(self):
821+
"""Test that the glint_trail column works, and that
822+
the trailed_glints output contains the expected information.
823+
"""
824+
noiseLevel = 1.
825+
staticSeed = 1
826+
diffim, diaSources = makeTestImage(seed=staticSeed, noiseLevel=noiseLevel, noiseSeed=6)
827+
self._check_values(diaSources['glint_trail'])
828+
829+
# Run detection and return the output Struct so we can check it
830+
def _detection_wrapper(diffim, diaSources):
831+
detectionTask = self._setup_detection()
832+
scienceBase, sources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=6)
833+
matchedTemplate, _ = makeTestImage(noiseLevel=noiseLevel/4, noiseSeed=7)
834+
science = scienceBase.clone()
835+
science.maskedImage -= diffim.maskedImage
836+
difference = science.clone()
837+
difference.maskedImage -= matchedTemplate.maskedImage
838+
output = detectionTask.run(science, matchedTemplate, difference, sources)
839+
return output
840+
841+
output = _detection_wrapper(diffim, diaSources)
842+
self.assertTrue('slopes' in output.glintTrailInfo)
843+
self.assertTrue('intercepts' in output.glintTrailInfo)
844+
self.assertTrue('stderrs' in output.glintTrailInfo)
845+
self.assertTrue('lengths' in output.glintTrailInfo)
846+
self.assertTrue('angles' in output.glintTrailInfo)
847+
820848

821849
class DetectAndMeasureScoreTest(DetectAndMeasureTestBase, lsst.utils.tests.TestCase):
822850
detectionTask = detectAndMeasure.DetectAndMeasureScoreTask

tests/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,7 @@ def _makeTruthSchema():
10751075
schema.getAliasMap().set("slot_CalibFlux", "truth")
10761076
schema.getAliasMap().set("slot_ApFlux", "truth")
10771077
schema.getAliasMap().set("slot_PsfFlux", "truth")
1078+
schema.addField("glint_trail", "Flag", "testing flag.")
10781079
return keys, schema
10791080

10801081

0 commit comments

Comments
 (0)