Coverage for python/lsst/faro/utils/phot_repeat.py: 16%
28 statements
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-29 03:28 -0700
« prev ^ index » next coverage.py v6.4.1, created at 2022-06-29 03:28 -0700
1# This file is part of faro.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22import numpy as np
23import astropy.units as u
25from lsst.faro.utils.filtermatches import filterMatches
27__all__ = ("photRepeat", "calcPhotRepeat")
30def photRepeat(matchedCatalog, magName=None, nMinPhotRepeat=50, **filterargs):
31 """Measure the photometric repeatability of a set of observations.
33 Parameters
34 ----------
35 matchedCatalog : `lsst.afw.table.base.Catalog`
36 `~lsst.afw.table.base.Catalog` object as created by
37 `~lsst.afw.table.multiMatch` matching of sources from multiple visits.
38 magName : `str`
39 Name of the magnitude field. Default "slot_PsfFlux_mag".
40 nMinPhotRepeat : `int`
41 Minimum number of sources required to return a measurement.
42 **filterargs
43 Additional arguments to pass to `filterMatches` for catalog filtering.
45 Returns
46 -------
47 photo_resid_meas : `dict`
48 Photometric repeatability statistics and related quantities as measured
49 by `calcPhotRepeat`. Returns NaN values if there are fewer than
50 nMinPhotRepeat sources in the matched catalog.
51 """
52 if magName is None:
53 magName = "slot_PsfFlux_mag"
54 filteredCat = filterMatches(matchedCatalog, **filterargs)
55 magKey = filteredCat.schema.find(magName).key
57 # Require at least nMinPhotRepeat objects to calculate the repeatability:
58 if filteredCat.count > nMinPhotRepeat:
59 phot_resid_meas = calcPhotRepeat(filteredCat, magKey)
60 # Check that the number of stars with >2 visits is >nMinPhotRepeat:
61 okcount = phot_resid_meas["count"] > 2
62 if np.sum(okcount) > nMinPhotRepeat:
63 return phot_resid_meas
64 else:
65 return {"nomeas": np.nan * u.mmag}
66 else:
67 return {"nomeas": np.nan * u.mmag}
70def calcPhotRepeat(matches, magKey):
71 """Calculate the photometric repeatability of a set of measurements.
73 Parameters
74 ----------
75 matches : `lsst.afw.table.GroupView`
76 `~lsst.afw.table.GroupView` of sources matched between visits using
77 MultiMatch, as provided by `lsst.faro.utils.matcher.matchCatalogs`.
78 magKey : `lsst.afw.table` schema key
79 Magnitude column key in the ``GroupView``.
80 E.g., ``magKey = allMatches.schema.find("slot_ModelFlux_mag").key``
81 where ``allMatches`` is the result of `lsst.afw.table.MultiMatch.finish()`.
83 Returns
84 -------
85 statistics : `dict`
86 Repeatability statistics and ancillary quantities calculated from the
87 input ``GroupView`` matched catalog. Fields are:
88 - ``count``: array of number of nonzero magnitude measurements for each
89 input source.
90 - ``magMean``: `~astropy.unit.Quantity` array of mean magnitudes, in mag,
91 for each input source.
92 - ``rms``: `~astropy.unit.Quantity` array of RMS photometric repeatability
93 about the mean, in mmag, for each input source.
94 - ``repeatability``: scalar `~astropy.unit.Quantity` of the median ``rms``.
95 This is calculated using all sources with more than 2 magnitude
96 measurements, and reported in mmag.
97 - ``magResid``: `~astropy.unit.Quantity` array for each input source,
98 containing the magnitude residuals, in mmag, with respect to ``magMean``.
99 """
100 matches_rms = (matches.aggregate(np.nanstd, field=magKey) * u.mag).to(u.mmag)
101 matches_count = matches.aggregate(np.count_nonzero, field=magKey)
102 matches_mean = matches.aggregate(np.mean, field=magKey) * u.mag
103 magResid = []
104 for gp in matches.groups:
105 magResid.append(((gp[magKey] - np.mean(gp[magKey])) * (u.mag)).to(u.mmag))
106 magResid = np.array(magResid, dtype="object")
107 okrms = matches_count > 2
108 if np.sum(okrms) > 0:
109 return {
110 "count": matches_count,
111 "magMean": matches_mean,
112 "rms": matches_rms,
113 "repeatability": np.median(matches_rms[okrms]),
114 "magResid": magResid,
115 }
116 else:
117 return {
118 "count": 0,
119 "magMean": np.nan * u.mag,
120 "rms": np.nan * u.mmag,
121 "repeatability": np.nan * u.mmag,
122 "magDiffs": 0 * u.mmag,
123 }