def makeMeasurement(self, values): """Compute the number of non-updated DIAObjects. Parameters ---------- values : sequence [`dict` [`str`, `int` or `None`]] A list where each element corresponds to a metadata object passed to `run`. Each `dict` has the following keys: ``"updatedObjects"`` The number of DIAObjects updated for this image (`int` or `None`). May be `None` if the image was not successfully associated. ``"unassociatedObjects"`` The number of DIAObjects not associated with a DiaSource in this image (`int` or `None`). May be `None` if the image was not successfully associated. Returns ------- measurement : `lsst.verify.Measurement` or `None` The total number of unassociated objects. """ nUpdated = 0 nUnassociated = 0 associated = False for value in values: if value["updatedObjects"] is not None \ and value["unassociatedObjects"] is not None: try: nUpdated += value["updatedObjects"] nUnassociated += value["unassociatedObjects"] except TypeError as e: raise MetricComputationError( "Corrupted value of numUpdatedDiaObjects " "or numUnassociatedDiaObjects") from e associated = True if associated: if nUpdated <= 0 and nUnassociated <= 0: raise MetricComputationError( "No pre-existing DIAObjects; can't compute updated fraction." ) else: fraction = nUpdated / (nUpdated + nUnassociated) return Measurement(self.getOutputMetricName(self.config), fraction * u.dimensionless_unscaled) else: self.log.info("Nothing to do: no association results found.") return None
def makeMeasurement(self, values): """Compute the number of new DIAObjects. Parameters ---------- values : `dict` [`str`, `int` or `None`] A `dict` representation of the metadata. Each `dict` has the following key: ``"newObjects"`` The number of new objects created for this image (`int` or `None`). May be `None` if the image was not successfully associated. Returns ------- measurement : `lsst.verify.Measurement` or `None` The total number of new objects. """ if values["newObjects"] is not None: try: nNew = int(values["newObjects"]) except (ValueError, TypeError) as e: raise MetricComputationError( "Corrupted value of numNewDiaObjects") from e else: return Measurement(self.config.metricName, nNew * u.count) else: self.log.info("Nothing to do: no association results found.") return None
def makeMeasurement(self, values): """Compute the total number of SolarSystemObjects within a detectorVisit. Parameters ---------- values : `dict` [`str`, `int` or `None`] A `dict` representation of the metadata. Each `dict` has the following key: ``"numTotalSolarSystemObjects"`` The number of SolarSystemObjects within the observable detector area (`int` or `None`). May be `None` if solar system association was not attempted or the image was not successfully associated. Returns ------- measurement : `lsst.verify.Measurement` or `None` The total number of Solar System objects. """ if values["numTotalSolarSystemObjects"] is not None: try: nNew = int(values["numTotalSolarSystemObjects"]) except (ValueError, TypeError) as e: raise MetricComputationError( "Corrupted value of numTotalSolarSystemObjects") from e else: return Measurement(self.config.metricName, nNew * u.count) else: self.log.info("Nothing to do: no solar system results found.") return None
def run(self, matchedFakes, band): """Compute the completeness of recovered fakes within a magnitude range. Parameters ---------- matchedFakes : `lsst.afw.table.SourceCatalog` or `None` Catalog of fakes that were inserted into the ccdExposure matched to their detected counterparts. Returns ------- result : `lsst.pipe.base.Struct` A `~lsst.pipe.base.Struct` containing the following component: ``measurement`` the ratio (`lsst.verify.Measurement` or `None`) """ if matchedFakes is not None: magnitudes = matchedFakes[f"{self.config.magVar}" % band] magCutFakes = matchedFakes[np.logical_and(magnitudes > self.config.magMin, magnitudes < self.config.magMax)] if len(magCutFakes) <= 0.0: raise MetricComputationError( "No matched fakes catalog sources found; Completeness is " "ill defined.") else: meas = Measurement( self.config.metricName, ((magCutFakes["diaSourceId"] > 0).sum() / len(magCutFakes)) * u.dimensionless_unscaled) else: self.log.info("Nothing to do: no matched catalog found.") meas = None return Struct(measurement=meas)
def run(self, sciSources, diaSources): """Compute the ratio of DIASources to science sources. Parameters ---------- sciSources : `lsst.afw.table.SourceCatalog` or `None` A science source catalog, which may be empty or `None`. diaSources : `lsst.afw.table.SourceCatalog` or `None` A DIASource catalog for the same unit of processing as ``sciSources``. Returns ------- result : `lsst.pipe.base.Struct` A `~lsst.pipe.base.Struct` containing the following component: ``measurement`` the ratio (`lsst.verify.Measurement` or `None`) """ if diaSources is not None and sciSources is not None: nSciSources = len(sciSources) nDiaSources = len(diaSources) metricName = self.config.metricName if nSciSources <= 0.0: raise MetricComputationError( "No science sources found; ratio of DIASources to science sources ill-defined." ) else: meas = Measurement( metricName, nDiaSources / nSciSources * u.dimensionless_unscaled) else: self.log.info("Nothing to do: no catalogs found.") meas = None return Struct(measurement=meas)
def makeMeasurement(self, timings): """Compute a wall-clock measurement from metadata provided by `lsst.pipe.base.timeMethod`. Parameters ---------- timings : sequence [`dict` [`str`, any]] A list where each element corresponds to a metadata object passed to `run`. Each `dict` has the following keys: ``"StartTime"`` The time the target method started (`float` or `None`). ``"EndTime"`` The time the target method ended (`float` or `None`). Returns ------- measurement : `lsst.verify.Measurement` or `None` The the total running time of the target method across all elements of ``metadata``. Raises ------ MetricComputationError Raised if any of the timing metadata are invalid. Notes ----- This method does not return a measurement if no timing information was provided by any of the metadata. """ # some timings indistinguishable from 0, so don't test totalTime > 0 timingFound = False totalTime = 0.0 for singleRun in timings: if singleRun["StartTime"] is not None \ or singleRun["EndTime"] is not None: try: totalTime += singleRun["EndTime"] - singleRun["StartTime"] timingFound = True except TypeError: raise MetricComputationError("Invalid metadata") # If both are None, assume the method was not run that time if timingFound: meas = Measurement(self.getOutputMetricName(self.config), totalTime * u.second) meas.notes['estimator'] = 'pipe.base.timeMethod' return meas else: self.log.info("Nothing to do: no timing information for %s found.", self.config.target) return None
def run(self, sources): """Count the number of science sources created by deblending. Parameters ---------- sources : `lsst.afw.table.SourceCatalog` or `None` A science source catalog, which may be empty or `None`. Returns ------- result : `lsst.pipe.base.Struct` A `~lsst.pipe.base.Struct` containing the following component: ``measurement`` the total number of science sources from deblending (`lsst.verify.Measurement`). If no deblending information is available in ``sources``, this is `None`. Raises ------ MetricComputationError Raised if ``sources`` is missing mandatory keys for source catalogs. """ if sources is None: self.log.info("Nothing to do: no catalogs found.") meas = None # Use deblend_parentNChild rather than detect_fromBlend because the # latter need not be defined in post-deblending catalogs. elif "deblend_parentNChild" not in sources.schema or "deblend_nChild" not in sources.schema: self.log.info("Nothing to do: no deblending performed.") meas = None else: try: children = ((sources["deblend_parentNChild"] > 1) # deblend child & (sources["deblend_nChild"] == 0) # not deblended ) children = _filterSkySources(sources, children) except LookupError as e: # Probably "parent"; all other columns already checked raise MetricComputationError("Invalid input catalog") from e else: nChildren = np.count_nonzero(children) meas = Measurement(self.config.metricName, nChildren * u.dimensionless_unscaled) return Struct(measurement=meas)
def makeMeasurement(self, timings): """Compute a wall-clock measurement from metadata provided by `lsst.utils.timer.timeMethod`. Parameters ---------- timings : `dict` [`str`, any] A representation of the metadata passed to `run`. The `dict` has the following keys: ``"StartTime"`` The time the target method started (`float` or `None`). ``"EndTime"`` The time the target method ended (`float` or `None`). ``"StartTimestamp"``, ``"EndTimestamp"`` The start and end timestamps, in an ISO 8601-compliant format (`str` or `None`). Returns ------- measurement : `lsst.verify.Measurement` or `None` The running time of the target method. Raises ------ MetricComputationError Raised if the timing metadata are invalid. """ if timings["StartTime"] is not None or timings["EndTime"] is not None: try: totalTime = timings["EndTime"] - timings["StartTime"] except TypeError: raise MetricComputationError("Invalid metadata") else: meas = Measurement(self.config.metricName, totalTime * u.second) meas.notes["estimator"] = "utils.timer.timeMethod" if timings["StartTimestamp"]: meas.extras["start"] = Datum(timings["StartTimestamp"]) if timings["EndTimestamp"]: meas.extras["end"] = Datum(timings["EndTimestamp"]) return meas else: self.log.info("Nothing to do: no timing information for %s found.", self.config.target) return None
def makeMeasurement(self, values): """Compute the number of non-updated DIAObjects. AssociationTask reports each pre-existing DIAObject as either updated (associated with a new DIASource) or unassociated. Parameters ---------- values : `dict` [`str`, `int` or `None`] A `dict` representation of the metadata. Each `dict` has the following keys: ``"updatedObjects"`` The number of DIAObjects updated for this image (`int` or `None`). May be `None` if the image was not successfully associated. ``"unassociatedObjects"`` The number of DIAObjects not associated with a DiaSource in this image (`int` or `None`). May be `None` if the image was not successfully associated. Returns ------- measurement : `lsst.verify.Measurement` or `None` The total number of unassociated objects. """ if values["updatedObjects"] is not None \ and values["unassociatedObjects"] is not None: try: nUpdated = int(values["updatedObjects"]) nUnassociated = int(values["unassociatedObjects"]) except (ValueError, TypeError) as e: raise MetricComputationError( "Corrupted value of numUpdatedDiaObjects " "or numUnassociatedDiaObjects") from e else: if nUpdated <= 0 and nUnassociated <= 0: return None # No pre-existing DIAObjects; no fraction to compute else: fraction = nUpdated / (nUpdated + nUnassociated) return Measurement(self.config.metricName, fraction * u.dimensionless_unscaled) else: self.log.info("Nothing to do: no association results found.") return None
def run(self, sources): """Count the number of deblended science sources. Parameters ---------- sources : `lsst.afw.table.SourceCatalog` or `None` A science source catalog, which may be empty or `None`. Returns ------- result : `lsst.pipe.base.Struct` A `~lsst.pipe.base.Struct` containing the following component: ``measurement`` the total number of deblended science sources (`lsst.verify.Measurement`). If no deblending information is available in ``sources``, this is `None`. Raises ------ MetricComputationError Raised if ``sources`` is missing mandatory keys for source catalogs. """ if sources is None: self.log.info("Nothing to do: no catalogs found.") meas = None elif "deblend_nChild" not in sources.schema: self.log.info("Nothing to do: no deblending performed.") meas = None else: try: deblended = ((sources["parent"] == 0) # top-level source & (sources["deblend_nChild"] > 0) # deblended ) deblended = _filterSkySources(sources, deblended) except LookupError as e: # Probably "parent"; all other columns already checked raise MetricComputationError("Invalid input catalog") from e else: nDeblended = np.count_nonzero(deblended) meas = Measurement(self.config.metricName, nDeblended * u.dimensionless_unscaled) return Struct(measurement=meas)
def run(self, sciSources, diaSources): """Compute the ratio of DIASources to science sources. Parameters ---------- sciSources : iterable of `lsst.afw.table.SourceCatalog` A collection of science source catalogs, one for each unit of processing to be incorporated into this metric. Its elements may be `None` to represent missing data. diaSources : iterable of `lsst.afw.table.SourceCatalog` A collection of difference imaging catalogs similar to ``sciSources``. Returns ------- result : `lsst.pipe.base.Struct` A `~lsst.pipe.base.Struct` containing the following component: ``measurement`` the ratio (`lsst.verify.Measurement` or `None`) """ nSciSources = 0 nDiaSources = 0 inputData = False for sciCatalog, diaCatalog in zip(sciSources, diaSources): if diaCatalog is not None and sciCatalog is not None: nSciSources += len(sciCatalog) nDiaSources += len(diaCatalog) inputData = True if inputData: metricName = self.getOutputMetricName(self.config) if nSciSources <= 0.0: raise MetricComputationError( "No science sources found; ratio of DIASources to science sources ill-defined.") meas = Measurement(metricName, 0.0 * u.dimensionless_unscaled) else: meas = Measurement(metricName, nDiaSources / nSciSources * u.dimensionless_unscaled) else: self.log.info("Nothing to do: no catalogs found.") meas = None return Struct(measurement=meas)
def makeMeasurement(self, memory): """Compute a maximum resident set size measurement from metadata provided by `lsst.utils.timer.timeMethod`. Parameters ---------- memory : `dict` [`str`, any] A representation of the metadata passed to `run`. Each `dict` has the following keys: ``"EndMemory"`` The memory usage at the end of the method (`int` or `None`). ``"MetadataVersion"`` The version of the task metadata in which the value was stored (`int` or `None`). `None` is assumed to be version 0. Returns ------- measurement : `lsst.verify.Measurement` or `None` The maximum memory usage of the target method. Raises ------ MetricComputationError Raised if the memory metadata are invalid. """ if memory["EndMemory"] is not None: try: maxMemory = int(memory["EndMemory"]) version = memory["MetadataVersion"] \ if memory["MetadataVersion"] else 0 except (ValueError, TypeError) as e: raise MetricComputationError("Invalid metadata") from e else: meas = Measurement(self.config.metricName, self._addUnits(maxMemory, version)) meas.notes['estimator'] = 'utils.timer.timeMethod' return meas else: self.log.info("Nothing to do: no memory information for %s found.", self.config.target) return None
def makeMeasurement(self, dbHandle, outputDataId): """Compute the number of unassociated DIAObjects. Parameters ---------- dbHandle : `lsst.dax.apdb.Apdb` A database instance. outputDataId : any data ID type The subset of the database to which this measurement applies. Must be empty, as the number of unassociated sources is ill-defined for subsets of the dataset. Returns ------- measurement : `lsst.verify.Measurement` The total number of unassociated objects. Raises ------ MetricComputationError Raised on any failure to query the database. ValueError Raised if outputDataId is not empty """ # All data ID types define keys() if outputDataId.keys() - {'instrument'}: raise ValueError( "%s must not be associated with specific data IDs (gave %s)." % (self.config.metricName, outputDataId)) try: nUnassociatedDiaObjects = dbHandle.countUnassociatedObjects() except Exception as e: raise MetricComputationError( "Could not get unassociated objects from database") from e meas = Measurement(self.config.metricName, nUnassociatedDiaObjects * u.count) return meas
def _extractMetadata(metadata, metadataKeys): """Read multiple keys from a metadata object. Parameters ---------- metadata : `lsst.daf.base.PropertySet` A metadata object, assumed not `None`. metadataKeys : `dict` [`str`, `str`] Keys are arbitrary labels, values are metadata keys (or their substrings) in the format of `lsst.pipe.base.Task.getFullMetadata()`. Returns ------- metadataValues : `dict` [`str`, any] Keys are the same as for ``metadataKeys``, values are the value of each metadata key, or `None` if no matching key was found. Raises ------ lsst.verify.tasks.MetricComputationError Raised if any metadata key string has more than one match in ``metadata``. """ data = {} for dataName, keyFragment in metadataKeys.items(): matchingKeys = MetadataMetricTask._searchKeys( metadata, keyFragment) if len(matchingKeys) == 1: key, = matchingKeys data[dataName] = metadata.getScalar(key) elif not matchingKeys: data[dataName] = None else: error = "String %s matches multiple metadata keys: %s" \ % (keyFragment, matchingKeys) raise MetricComputationError(error) return data
def makeMeasurement(self, values): """Compute the number of non-updated DIAObjects. Parameters ---------- values : sequence [`dict` [`str`, `int` or `None`]] A list where each element corresponds to a metadata object passed to `run`. Each `dict` has the following key: ``"unassociatedObjects"`` The number of DIAObjects not associated with a DiaSource in this image (`int` or `None`). May be `None` if the image was not successfully associated. Returns ------- measurement : `lsst.verify.Measurement` or `None` The total number of unassociated objects. """ nNew = 0 associated = False for value in values: if value["unassociatedObjects"] is not None: try: nNew += value["unassociatedObjects"] except TypeError as e: raise MetricComputationError( "Corrupted value of numUnassociatedDiaObjects") from e associated = True if associated: return Measurement(self.getOutputMetricName(self.config), nNew * u.count) else: self.log.info("Nothing to do: no association results found.") return None
def run(self, *args, **kwargs): raise MetricComputationError()