Coverage for python/lsst/ap/verify/metrics.py: 31%

43 statements  

« prev     ^ index     » next       coverage.py v7.2.1, created at 2023-03-12 03:57 -0700

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# 

23 

24"""Verification metrics handling for the AP pipeline. 

25 

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""" 

30 

31__all__ = ["MetricsParser", "computeMetrics"] 

32 

33import argparse 

34import collections 

35import copy 

36import os 

37 

38import lsst.utils 

39from lsst.verify.gen2tasks import MetricsControllerTask 

40 

41 

42class MetricsParser(argparse.ArgumentParser): 

43 """An argument parser for data needed by metrics activities. 

44 

45 This parser is not complete, and is designed to be passed to another parser 

46 using the `parent` parameter. 

47 """ 

48 

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.') 

67 

68 

69class _OptionalFormatDict(collections.UserDict): 

70 """A dictionary that, when used with a formatter, preserves unknown 

71 replacement fields. 

72 

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 

80 

81 

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. 

85 

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)) 

99 

100 imageConfig = _getMetricsConfig(args.image_metrics_config, 

101 "default_image_metrics.py", 

102 metricsFile) 

103 _runMetricTasks(imageConfig, dataIds.refList, skipExisting=args.skip_existing_metrics) 

104 

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) 

110 

111 

112def _getMetricsConfig(userFile, defaultFile, metricsOutputTemplate=None): 

113 """Load a metrics config based on program settings. 

114 

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. 

124 

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() 

132 

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 

140 

141 

142def _runMetricTasks(config, dataRefs, skipExisting=False): 

143 """Run MetricControllerTask on a single dataset. 

144 

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) 

158 

159 

160def _sanitizeRef(dataRef): 

161 """Remove data ID tags that can cause problems when loading arbitrary 

162 dataset types. 

163 

164 Parameters 

165 ---------- 

166 dataRef : `lsst.daf.persistence.ButlerDataRef` 

167 The dataref to sanitize. 

168 

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