Source code for rubin_sim.maf.metrics.scaling_metrics
__all__ = ("NgalScaleMetric", "NlcPointsMetric")
import healpy as hp
import numpy as np
from rubin_sim.maf.maps import TrilegalDensityMap
from rubin_sim.phot_utils import DustValues
from .base_metric import BaseMetric
[docs]
class NgalScaleMetric(BaseMetric):
"""Approximate number of galaxies, scaled by median seeing.
Parameters
----------
a_max : `float`
The maximum dust extinction to allow. Anything with higher dust
extinction is considered to have zero usable galaxies.
m5min : `float`
The minimum coadded 5-sigma depth to allow. Anything less is
considered to have zero usable galaxies.
filter : `str`
The filter to use. Any visits in other filters are ignored.
"""
def __init__(
self,
seeing_col="seeingFwhmEff",
m5_col="fiveSigmaDepth",
metric_name="NgalScale",
filtername="i",
a_max=0.2,
m5min=26.0,
filter_col="filter",
**kwargs,
):
maps = ["DustMap"]
units = "N gals"
self.seeing_col = seeing_col
self.m5_col = m5_col
self.filtername = filtername
self.filter_col = filter_col
self.a_max = a_max
self.m5min = m5min
super().__init__(
col=[self.m5_col, self.filter_col, self.seeing_col],
maps=maps,
units=units,
metric_name=metric_name,
**kwargs,
)
dust_properties = DustValues()
self.ax1 = dust_properties.ax1
[docs]
def run(self, data_slice, slice_point):
# I'm a little confused why there's a dust cut and an M5 cut,
# but whatever
a_x = self.ax1[data_slice[self.filter_col][0]] * slice_point["ebv"]
if a_x > self.a_max:
return 0
in_filt = np.where(data_slice[self.filter_col] == self.filtername)
coadd_m5 = 1.25 * np.log10(np.sum(10.0 ** (0.8 * data_slice[self.m5_col][in_filt])))
if coadd_m5 < self.m5min:
return 0
theta = np.median(data_slice[self.seeing_col])
# N gals per arcmin2
ngal_per_arcmin2 = 57 * (0.75 / theta) ** 1.5
area = hp.nside2pixarea(slice_point["nside"], degrees=True) * 3600.0
ngal = ngal_per_arcmin2 * area
return ngal
[docs]
class NlcPointsMetric(BaseMetric):
"""Number of points in stellar light curves
Parameters
----------
ndpmin : `int`
The number of points to demand on a lightcurve in a single
filter to have that light curve qualify.
mags : `float`
The magnitude of our fiducial object (maybe make it a dict in the
future to support arbitrary colors).
maps : `list` [`~rubin_sim.maf.map`] or None
List of stellar density maps to use.
Default of None loads Trilegal maps.
nside : `int`
The nside is needed to make sure the loaded maps
match the slicer nside.
"""
def __init__(
self,
ndpmin=10,
mags=21.0,
m5_col="fiveSigmaDepth",
filter_col="filter",
metric_name="NlcPoints",
maps=None,
nside=128,
**kwargs,
):
units = "N LC points"
self.ndpmin = ndpmin
self.mags = mags
self.m5_col = m5_col
self.filter_col = filter_col
self.nside = nside
if maps is None:
maps = [TrilegalDensityMap(filtername=fn, nside=nside) for fn in "ugrizy"]
super().__init__(
col=[self.m5_col, self.filter_col],
maps=maps,
units=units,
metric_name=metric_name,
**kwargs,
)
[docs]
def run(self, data_slice, slice_point):
if self.nside != slice_point["nside"]:
raise ValueError("nside of metric does not match slicer")
pix_area = hp.nside2pixarea(slice_point["nside"], degrees=True)
nlcpoints = 0
# Let's do it per filter
for filtername in np.unique(data_slice[self.filter_col]):
in_filt = np.where(
(data_slice[self.filter_col] == filtername) & (data_slice[self.m5_col] > self.mags)
)[0]
n_obs = np.size(in_filt)
if n_obs > self.ndpmin:
nstars = (
np.interp(
self.mags,
slice_point[f"starMapBins_{filtername}"][1:],
slice_point[f"starLumFunc_{filtername}"],
)
* pix_area
)
nlcpoints += n_obs * nstars
return nlcpoints