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.",
61 applyColorTerms = pexConfig.Field(
62 doc=(
"Apply photometric color terms to reference stars?"
63 "Requires that colorterms be set to a ColorTermLibrary"),
67 colorterms = pexConfig.ConfigField(
68 doc=
"Library of photometric reference catalog name to color term dict.",
69 dtype=ColortermLibrary,
71 referenceSelector = pexConfig.ConfigurableField(
72 target=ReferenceSourceSelectorTask,
73 doc=
"Selection of reference sources",
79 msg =
'Must set refFilterMap'
80 raise pexConfig.FieldValidationError(FgcmLoadReferenceCatalogConfig.refFilterMap, self, msg)
82 msg =
"applyColorTerms=True requires the `colorterms` field be set to a ColortermLibrary."
83 raise pexConfig.FieldValidationError(FgcmLoadReferenceCatalogConfig.colorterms, self, msg)
88 Load multi-band reference objects from a reference catalog.
92 butler: `lsst.daf.persistence.Butler`
93 Data butler for reading catalogs
95 ConfigClass = FgcmLoadReferenceCatalogConfig
96 _DefaultName =
'fgcmLoadReferenceCatalog'
98 def __init__(self, butler=None, refObjLoader=None, **kwargs):
99 """Construct an FgcmLoadReferenceCatalogTask
103 butler: `lsst.daf.persistence.Buter`
104 Data butler for reading catalogs.
106 pipeBase.Task.__init__(self, **kwargs)
107 if refObjLoader
is None and butler
is not None:
108 self.makeSubtask(
'refObjLoader', butler=butler)
112 self.makeSubtask(
'referenceSelector')
119 Get a reference catalog that overlaps a healpix pixel, using multiple
120 filters. In addition, apply colorterms if available.
122 Return format is a numpy recarray for use with fgcm, with the format:
124 dtype = ([('ra', `np.float64`),
125 ('dec', `np.float64`),
126 ('refMag', `np.float32`, len(filterList)),
127 ('refMagErr', `np.float32`, len(filterList)])
129 Reference magnitudes (AB) will be 99 for non-detections.
134 Healpix nside of pixel to load
136 Healpix pixel of pixel to load
138 list of `str` of camera filter names.
139 nest: `bool`, optional
140 Is the pixel in nest format? Default is False.
144 fgcmRefCat: `np.recarray`
148 theta, phi = hp.pix2ang(nside, pixel, nest=nest)
149 center = lsst.geom.SpherePoint(phi * lsst.geom.radians, (np.pi/2. - theta) * lsst.geom.radians)
151 corners = hp.boundaries(nside, pixel, step=1, nest=nest)
152 theta_phi = hp.vec2ang(np.transpose(corners))
154 radius = 0.0 * lsst.geom.radians
155 for ctheta, cphi
in zip(*theta_phi):
156 rad = center.separation(lsst.geom.SpherePoint(cphi * lsst.geom.radians,
157 (np.pi/2. - ctheta) * lsst.geom.radians))
163 center.getDec().asDegrees(),
166 catPix = hp.ang2pix(nside, np.radians(90.0 - fgcmRefCat[
'dec']),
167 np.radians(fgcmRefCat[
'ra']), nest=nest)
169 inPix, = np.where(catPix == pixel)
171 return fgcmRefCat[inPix]
175 Get a reference catalog that overlaps a circular sky region, using
176 multiple filters. In addition, apply colorterms if available.
178 Return format is a numpy recarray for use with fgcm.
180 dtype = ([('ra', `np.float64`),
181 ('dec', `np.float64`),
182 ('refMag', `np.float32`, len(filterList)),
183 ('refMagErr', `np.float32`, len(filterList)])
185 Reference magnitudes (AB) will be 99 for non-detections.
190 ICRS right ascension, degrees.
192 ICRS declination, degrees.
194 Radius to search, degrees.
196 list of `str` of camera filter names.
200 fgcmRefCat: `np.recarray`
203 center = lsst.geom.SpherePoint(ra * lsst.geom.degrees, dec * lsst.geom.degrees)
209 skyCircle = self.
refObjLoaderrefObjLoader.loadSkyCircle(center,
210 radius * lsst.geom.degrees,
213 if not skyCircle.refCat.isContiguous():
214 refCat = skyCircle.refCat.copy(deep=
True)
216 refCat = skyCircle.refCat
219 goodSources = self.referenceSelector.selectSources(refCat)
220 selected = goodSources.selected
222 fgcmRefCat = np.zeros(np.sum(selected), dtype=[(
'ra',
'f8'),
224 (
'refMag',
'f4', len(filterList)),
225 (
'refMagErr',
'f4', len(filterList))])
226 if fgcmRefCat.size == 0:
236 conv = refCat[0][
'coord_ra'].asDegrees() / float(refCat[0][
'coord_ra'])
237 fgcmRefCat[
'ra'] = refCat[
'coord_ra'][selected] * conv
238 fgcmRefCat[
'dec'] = refCat[
'coord_dec'][selected] * conv
241 fgcmRefCat[
'refMag'][:, :] = 99.0
242 fgcmRefCat[
'refMagErr'][:, :] = 99.0
244 if self.config.applyColorTerms:
245 if isinstance(self.
refObjLoaderrefObjLoader, ReferenceObjectLoader):
247 refCatName = self.
refObjLoaderrefObjLoader.config.value.ref_dataset_name
250 refCatName = self.
refObjLoaderrefObjLoader.ref_dataset_name
253 if fluxField
is None:
256 self.log.debug(
"Applying color terms for filtername=%r" % (filterName))
258 colorterm = self.config.colorterms.getColorterm(
259 filterName=filterName, photoCatName=refCatName, doRaise=
True)
261 refMag, refMagErr = colorterm.getCorrectedMagnitudes(refCat, filterName)
267 good, = np.where((np.nan_to_num(refMag[selected]) < 90.0)
268 & (np.nan_to_num(refMagErr[selected]) < 90.0)
269 & (np.nan_to_num(refMagErr[selected]) > 0.0))
271 fgcmRefCat[
'refMag'][good, i] = refMag[selected][good]
272 fgcmRefCat[
'refMagErr'][good, i] = refMagErr[selected][good]
280 good, = np.where((np.nan_to_num(refCat[fluxField][selected]) > 0.0)
281 & (np.nan_to_num(refCat[fluxField+
'Err'][selected]) > 0.0))
282 refMag = (refCat[fluxField][selected][good] * units.nJy).to_value(units.ABmag)
283 refMagErr = abMagErrFromFluxErr(refCat[fluxField+
'Err'][selected][good],
284 refCat[fluxField][selected][good])
285 fgcmRefCat[
'refMag'][good, i] = refMag
286 fgcmRefCat[
'refMagErr'][good, i] = refMagErr
290 def _determine_flux_fields(self, center, filterList):
292 Determine the flux field names for a reference catalog.
294 Will set self._fluxFields, self._referenceFilter.
298 center: `lsst.geom.SpherePoint`
299 The center around which to load test sources.
301 list of `str` of camera filter names.
309 foundReferenceFilter =
False
310 for filterName
in filterList:
311 refFilterName = self.config.refFilterMap.get(filterName)
312 if refFilterName
is None:
316 results = self.
refObjLoaderrefObjLoader.loadSkyCircle(center,
317 0.05 * lsst.geom.degrees,
319 foundReferenceFilter =
True
327 if not foundReferenceFilter:
328 raise RuntimeError(
"Could not find any valid flux field(s) %s" %
329 (
", ".join(filterList)))
333 for filterName
in filterList:
336 refFilterName = self.config.refFilterMap.get(filterName)
338 if refFilterName
is not None:
340 fluxField = getRefFluxField(results.refCat.schema, filterName=refFilterName)
345 if fluxField
is None:
346 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)