Coverage for python/lsst/ap/verify/metrics.py: 36%
43 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-17 10:19 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-17 10:19 +0000
1#
2# This file is part of ap_verify.
3#
4# Developed for the LSST Data Management System.
5# This product includes software developed by the LSST Project
6# (http://www.lsst.org).
7# See the COPYRIGHT file at the top-level directory of this distribution
8# for details of code ownership.
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
24"""Verification metrics handling for the AP pipeline.
26This module handles metrics loading and export (via the `AutoJob` class), but not
27processing of individual measurements. Measurements are handled in the
28``ap_verify`` module or in the appropriate pipeline step, as appropriate.
29"""
31__all__ = ["MetricsParser", "computeMetrics"]
33import argparse
34import collections
35import copy
36import os
38import lsst.utils
39from lsst.verify.gen2tasks import MetricsControllerTask
42class MetricsParser(argparse.ArgumentParser):
43 """An argument parser for data needed by metrics activities.
45 This parser is not complete, and is designed to be passed to another parser
46 using the `parent` parameter.
47 """
49 def __init__(self):
50 # Help and documentation will be handled by main program's parser
51 argparse.ArgumentParser.__init__(self, add_help=False)
52 self.add_argument('--skip-existing-metrics', action='store_true',
53 help='Calculate metrics for only those data IDs that don\'t yet '
54 'have a Job file on disk.')
55 self.add_argument(
56 '--metrics-file', default='{output}/ap_verify.{dataId}.verify.json',
57 help="The file template to which to output metrics in lsst.verify "
58 "format. {output} will be replaced with the value of the "
59 "--output argument, while {dataId} will be replaced with the "
60 "job\'s data ID. Defaults to {output}/ap_verify.{dataId}.verify.json.")
61 self.add_argument('--dataset-metrics-config',
62 help='The config file specifying the dataset-level metrics to measure. '
63 'Defaults to config/default_dataset_metrics.py.')
64 self.add_argument('--image-metrics-config',
65 help='The config file specifying the image-level metrics to measure. '
66 'Defaults to config/default_image_metrics.py.')
69class _OptionalFormatDict(collections.UserDict):
70 """A dictionary that, when used with a formatter, preserves unknown
71 replacement fields.
73 This lets clients perform partial string substitution without `str.format`
74 or `str.format_map` failing over missing keywords.
75 """
76 def __missing__(self, key):
77 """Re-create the replacement field if there is no replacement.
78 """
79 return "{%s}" % key
82def computeMetrics(workspace, dataIds, args):
83 """Measure any metrics that apply to the final result of the AP pipeline,
84 rather than to a particular processing stage.
86 Parameters
87 ----------
88 workspace : `lsst.ap.verify.workspace.Workspace`
89 The abstract location containing input and output repositories.
90 dataIds : `lsst.pipe.base.DataIdContainer`
91 The data IDs ap_pipe was run on. Each data ID must be complete.
92 args : `argparse.Namespace`
93 Command-line arguments, including arguments controlling output.
94 """
95 # Substitute all fields that won't be filled in by MetricsControllerTask
96 # _OptionalFormatDict makes format_map preserve unspecified fields for later replacement
97 metricsFile = args.metrics_file.format_map(
98 _OptionalFormatDict(output=workspace.workDir))
100 imageConfig = _getMetricsConfig(args.image_metrics_config,
101 "default_image_metrics.py",
102 metricsFile)
103 _runMetricTasks(imageConfig, dataIds.refList, skipExisting=args.skip_existing_metrics)
105 datasetConfig = _getMetricsConfig(args.dataset_metrics_config,
106 "default_dataset_metrics.py",
107 metricsFile)
108 _runMetricTasks(datasetConfig, [workspace.analysisButler.dataRef("apPipe_config")],
109 skipExisting=args.skip_existing_metrics)
112def _getMetricsConfig(userFile, defaultFile, metricsOutputTemplate=None):
113 """Load a metrics config based on program settings.
115 Parameters
116 ----------
117 userFile : `str` or `None`
118 The path provided by the user for this config file.
119 defaultFile : `str`
120 The filename (not a path) of the default config file.
121 metricsOutputTemplate : `str` or `None`
122 The files to which to write metrics. If not `None`, this argument
123 overrides any output files set by either config file.
125 Returns
126 -------
127 config : `lsst.verify.gen2tasks.MetricsControllerConfig`
128 The config from ``userFile`` if the user provided one, otherwise the
129 default config.
130 """
131 timingConfig = MetricsControllerTask.ConfigClass()
133 if userFile is not None:
134 timingConfig.load(userFile)
135 else:
136 timingConfig.load(os.path.join(lsst.utils.getPackageDir("ap_verify"), "config", defaultFile))
137 if metricsOutputTemplate:
138 timingConfig.jobFileTemplate = metricsOutputTemplate
139 return timingConfig
142def _runMetricTasks(config, dataRefs, skipExisting=False):
143 """Run MetricControllerTask on a single dataset.
145 Parameters
146 ----------
147 config : `lsst.verify.gen2tasks.MetricsControllerConfig`
148 The config for running `~lsst.verify.gen2tasks.MetricsControllerTask`.
149 dataRefs : `list` [`lsst.daf.persistence.ButlerDataRef`]
150 The data references over which to compute metrics. The granularity
151 determines the metric granularity; see
152 `MetricsControllerTask.runDataRef` for more details.
153 skipExisting : `bool`, optional
154 If set, avoid recalculating metrics that are already on disk.
155 """
156 allMetricTasks = MetricsControllerTask(config)
157 allMetricTasks.runDataRefs([_sanitizeRef(ref) for ref in dataRefs], skipExisting=skipExisting)
160def _sanitizeRef(dataRef):
161 """Remove data ID tags that can cause problems when loading arbitrary
162 dataset types.
164 Parameters
165 ----------
166 dataRef : `lsst.daf.persistence.ButlerDataRef`
167 The dataref to sanitize.
169 Returns
170 -------
171 clean : `lsst.daf.persistence.ButlerDataRef`
172 A dataref that is safe to use. May be ``dataRef`` if it was already safe.
173 """
174 if "hdu" in dataRef.dataId:
175 # To avoid copying Butler (slow!), make shallow copy then overwrite data ID
176 newDataRef = copy.copy(dataRef)
177 id = copy.copy(newDataRef.dataId)
178 del id["hdu"]
179 newDataRef.dataId = id
180 return newDataRef
181 return dataRef