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
41 from lsst.meas.algorithms
import ReferenceObjectLoader
45 __all__ = [
'FgcmLoadReferenceCatalogConfig',
'FgcmLoadReferenceCatalogTask']
49 """Config for FgcmLoadReferenceCatalogTask"""
51 refObjLoader = pexConfig.ConfigurableField(
52 target=LoadIndexedReferenceObjectsTask,
53 doc=
"Reference object loader for photometry",
55 refFilterMap = pexConfig.DictField(
56 doc=
"Mapping from camera 'filterName' to reference filter name.",
60 deprecated=(
"This field is no longer used, and has been deprecated by "
61 "DM-28088. It will be removed after v22. Use "
64 filterMap = pexConfig.DictField(
65 doc=
"Mapping from physicalFilter label to reference filter name.",
70 applyColorTerms = pexConfig.Field(
71 doc=(
"Apply photometric color terms to reference stars?"
72 "Requires that colorterms be set to a ColorTermLibrary"),
76 colorterms = pexConfig.ConfigField(
77 doc=
"Library of photometric reference catalog name to color term dict.",
78 dtype=ColortermLibrary,
80 referenceSelector = pexConfig.ConfigurableField(
81 target=ReferenceSourceSelectorTask,
82 doc=
"Selection of reference sources",
88 msg =
'Must set filterMap'
89 raise pexConfig.FieldValidationError(FgcmLoadReferenceCatalogConfig.filterMap, self, msg)
91 msg =
"applyColorTerms=True requires the `colorterms` field be set to a ColortermLibrary."
92 raise pexConfig.FieldValidationError(FgcmLoadReferenceCatalogConfig.colorterms, self, msg)
97 Load multi-band reference objects from a reference catalog.
101 butler: `lsst.daf.persistence.Butler`
102 Data butler for reading catalogs
104 ConfigClass = FgcmLoadReferenceCatalogConfig
105 _DefaultName =
'fgcmLoadReferenceCatalog'
107 def __init__(self, butler=None, refObjLoader=None, **kwargs):
108 """Construct an FgcmLoadReferenceCatalogTask
112 butler: `lsst.daf.persistence.Buter`
113 Data butler for reading catalogs.
115 pipeBase.Task.__init__(self, **kwargs)
116 if refObjLoader
is None and butler
is not None:
117 self.makeSubtask(
'refObjLoader', butler=butler)
121 self.makeSubtask(
'referenceSelector')
128 Get a reference catalog that overlaps a healpix pixel, using multiple
129 filters. In addition, apply colorterms if available.
131 Return format is a numpy recarray for use with fgcm, with the format:
133 dtype = ([('ra', `np.float64`),
134 ('dec', `np.float64`),
135 ('refMag', `np.float32`, len(filterList)),
136 ('refMagErr', `np.float32`, len(filterList)])
138 Reference magnitudes (AB) will be 99 for non-detections.
143 Healpix nside of pixel to load
145 Healpix pixel of pixel to load
147 list of `str` of camera filter names.
148 nest: `bool`, optional
149 Is the pixel in nest format? Default is False.
153 fgcmRefCat: `np.recarray`
157 theta, phi = hp.pix2ang(nside, pixel, nest=nest)
158 center = lsst.geom.SpherePoint(phi * lsst.geom.radians, (np.pi/2. - theta) * lsst.geom.radians)
160 corners = hp.boundaries(nside, pixel, step=1, nest=nest)
161 theta_phi = hp.vec2ang(np.transpose(corners))
163 radius = 0.0 * lsst.geom.radians
164 for ctheta, cphi
in zip(*theta_phi):
165 rad = center.separation(lsst.geom.SpherePoint(cphi * lsst.geom.radians,
166 (np.pi/2. - ctheta) * lsst.geom.radians))
172 center.getDec().asDegrees(),
175 catPix = hp.ang2pix(nside, np.radians(90.0 - fgcmRefCat[
'dec']),
176 np.radians(fgcmRefCat[
'ra']), nest=nest)
178 inPix, = np.where(catPix == pixel)
180 return fgcmRefCat[inPix]
184 Get a reference catalog that overlaps a circular sky region, using
185 multiple filters. In addition, apply colorterms if available.
187 Return format is a numpy recarray for use with fgcm.
189 dtype = ([('ra', `np.float64`),
190 ('dec', `np.float64`),
191 ('refMag', `np.float32`, len(filterList)),
192 ('refMagErr', `np.float32`, len(filterList)])
194 Reference magnitudes (AB) will be 99 for non-detections.
199 ICRS right ascension, degrees.
201 ICRS declination, degrees.
203 Radius to search, degrees.
205 list of `str` of camera filter names.
209 fgcmRefCat: `np.recarray`
212 center = lsst.geom.SpherePoint(ra * lsst.geom.degrees, dec * lsst.geom.degrees)
218 skyCircle = self.
refObjLoaderrefObjLoader.loadSkyCircle(center,
219 radius * lsst.geom.degrees,
222 if not skyCircle.refCat.isContiguous():
223 refCat = skyCircle.refCat.copy(deep=
True)
225 refCat = skyCircle.refCat
228 goodSources = self.referenceSelector.selectSources(refCat)
229 selected = goodSources.selected
231 fgcmRefCat = np.zeros(np.sum(selected), dtype=[(
'ra',
'f8'),
233 (
'refMag',
'f4', len(filterList)),
234 (
'refMagErr',
'f4', len(filterList))])
235 if fgcmRefCat.size == 0:
245 conv = refCat[0][
'coord_ra'].asDegrees() / float(refCat[0][
'coord_ra'])
246 fgcmRefCat[
'ra'] = refCat[
'coord_ra'][selected] * conv
247 fgcmRefCat[
'dec'] = refCat[
'coord_dec'][selected] * conv
250 fgcmRefCat[
'refMag'][:, :] = 99.0
251 fgcmRefCat[
'refMagErr'][:, :] = 99.0
253 if self.config.applyColorTerms:
254 if isinstance(self.
refObjLoaderrefObjLoader, ReferenceObjectLoader):
256 refCatName = self.
refObjLoaderrefObjLoader.config.value.ref_dataset_name
259 refCatName = self.
refObjLoaderrefObjLoader.ref_dataset_name
262 if fluxField
is None:
265 self.log.debug(
"Applying color terms for filtername=%r" % (filterName))
267 colorterm = self.config.colorterms.getColorterm(filterName, refCatName, doRaise=
True)
269 refMag, refMagErr = colorterm.getCorrectedMagnitudes(refCat)
275 good, = np.where((np.nan_to_num(refMag[selected]) < 90.0)
276 & (np.nan_to_num(refMagErr[selected]) < 90.0)
277 & (np.nan_to_num(refMagErr[selected]) > 0.0))
279 fgcmRefCat[
'refMag'][good, i] = refMag[selected][good]
280 fgcmRefCat[
'refMagErr'][good, i] = refMagErr[selected][good]
288 good, = np.where((np.nan_to_num(refCat[fluxField][selected]) > 0.0)
289 & (np.nan_to_num(refCat[fluxField+
'Err'][selected]) > 0.0))
290 refMag = (refCat[fluxField][selected][good] * units.nJy).to_value(units.ABmag)
291 refMagErr = abMagErrFromFluxErr(refCat[fluxField+
'Err'][selected][good],
292 refCat[fluxField][selected][good])
293 fgcmRefCat[
'refMag'][good, i] = refMag
294 fgcmRefCat[
'refMagErr'][good, i] = refMagErr
298 def _determine_flux_fields(self, center, filterList):
300 Determine the flux field names for a reference catalog.
302 Will set self._fluxFields, self._referenceFilter.
306 center: `lsst.geom.SpherePoint`
307 The center around which to load test sources.
309 list of `str` of camera filter names.
317 foundReferenceFilter =
False
318 for filterName
in filterList:
319 refFilterName = self.config.filterMap.get(filterName)
320 if refFilterName
is None:
324 results = self.
refObjLoaderrefObjLoader.loadSkyCircle(center,
325 0.05 * lsst.geom.degrees,
327 foundReferenceFilter =
True
335 if not foundReferenceFilter:
336 raise RuntimeError(
"Could not find any valid flux field(s) %s" %
337 (
", ".join(filterList)))
341 for filterName
in filterList:
344 refFilterName = self.config.filterMap.get(filterName)
346 if refFilterName
is not None:
348 fluxField = getRefFluxField(results.refCat.schema, filterName=refFilterName)
353 if fluxField
is None:
354 self.log.warn(f
'No reference flux field for camera filter {filterName}')
def __init__(self, butler=None, refObjLoader=None, **kwargs)
def getFgcmReferenceStarsSkyCircle(self, ra, dec, radius, filterList)
def _determine_flux_fields(self, center, filterList)
def getFgcmReferenceStarsHealpix(self, nside, pixel, filterList, nest=False)