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(
268 filterName=filterName, photoCatName=refCatName, doRaise=
True)
270 refMag, refMagErr = colorterm.getCorrectedMagnitudes(refCat, filterName)
276 good, = np.where((np.nan_to_num(refMag[selected]) < 90.0)
277 & (np.nan_to_num(refMagErr[selected]) < 90.0)
278 & (np.nan_to_num(refMagErr[selected]) > 0.0))
280 fgcmRefCat[
'refMag'][good, i] = refMag[selected][good]
281 fgcmRefCat[
'refMagErr'][good, i] = refMagErr[selected][good]
289 good, = np.where((np.nan_to_num(refCat[fluxField][selected]) > 0.0)
290 & (np.nan_to_num(refCat[fluxField+
'Err'][selected]) > 0.0))
291 refMag = (refCat[fluxField][selected][good] * units.nJy).to_value(units.ABmag)
292 refMagErr = abMagErrFromFluxErr(refCat[fluxField+
'Err'][selected][good],
293 refCat[fluxField][selected][good])
294 fgcmRefCat[
'refMag'][good, i] = refMag
295 fgcmRefCat[
'refMagErr'][good, i] = refMagErr
299 def _determine_flux_fields(self, center, filterList):
301 Determine the flux field names for a reference catalog.
303 Will set self._fluxFields, self._referenceFilter.
307 center: `lsst.geom.SpherePoint`
308 The center around which to load test sources.
310 list of `str` of camera filter names.
318 foundReferenceFilter =
False
319 for filterName
in filterList:
320 refFilterName = self.config.filterMap.get(filterName)
321 if refFilterName
is None:
325 results = self.
refObjLoaderrefObjLoader.loadSkyCircle(center,
326 0.05 * lsst.geom.degrees,
328 foundReferenceFilter =
True
336 if not foundReferenceFilter:
337 raise RuntimeError(
"Could not find any valid flux field(s) %s" %
338 (
", ".join(filterList)))
342 for filterName
in filterList:
345 refFilterName = self.config.filterMap.get(filterName)
347 if refFilterName
is not None:
349 fluxField = getRefFluxField(results.refCat.schema, filterName=refFilterName)
354 if fluxField
is None:
355 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)