23 """Load reference catalog objects for input to FGCM.
25 This task will load multi-band reference objects and apply color terms (if
26 configured). This wrapper around LoadReferenceObjects task also allows loading
27 by healpix pixel (the native pixelization of fgcm), and is self-contained so
28 the task can be called by third-party code.
33 from astropy
import units
35 import lsst.pex.config
as pexConfig
36 import lsst.pipe.base
as pipeBase
37 from lsst.meas.algorithms
import LoadIndexedReferenceObjectsTask, ReferenceSourceSelectorTask
38 from lsst.meas.algorithms
import getRefFluxField
39 from lsst.pipe.tasks.colorterms
import ColortermLibrary
40 from lsst.afw.image
import abMagErrFromFluxErr
43 __all__ = [
'FgcmLoadReferenceCatalogConfig',
'FgcmLoadReferenceCatalogTask']
47 """Config for FgcmLoadReferenceCatalogTask"""
49 refObjLoader = pexConfig.ConfigurableField(
50 target=LoadIndexedReferenceObjectsTask,
51 doc=
"Reference object loader for photometry",
53 refFilterMap = pexConfig.DictField(
54 doc=
"Mapping from camera 'filterName' to reference filter name.",
59 applyColorTerms = pexConfig.Field(
60 doc=(
"Apply photometric color terms to reference stars?"
61 "Requires that colorterms be set to a ColorTermLibrary"),
65 colorterms = pexConfig.ConfigField(
66 doc=
"Library of photometric reference catalog name to color term dict.",
67 dtype=ColortermLibrary,
69 referenceSelector = pexConfig.ConfigurableField(
70 target=ReferenceSourceSelectorTask,
71 doc=
"Selection of reference sources",
77 msg =
'Must set refFilterMap'
78 raise pexConfig.FieldValidationError(FgcmLoadReferenceCatalogConfig.refFilterMap, self, msg)
80 msg =
"applyColorTerms=True requires the `colorterms` field be set to a ColortermLibrary."
81 raise pexConfig.FieldValidationError(FgcmLoadReferenceCatalogConfig.colorterms, self, msg)
86 Load multi-band reference objects from a reference catalog.
90 butler: `lsst.daf.persistence.Butler`
91 Data butler for reading catalogs
93 ConfigClass = FgcmLoadReferenceCatalogConfig
94 _DefaultName =
'fgcmLoadReferenceCatalog'
97 """Construct an FgcmLoadReferenceCatalogTask
101 butler: `lsst.daf.persistence.Buter`
102 Data butler for reading catalogs.
104 pipeBase.Task.__init__(self, *args, **kwargs)
106 self.makeSubtask(
'refObjLoader', butler=butler)
107 self.makeSubtask(
'referenceSelector')
114 Get a reference catalog that overlaps a healpix pixel, using multiple
115 filters. In addition, apply colorterms if available.
117 Return format is a numpy recarray for use with fgcm, with the format:
119 dtype = ([('ra', `np.float64`),
120 ('dec', `np.float64`),
121 ('refMag', `np.float32`, len(filterList)),
122 ('refMagErr', `np.float32`, len(filterList)])
124 Reference magnitudes (AB) will be 99 for non-detections.
129 Healpix nside of pixel to load
131 Healpix pixel of pixel to load
133 list of `str` of camera filter names.
134 nest: `bool`, optional
135 Is the pixel in nest format? Default is False.
139 fgcmRefCat: `np.recarray`
143 theta, phi = hp.pix2ang(nside, pixel, nest=nest)
144 center = lsst.geom.SpherePoint(phi * lsst.geom.radians, (np.pi/2. - theta) * lsst.geom.radians)
146 corners = hp.boundaries(nside, pixel, step=1, nest=nest)
147 theta_phi = hp.vec2ang(np.transpose(corners))
149 radius = 0.0 * lsst.geom.radians
150 for ctheta, cphi
in zip(*theta_phi):
151 rad = center.separation(lsst.geom.SpherePoint(cphi * lsst.geom.radians,
152 (np.pi/2. - ctheta) * lsst.geom.radians))
158 center.getDec().asDegrees(),
161 catPix = hp.ang2pix(nside, np.radians(90.0 - fgcmRefCat[
'dec']),
162 np.radians(fgcmRefCat[
'ra']), nest=nest)
164 inPix, = np.where(catPix == pixel)
166 return fgcmRefCat[inPix]
170 Get a reference catalog that overlaps a circular sky region, using
171 multiple filters. In addition, apply colorterms if available.
173 Return format is a numpy recarray for use with fgcm.
175 dtype = ([('ra', `np.float64`),
176 ('dec', `np.float64`),
177 ('refMag', `np.float32`, len(filterList)),
178 ('refMagErr', `np.float32`, len(filterList)])
180 Reference magnitudes (AB) will be 99 for non-detections.
185 ICRS right ascension, degrees.
187 ICRS declination, degrees.
189 Radius to search, degrees.
191 list of `str` of camera filter names.
195 fgcmRefCat: `np.recarray`
198 center = lsst.geom.SpherePoint(ra * lsst.geom.degrees, dec * lsst.geom.degrees)
204 skyCircle = self.refObjLoader.loadSkyCircle(center,
205 radius * lsst.geom.degrees,
208 if not skyCircle.refCat.isContiguous():
209 refCat = skyCircle.refCat.copy(deep=
True)
211 refCat = skyCircle.refCat
214 goodSources = self.referenceSelector.selectSources(refCat)
215 selected = goodSources.selected
217 fgcmRefCat = np.zeros(np.sum(selected), dtype=[(
'ra',
'f8'),
219 (
'refMag',
'f4', len(filterList)),
220 (
'refMagErr',
'f4', len(filterList))])
221 if fgcmRefCat.size == 0:
231 conv = refCat[0][
'coord_ra'].asDegrees() / float(refCat[0][
'coord_ra'])
232 fgcmRefCat[
'ra'] = refCat[
'coord_ra'][selected] * conv
233 fgcmRefCat[
'dec'] = refCat[
'coord_dec'][selected] * conv
236 fgcmRefCat[
'refMag'][:, :] = 99.0
237 fgcmRefCat[
'refMagErr'][:, :] = 99.0
239 if self.config.applyColorTerms:
241 refCatName = self.refObjLoader.ref_dataset_name
242 except AttributeError:
245 raise RuntimeError(
"Cannot perform colorterm corrections with a.net refcats.")
248 if fluxField
is None:
251 self.log.debug(
"Applying color terms for filtername=%r" % (filterName))
253 colorterm = self.config.colorterms.getColorterm(
254 filterName=filterName, photoCatName=refCatName, doRaise=
True)
256 refMag, refMagErr = colorterm.getCorrectedMagnitudes(refCat, filterName)
262 good, = np.where((np.nan_to_num(refMag[selected]) < 90.0) &
263 (np.nan_to_num(refMagErr[selected]) < 90.0) &
264 (np.nan_to_num(refMagErr[selected]) > 0.0))
266 fgcmRefCat[
'refMag'][good, i] = refMag[selected][good]
267 fgcmRefCat[
'refMagErr'][good, i] = refMagErr[selected][good]
277 good, = np.where((np.nan_to_num(refCat[fluxField][selected]) > 0.0) &
278 (np.nan_to_num(refCat[fluxField+
'Err'][selected]) > 0.0))
279 refMag = (refCat[fluxField][selected][good] * units.Jy).to_value(units.ABmag)
280 refMagErr = abMagErrFromFluxErr(refCat[fluxField+
'Err'][selected][good],
281 refCat[fluxField][selected][good])
282 fgcmRefCat[
'refMag'][good, i] = refMag
283 fgcmRefCat[
'refMagErr'][good, i] = refMagErr
287 def _determine_flux_fields(self, center, filterList):
289 Determine the flux field names for a reference catalog.
291 Will set self._fluxFields, self._referenceFilter.
295 center: `lsst.geom.SpherePoint`
296 The center around which to load test sources.
298 list of `str` of camera filter names.
306 foundReferenceFilter =
False
307 for filterName
in filterList:
308 refFilterName = self.config.refFilterMap.get(filterName)
309 if refFilterName
is None:
313 results = self.refObjLoader.loadSkyCircle(center,
314 0.05 * lsst.geom.degrees,
316 foundReferenceFilter =
True
324 if not foundReferenceFilter:
325 raise RuntimeError(
"Could not find any valid flux field(s) %s" %
326 (
", ".join(filterList)))
330 for filterName
in filterList:
333 refFilterName = self.config.refFilterMap.get(filterName)
335 if refFilterName
is not None:
337 fluxField = getRefFluxField(results.refCat.schema, filterName=refFilterName)
342 if fluxField
is None:
343 self.log.warn(f
'No reference flux field for camera filter {filterName}')