Coverage for python/lsst/faro/measurement/VisitMeasurement.py: 47%
39 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-12 11:41 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-12 11:41 +0000
1# This file is part of faro.
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/>.
22from lsst.afw.geom import SkyWcs
23from lsst.afw.image import PhotoCalib
24from lsst.afw.table import SourceCatalog
25import lsst.pex.config as pexConfig
26import lsst.pipe.base as pipeBase
28from lsst.faro.base.BaseSubTasks import NumSourcesMergeTask
29from lsst.faro.base.CatalogMeasurementBase import (
30 CatalogMeasurementBaseConnections,
31 CatalogMeasurementBaseConfig,
32 CatalogMeasurementBaseTask,
33)
34from lsst.faro.utils.calibrated_catalog import CalibratedCatalog
36from collections import defaultdict
37from typing import List
39__all__ = ("VisitMeasurementConfig", "VisitMeasurementTask")
42class VisitMeasurementConnections(
43 CatalogMeasurementBaseConnections,
44 dimensions=("instrument", "visit", "band"),
45 defaultTemplates={"photoCalibName": "calexp.photoCalib", "wcsName": "calexp.wcs"},
46):
48 catalogs = pipeBase.connectionTypes.Input(
49 doc="Source catalogs.",
50 dimensions=("instrument", "visit", "detector", "band"),
51 storageClass="SourceCatalog",
52 name="src",
53 multiple=True,
54 )
56 photoCalibs = pipeBase.connectionTypes.Input(
57 doc="Photometric calibration object.",
58 dimensions=("instrument", "visit", "detector", "band"),
59 storageClass="PhotoCalib",
60 name="{photoCalibName}",
61 multiple=True,
62 )
64 astromCalibs = pipeBase.connectionTypes.Input(
65 doc="WCS for the catalog.",
66 dimensions=("instrument", "visit", "detector", "band"),
67 storageClass="Wcs",
68 name="{wcsName}",
69 multiple=True,
70 )
72 measurement = pipeBase.connectionTypes.Output(
73 doc="Per-visit measurement.",
74 dimensions=("instrument", "visit", "band"),
75 storageClass="MetricValue",
76 name="metricvalue_{package}_{metric}",
77 )
80class VisitMeasurementConfig(
81 CatalogMeasurementBaseConfig, pipelineConnections=VisitMeasurementConnections
82):
83 measure = pexConfig.ConfigurableField(
84 # The (plain old) Task that actually measures the desired metric
85 # Should be overridden in pipelines
86 target=NumSourcesMergeTask,
87 doc="Measure task",
88 )
91class VisitMeasurementTask(CatalogMeasurementBaseTask):
92 ConfigClass = VisitMeasurementConfig
93 _DefaultName = "visitMeasurementTask"
95 def run(
96 self,
97 catalogs: List[SourceCatalog],
98 photoCalibs: List[PhotoCalib],
99 astromCalibs: List[SkyWcs],
100 dataIds: List[dict],
101 ):
102 data = defaultdict(list)
103 for catalog, photoCalib, astromCalib, dataId in zip(catalogs, photoCalibs, astromCalibs, dataIds):
104 if self.config.requireAstrometry and astromCalib is None:
105 self.log.info("requireAstrometry is True but astromCalib is None for %s. Skipping...",
106 dataId)
107 continue
108 if self.config.requirePhotometry and photoCalib is None:
109 self.log.info("requirePhotometry is True but photoCalib is None for %s. Skipping...",
110 dataId)
111 continue
112 data[dataId["band"]].append(CalibratedCatalog(catalog, photoCalib, astromCalib))
114 return self.measure.run(self.config.connections.metric, data)
116 def runQuantum(self, butlerQC, inputRefs, outputRefs):
117 inputs = butlerQC.get(inputRefs)
118 inputs["dataIds"] = [
119 butlerQC.registry.expandDataId(c.dataId) for c in inputRefs.catalogs
120 ]
121 outputs = self.run(**inputs)
122 if outputs.measurement is not None:
123 butlerQC.put(outputs, outputRefs)
124 else:
125 self.log.debug(
126 "Skipping measurement of {!r} on {} " "as not applicable.",
127 self,
128 inputRefs,
129 )