Skip to content

Commit 55162e8

Browse files
authored
Merge pull request #231 from lsst/tickets/DM-43404
DM-43404: Add diffim QA plots
2 parents 98dbbe6 + 24c0fdb commit 55162e8

8 files changed

Lines changed: 289 additions & 9 deletions

File tree

pipelines/apDetectorVisitQualityCore.yaml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ tasks:
5353
python: |
5454
from lsst.analysis.tools.atools import *
5555
diffimTaskCore:
56-
class: lsst.analysis.tools.tasks.DiffimDetectorVisitAnalysisTask
56+
class: lsst.analysis.tools.tasks.DiffimDetectorVisitMetricsAnalysisTask
5757
config:
5858
connections.outputName: diffimMetadata
5959

@@ -75,11 +75,40 @@ tasks:
7575
templateCoveragePercent: percent
7676
python: |
7777
from lsst.analysis.tools.atools import DiffimMetadataMetricTool
78+
diffimTaskPlots:
79+
class: lsst.analysis.tools.tasks.DiffimDetectorVisitSpatiallySampledPlotsTask
80+
config:
81+
connections.outputName: diffimPlots
82+
atools.diaFlagHistogram: DiffimSpatialMetricsHistPlot
83+
atools.interpolatedDipoleDensity: DiffimSpatialMetricsInterpolatePlot
84+
atools.interpolatedDipoleDensity.metricName: dipole_density
85+
atools.interpolatedDipoleDensity.produce.plot.zAxisLabel: Number/degree^2
86+
atools.interpolatedSourceDensity: DiffimSpatialMetricsInterpolatePlot
87+
atools.interpolatedSourceDensity.metricName: source_density
88+
atools.interpolatedSourceDensity.produce.plot.zAxisLabel: Number/degree^2
89+
atools.interpolatedTemplateMedian: DiffimSpatialMetricsInterpolatePlot
90+
atools.interpolatedTemplateMedian.metricName: template_value
91+
atools.interpolatedTemplateMedian.produce.plot.zAxisLabel: nJy
92+
atools.interpolatedScienceMedian: DiffimSpatialMetricsInterpolatePlot
93+
atools.interpolatedScienceMedian.metricName: science_value
94+
atools.interpolatedScienceMedian.produce.plot.zAxisLabel: nJy
95+
atools.interpolatedDiffimMedian: DiffimSpatialMetricsInterpolatePlot
96+
atools.interpolatedDiffimMedian.metricName: diffim_value
97+
atools.interpolatedDiffimMedian.produce.plot.zAxisLabel: nJy
98+
atools.interpolatedSciencePsfSize: DiffimSpatialMetricsInterpolatePlot
99+
atools.interpolatedSciencePsfSize.metricName: science_psfSize
100+
atools.interpolatedSciencePsfSize.produce.plot.zAxisLabel: pixels
101+
atools.interpolatedTemplatePsfSize: DiffimSpatialMetricsInterpolatePlot
102+
atools.interpolatedTemplatePsfSize.metricName: template_psfSize
103+
atools.interpolatedTemplatePsfSize.produce.plot.zAxisLabel: pixels
104+
python: |
105+
from lsst.analysis.tools.atools import DiffimSpatialMetricsHistPlot, DiffimSpatialMetricsInterpolatePlot
78106
subsets:
79107
promptQaMetrics:
80108
subset:
81109
- analyzeAssocDiaSrcCore
82110
- analyzeTrailedDiaSrcCore
83111
- diffimTaskCore
112+
- diffimTaskPlots
84113
description: >
85114
QA metrics to run in Prompt Processing.

python/lsst/analysis/tools/actions/plot/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .focalPlanePlot import *
66
from .gridPlot import *
77
from .histPlot import *
8+
from .interpolateDetectorPlot import *
89
from .matrixPlot import *
910
from .multiVisitCoveragePlot import *
1011
from .propertyMapPlot import *
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# This file is part of analysis_tools.
2+
#
3+
# Developed for the LSST Data Management System.
4+
# This product includes software developed by the LSST Project
5+
# (https://www.lsst.org).
6+
# See the COPYRIGHT file at the top-level directory of this distribution
7+
# for details of code ownership.
8+
#
9+
# This program is free software: you can redistribute it and/or modify
10+
# it under the terms of the GNU General Public License as published by
11+
# the Free Software Foundation, either version 3 of the License, or
12+
# (at your option) any later version.
13+
#
14+
# This program is distributed in the hope that it will be useful,
15+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
# GNU General Public License for more details.
18+
#
19+
# You should have received a copy of the GNU General Public License
20+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
21+
from __future__ import annotations
22+
23+
__all__ = ("InterpolateDetectorMetricPlot",)
24+
25+
import logging
26+
from typing import Mapping, Optional
27+
28+
import matplotlib.pyplot as plt
29+
import numpy as np
30+
from lsst.pex.config import Field
31+
from matplotlib.figure import Figure
32+
from scipy.interpolate import CloughTocher2DInterpolator
33+
34+
from ...interfaces import KeyedData, KeyedDataSchema, PlotAction, Vector
35+
from .plotUtils import addPlotInfo
36+
37+
_LOG = logging.getLogger(__name__)
38+
39+
40+
class InterpolateDetectorMetricPlot(PlotAction):
41+
"""Interpolate metrics evaluated at locations across a detector."""
42+
43+
xAxisLabel = Field[str](doc="Label to use for the x axis.", default="x (pixel)", optional=True)
44+
yAxisLabel = Field[str](doc="Label to use for the y axis.", default="y (pixel)", optional=True)
45+
zAxisLabel = Field[str](doc="Label to use for the z axis.", optional=True)
46+
47+
xCoordSize = Field[int]("Dimensions for X direction field to interpolate", default=4000)
48+
yCoordSize = Field[int]("Dimensions for Y direction field to interpolate", default=4072)
49+
nGridPoints = Field[int]("N points in the grid for the field to interpolate", default=40)
50+
gridMargin = Field[int]("Grid margins for the field to interpolate", default=20)
51+
52+
def getInputSchema(self) -> KeyedDataSchema:
53+
base = []
54+
55+
base.append(("x", Vector))
56+
base.append(("y", Vector))
57+
base.append(("metricValues", Vector))
58+
59+
return base
60+
61+
def __call__(self, data: KeyedData, **kwargs) -> Mapping[str, Figure] | Figure:
62+
return self.makePlot(data, **kwargs)
63+
64+
def makePlot(self, data: KeyedData, plotInfo: Optional[Mapping[str, str]] = None, **kwargs) -> Figure:
65+
66+
X = np.linspace(-self.gridMargin, self.xCoordSize + self.gridMargin, self.nGridPoints)
67+
Y = np.linspace(-self.gridMargin, self.yCoordSize + self.gridMargin, self.nGridPoints)
68+
meshgridX, meshgridY = np.meshgrid(X, Y) # 2D grid for interpolation
69+
70+
interp = CloughTocher2DInterpolator(list(zip(data["x"], data["y"])), data["metricValues"])
71+
Z = interp(meshgridX, meshgridY)
72+
73+
fig, ax = plt.subplots(1, 1, figsize=(8, 6))
74+
pc = ax.pcolormesh(X, Y, Z, shading="auto")
75+
ax.scatter(data["x"], data["y"], s=5, c="black")
76+
cbar = fig.colorbar(pc)
77+
cbar.set_label(self.zAxisLabel, rotation=270)
78+
ax.set_xlabel(self.xAxisLabel)
79+
ax.set_ylabel(self.yAxisLabel)
80+
ax.set_aspect("equal", "box")
81+
82+
# add general plot info
83+
if plotInfo is not None:
84+
fig = addPlotInfo(fig, plotInfo)
85+
86+
return fig

python/lsst/analysis/tools/atools/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .diaSolarSystemObjectMetrics import *
1010
from .diaSourceMetrics import *
1111
from .diaSourceTableTractMetrics import *
12+
from .diaSpatialMetricsPlots import *
1213
from .diffimMetadataMetrics import *
1314
from .diffMatched import *
1415
from .fluxMetrics import *
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# This file is part of analysis_tools.
2+
#
3+
# Developed for the LSST Data Management System.
4+
# This product includes software developed by the LSST Project
5+
# (https://www.lsst.org).
6+
# See the COPYRIGHT file at the top-level directory of this distribution
7+
# for details of code ownership.
8+
#
9+
# This program is free software: you can redistribute it and/or modify
10+
# it under the terms of the GNU General Public License as published by
11+
# the Free Software Foundation, either version 3 of the License, or
12+
# (at your option) any later version.
13+
#
14+
# This program is distributed in the hope that it will be useful,
15+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
# GNU General Public License for more details.
18+
#
19+
# You should have received a copy of the GNU General Public License
20+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
21+
__all__ = ("DiffimSpatialMetricsHistPlot", "DiffimSpatialMetricsInterpolatePlot")
22+
23+
from lsst.pex.config import Field
24+
25+
from ..actions.plot.histPlot import HistPanel, HistPlot
26+
from ..actions.plot.interpolateDetectorPlot import InterpolateDetectorMetricPlot
27+
from ..actions.vector import LoadVector
28+
from ..interfaces import AnalysisTool
29+
30+
31+
class DiffimSpatialMetricsHistPlot(AnalysisTool):
32+
"""Create histograms of the fraction of pixels with certain mask planes
33+
set.
34+
"""
35+
36+
parameterizedBand: bool = False
37+
38+
def setDefaults(self):
39+
super().setDefaults()
40+
41+
self.process.buildActions.bad_mask_fraction = LoadVector(vectorKey="bad_mask_fraction")
42+
self.process.buildActions.cr_mask_fraction = LoadVector(vectorKey="cr_mask_fraction")
43+
self.process.buildActions.detected_mask_fraction = LoadVector(vectorKey="detected_mask_fraction")
44+
self.process.buildActions.detected_negative_mask_fraction = LoadVector(
45+
vectorKey="detected_negative_mask_fraction"
46+
)
47+
self.process.buildActions.intrp_mask_fraction = LoadVector(vectorKey="intrp_mask_fraction")
48+
self.process.buildActions.no_data_mask_fraction = LoadVector(vectorKey="no_data_mask_fraction")
49+
self.process.buildActions.sat_mask_fraction = LoadVector(vectorKey="sat_mask_fraction")
50+
self.process.buildActions.sat_template_mask_fraction = LoadVector(
51+
vectorKey="sat_template_mask_fraction"
52+
)
53+
self.process.buildActions.streak_mask_fraction = LoadVector(vectorKey="streak_mask_fraction")
54+
55+
self.produce.plot = HistPlot()
56+
57+
self.produce.plot.panels["panel_flags"] = HistPanel()
58+
self.produce.plot.panels["panel_flags"].label = "Flagged pixel fraction"
59+
self.produce.plot.panels["panel_flags"].bins = 20
60+
self.produce.plot.panels["panel_flags"].rangeType = "fixed"
61+
self.produce.plot.panels["panel_flags"].lowerRange = 0
62+
self.produce.plot.panels["panel_flags"].upperRange = 1.0
63+
self.produce.plot.panels["panel_flags"].hists = dict(
64+
no_data_mask_fraction="No data",
65+
sat_mask_fraction="Saturated",
66+
bad_mask_fraction="Bad",
67+
cr_mask_fraction="Cosmic ray",
68+
detected_mask_fraction="Detected",
69+
detected_negative_mask_fraction="Detected negative",
70+
intrp_mask_fraction="Interpolated",
71+
sat_template_mask_fraction="Saturated template",
72+
streak_mask_fraction="Streak",
73+
)
74+
75+
76+
class DiffimSpatialMetricsInterpolatePlot(AnalysisTool):
77+
"""Interpolate spatially-sampled metric values and create low-resolution
78+
images of the result.
79+
"""
80+
81+
metricName = Field[str](doc="Metric name to interpolate", optional=False)
82+
parameterizedBand: bool = False
83+
84+
def setDefaults(self):
85+
super().setDefaults()
86+
87+
self.produce.plot = InterpolateDetectorMetricPlot()
88+
self.process.buildActions.x = LoadVector()
89+
self.process.buildActions.x.vectorKey = "x"
90+
self.process.buildActions.y = LoadVector()
91+
self.process.buildActions.y.vectorKey = "y"
92+
93+
def finalize(self):
94+
self.process.buildActions.metricValues = LoadVector()
95+
self.process.buildActions.metricValues.vectorKey = self.metricName

python/lsst/analysis/tools/tasks/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
from .ccdVisitTableAnalysis import *
99
from .diaObjectDetectorVisitAnalysis import *
1010
from .diaSourceTableTractAnalysis import *
11-
from .diffimTaskDetectorVisitAnalysis import *
11+
from .diffimTaskDetectorVisitMetricsAnalysis import *
12+
from .diffimTaskDetectorVisitSpatiallySampledAnalysis import *
1213
from .diffMatchedAnalysis import *
1314
from .gatherResourceUsage import *
1415
from .objectTableSurveyAnalysis import *

python/lsst/analysis/tools/tasks/diffimTaskDetectorVisitAnalysis.py renamed to python/lsst/analysis/tools/tasks/diffimTaskDetectorVisitMetricsAnalysis.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@
2020
# along with this program. If not, see <https://www.gnu.org/licenses/>.
2121
from __future__ import annotations
2222

23-
__all__ = ("DiffimDetectorVisitAnalysisConfig", "DiffimDetectorVisitAnalysisTask")
23+
__all__ = ("DiffimDetectorVisitMetricsAnalysisConfig", "DiffimDetectorVisitMetricsAnalysisTask")
2424

2525
import pandas as pd
2626
from lsst.pipe.base import NoWorkFound, connectionTypes
2727

2828
from ..interfaces import AnalysisBaseConfig, AnalysisBaseConnections, AnalysisPipelineTask
2929

3030

31-
class DiffimDetectorVisitAnalysisConnections(
31+
class DiffimDetectorVisitMetricsAnalysisConnections(
3232
AnalysisBaseConnections,
3333
dimensions=("visit", "band", "detector"),
3434
):
@@ -46,15 +46,15 @@ class DiffimDetectorVisitAnalysisConnections(
4646
)
4747

4848

49-
class DiffimDetectorVisitAnalysisConfig(
50-
AnalysisBaseConfig, pipelineConnections=DiffimDetectorVisitAnalysisConnections
49+
class DiffimDetectorVisitMetricsAnalysisConfig(
50+
AnalysisBaseConfig, pipelineConnections=DiffimDetectorVisitMetricsAnalysisConnections
5151
):
5252
pass
5353

5454

55-
class DiffimDetectorVisitAnalysisTask(AnalysisPipelineTask):
56-
ConfigClass = DiffimDetectorVisitAnalysisConfig
57-
_DefaultName = "DiffimDetectorVisitAnalysis"
55+
class DiffimDetectorVisitMetricsAnalysisTask(AnalysisPipelineTask):
56+
ConfigClass = DiffimDetectorVisitMetricsAnalysisConfig
57+
_DefaultName = "DiffimDetectorVisitMetricsAnalysis"
5858

5959
def runQuantum(self, butlerQC, inputRefs, outputRefs):
6060
inputs = butlerQC.get(inputRefs)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# This file is part of analysis_tools.
2+
#
3+
# Developed for the LSST Data Management System.
4+
# This product includes software developed by the LSST Project
5+
# (https://www.lsst.org).
6+
# See the COPYRIGHT file at the top-level directory of this distribution
7+
# for details of code ownership.
8+
#
9+
# This program is free software: you can redistribute it and/or modify
10+
# it under the terms of the GNU General Public License as published by
11+
# the Free Software Foundation, either version 3 of the License, or
12+
# (at your option) any later version.
13+
#
14+
# This program is distributed in the hope that it will be useful,
15+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
# GNU General Public License for more details.
18+
#
19+
# You should have received a copy of the GNU General Public License
20+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
21+
from __future__ import annotations
22+
23+
__all__ = ("DiffimDetectorVisitSpatiallySampledPlotsConfig", "DiffimDetectorVisitSpatiallySampledPlotsTask")
24+
25+
from lsst.pipe.base import connectionTypes
26+
27+
from ..interfaces import AnalysisBaseConfig, AnalysisBaseConnections, AnalysisPipelineTask
28+
29+
30+
class DiffimDetectorVisitSpatiallySampledPlotsConnections(
31+
AnalysisBaseConnections,
32+
dimensions=("visit", "band", "detector"),
33+
defaultTemplates={"coaddName": "goodSeeing", "fakesType": ""},
34+
):
35+
data = connectionTypes.Input(
36+
doc="QA metrics evaluated in locations throughout the difference image.",
37+
name="{fakesType}{coaddName}Diff_spatiallySampledMetrics",
38+
storageClass="ArrowAstropy",
39+
dimensions=("instrument", "visit", "detector"),
40+
deferLoad=True,
41+
)
42+
43+
44+
class DiffimDetectorVisitSpatiallySampledPlotsConfig(
45+
AnalysisBaseConfig, pipelineConnections=DiffimDetectorVisitSpatiallySampledPlotsConnections
46+
):
47+
pass
48+
49+
50+
class DiffimDetectorVisitSpatiallySampledPlotsTask(AnalysisPipelineTask):
51+
ConfigClass = DiffimDetectorVisitSpatiallySampledPlotsConfig
52+
_DefaultName = "DiffimDetectorVisitSpatiallySampledPlots"
53+
54+
def runQuantum(self, butlerQC, inputRefs, outputRefs):
55+
# Docs inherited from base class.
56+
inputs = butlerQC.get(inputRefs)
57+
dataId = butlerQC.quantum.dataId
58+
plotInfo = self.parsePlotInfo(inputs, dataId)
59+
plotInfo["tableName"] += f", detector: {plotInfo['detector']}"
60+
data = self.loadData(inputs["data"])
61+
62+
outputs = self.run(
63+
data=data,
64+
plotInfo=plotInfo,
65+
bands=plotInfo["band"],
66+
)
67+
butlerQC.put(outputs, outputRefs)

0 commit comments

Comments
 (0)