Source code for rubin_sim.maf.metrics.area_summary_metrics
__all__ = ("AreaSummaryMetric", "AreaThresholdMetric")
import healpy as hp
import numpy as np
from .base_metric import BaseMetric
[docs]
class AreaSummaryMetric(BaseMetric):
"""
Find the min/max of a value over the area with the 'best' results
in the metric.
This is a handy substitute for when users want to know "the WFD value".
Parameters
----------
area : `float`
The area to consider (sq degrees)
decreasing : `bool`
Should the values be sorted by increasing or decreasing order.
For values where "larger is better", decreasing (True) is probably
what you want. For metrics where "smaller is better"
(e.g., astrometric precission), set decreasing to False.
reduce_func : None
The function to reduce the clipped values by.
Will default to min/max depending on the bool val of the decreasing
kwarg.
"""
def __init__(
self,
col="metricdata",
metric_name="AreaSummary",
area=18000.0,
decreasing=True,
reduce_func=None,
**kwargs,
):
super().__init__(col=col, metric_name=metric_name, **kwargs)
self.area = area
self.decreasing = decreasing
self.reduce_func = reduce_func
self.mask_val = np.nan # Include so all values get passed
self.col = col
if reduce_func is None:
if decreasing:
self.reduce_func = np.min
else:
self.reduce_func = np.max
else:
self.reduce_func = reduce_func
[docs]
def run(self, data_slice, slice_point=None):
# find out what nside we have
nside = hp.npix2nside(data_slice.size)
pix_area = hp.nside2pixarea(nside, degrees=True)
n_pix_needed = int(np.ceil(self.area / pix_area))
# Only use the finite data
data = data_slice[self.col][np.isfinite(data_slice[self.col].astype(float))]
order = np.argsort(data)
if self.decreasing:
order = order[::-1]
result = self.reduce_func(data[order][0:n_pix_needed])
return result
[docs]
class AreaThresholdMetric(BaseMetric):
"""Find the amount of area on the sky that meets a given threshold value.
The area per pixel is determined from the size of the metric_values
array passed to the summary metric.
This assumes that both all values are passed and that the metric was
calculated with a healpix slicer.
Parameters
----------
upper_threshold : `float` or None
The metric value must be below this threshold to count toward the area.
Default None implies no upper bound.
lower_threshold : `float` or None, opt
The metric value must be above this threshold to count toward the area.
Default None implies no lower bound.
"""
def __init__(
self,
col="metricdata",
metric_name="AreaThreshold",
upper_threshold=None,
lower_threshold=None,
**kwargs,
):
super().__init__(col=col, metric_name=metric_name, **kwargs)
self.upper_threshold = upper_threshold
self.lower_threshold = lower_threshold
self.mask_val = np.nan # Include so all values get passed
self.col = col
self.units = "degrees"
[docs]
def run(self, data_slice, slice_point=None):
# find out what nside we have
nside = hp.npix2nside(data_slice.size)
pix_area = hp.nside2pixarea(nside, degrees=True)
# Look for pixels which match the critera for the thresholds
if self.upper_threshold is None and self.lower_threshold is None:
npix = len(data_slice)
elif self.upper_threshold is None:
npix = len(np.where(data_slice[self.col] > self.lower_threshold)[0])
elif self.lower_threshold is None:
npix = len(np.where(data_slice[self.col] < self.upper_threshold)[0])
else:
npix = len(
np.where(
(data_slice[self.col] > self.lower_threshold)
and (data_slice[self.col] < self.upper_threshold)
)[0]
)
area = pix_area * npix
return area