Skip to content

Commit

Permalink
Merge pull request #167 from lsst/tickets/DM-34414
Browse files Browse the repository at this point in the history
DM-34414: Add deblender metrics
  • Loading branch information
fred3m authored Dec 6, 2023
2 parents d02be7e + e5c6aad commit ba6f644
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pipelines/coaddQualityCore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ tasks:
atools.e2Diff: E2Diff
atools.skyFluxStatisticMetric: SkyFluxStatisticMetric
atools.skyFluxStatisticMetric.applyContext: CoaddContext
atools.parentDeblenderMetrics: ParentDeblenderMetrics
atools.skippedDeblenderMetrics: SkippedDeblenderMetrics
atools.blendMetrics: BlendMetrics
atools.isolatedDeblenderMetrics: IsolatedDeblenderMetrics
atools.wPerpPSFP: WPerpPSF
atools.wPerpCModel: WPerpCModel
atools.xPerpPSFP: XPerpPSF
Expand Down
15 changes: 15 additions & 0 deletions python/lsst/analysis/tools/actions/scalar/scalarActions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"MinAction",
"FracInRange",
"FracNan",
"SumAction",
)

import operator
Expand Down Expand Up @@ -307,3 +308,17 @@ def __call__(self, data: KeyedData, **kwargs) -> Scalar:
return 100.0 * result
else:
return result


class SumAction(ScalarAction):
"""Returns the sum of all values in the column."""

vectorKey = Field[str]("Key of Vector to sum")

def getInputSchema(self) -> KeyedDataSchema:
return ((self.vectorKey, Vector),)

def __call__(self, data: KeyedData, **kwargs) -> Scalar:
mask = self.getMask(**kwargs)
arr = cast(Vector, data[self.vectorKey.format(**kwargs)])[mask]
return cast(Scalar, np.nansum(arr))
12 changes: 12 additions & 0 deletions python/lsst/analysis/tools/actions/vector/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,3 +494,15 @@ def __call__(self, data: KeyedData, **kwargs) -> Vector:
# No band selection is applied, i.e., select all rows
mask = np.full(len(data[self.vectorKey]), True) # type: ignore
return cast(Vector, mask)


class ParentObjectSelector(FlagSelector):
"""Select only parent objects that are not sky objects."""

def setDefaults(self):
# This selects all of the parents
self.selectWhenFalse = [
"detect_isDeblendedModelSource",
"sky_object",
]
self.selectWhenTrue = ["detect_isPatchInner"]
1 change: 1 addition & 0 deletions python/lsst/analysis/tools/atools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .astrometricRepeatability import *
from .coveragePlots import *
from .deblenderMetric import *
from .diaSolarSystemObjectMetrics import *
from .diaSourceMetrics import *
from .diffimMetadataMetrics import *
Expand Down
142 changes: 142 additions & 0 deletions python/lsst/analysis/tools/atools/deblenderMetric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# This file is part of analysis_tools.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (https://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations

__all__ = ("ParentDeblenderMetrics", "SkippedDeblenderMetrics", "BlendMetrics", "IsolatedDeblenderMetrics")

from ..actions.scalar.scalarActions import CountAction, MeanAction, SumAction
from ..actions.vector.selectors import FlagSelector, ParentObjectSelector, ThresholdSelector
from ..interfaces import AnalysisTool


class ParentDeblenderMetrics(AnalysisTool):
"""Calculate metrics based on the performance of the deblender"""

def setDefaults(self):
super().setDefaults()

# Only select parents
self.prep.selectors.parentSelector = ParentObjectSelector()

# Statistics for parent blends
self.process.calculateActions.numParents = CountAction(vectorKey="parentObjectId")
self.process.calculateActions.numDeblendFailed = SumAction(vectorKey="deblend_failed")
self.process.calculateActions.numIncompleteData = SumAction(vectorKey="deblend_incompleteData")

# Total number of detected peaks
self.process.calculateActions.numDetectedPeaks = SumAction(vectorKey="deblend_nPeaks")
# Total number of deblended children
self.process.calculateActions.numDeblendedChildren = SumAction(vectorKey="deblend_nChild")

self.produce.metric.units = {
"numParents": "",
"numDeblendFailed": "",
"numIncompleteData": "",
"numDetectedPeaks": "",
"numDeblendedChildren": "",
}


class SkippedDeblenderMetrics(AnalysisTool):
"""Calculate metrics based on blends skipped by the deblender"""

def setDefaults(self):
super().setDefaults()

# Only select non-sky object parents that were skipped but did not fail
# This also excludes isolated objects that were skipped
# if isolated objects are not being deblended
self.prep.selectors.parentSelector = ParentObjectSelector()
self.prep.selectors.skippedSelector = FlagSelector()
self.prep.selectors.skippedSelector.selectWhenTrue = ["deblend_skipped"]
self.prep.selectors.skippedSelector.selectWhenFalse = ["deblend_failed", "deblend_isolatedParent"]

# Statistics for skipped blends
self.process.calculateActions.numSkippedBlends = CountAction(vectorKey="parentObjectId")
self.process.calculateActions.numBlendParentTooBig = SumAction(vectorKey="deblend_parentTooBig")
self.process.calculateActions.numBlendTooManyPeaks = SumAction(vectorKey="deblend_tooManyPeaks")
self.process.calculateActions.numBlendTooManyMasked = SumAction(vectorKey="deblend_masked")

# Total number of skipped peaks
self.process.calculateActions.numSkippedPeaks = SumAction(vectorKey="deblend_nPeaks")

self.produce.metric.units = {
"numSkippedBlends": "",
"numBlendParentTooBig": "",
"numBlendTooManyPeaks": "",
"numBlendTooManyMasked": "",
"numSkippedPeaks": "",
}


class BlendMetrics(AnalysisTool):
"""Calculate metrics based on the performance of the deblender for blends
with multiple children
"""

def setDefaults(self):
super().setDefaults()

# Only select parents that were successfully deblended
# with more than one child
self.prep.selectors.parentSelector = ParentObjectSelector()
self.prep.selectors.blendSelector = ThresholdSelector()
self.prep.selectors.blendSelector.vectorKey = "deblend_nChild"
self.prep.selectors.blendSelector.op = "gt"
self.prep.selectors.blendSelector.threshold = 1

# Statistics for blended parents
self.process.calculateActions.numBlends = CountAction(vectorKey="parentObjectId")
self.process.calculateActions.meanBlendIterations = MeanAction(vectorKey="deblend_iterations")
self.process.calculateActions.meanBlendLogL = MeanAction(vectorKey="deblend_logL")

self.produce.metric.units = {
"numBlends": "",
"meanBlendIterations": "",
"meanBlendLogL": "",
}


class IsolatedDeblenderMetrics(AnalysisTool):
"""Calculate metrics based on the performance of the deblender for
parents with only a single child peak.
"""

def setDefaults(self):
super().setDefaults()

# Only select parents that were successfully deblended with one child
self.prep.selectors.parentSelector = ParentObjectSelector()
self.prep.selectors.blendSelector = ThresholdSelector()
self.prep.selectors.blendSelector.vectorKey = "deblend_nChild"
self.prep.selectors.blendSelector.op = "eq"
self.prep.selectors.blendSelector.threshold = 1

# Statistics for isolated parent scarlet_lite models
self.process.calculateActions.numIsolated = CountAction(vectorKey="parentObjectId")
self.process.calculateActions.meanIsolatedIterations = MeanAction(vectorKey="deblend_iterations")
self.process.calculateActions.meanIsolatedLogL = MeanAction(vectorKey="deblend_logL")

self.produce.metric.units = {
"numIsolated": "",
"meanIsolatedIterations": "",
"meanIsolatedLogL": "",
}

0 comments on commit ba6f644

Please sign in to comment.