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 filterMap = pexConfig.DictField(
56 doc=
"Mapping from physicalFilter label 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 filterMap'
80 raise pexConfig.FieldValidationError(FgcmLoadReferenceCatalogConfig.filterMap, 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(filterName, refCatName, doRaise=
True)
260 refMag, refMagErr = colorterm.getCorrectedMagnitudes(refCat)
266 good, = np.where((np.nan_to_num(refMag[selected]) < 90.0)
267 & (np.nan_to_num(refMagErr[selected]) < 90.0)
268 & (np.nan_to_num(refMagErr[selected]) > 0.0))
270 fgcmRefCat[
'refMag'][good, i] = refMag[selected][good]
271 fgcmRefCat[
'refMagErr'][good, i] = refMagErr[selected][good]
279 good, = np.where((np.nan_to_num(refCat[fluxField][selected]) > 0.0)
280 & (np.nan_to_num(refCat[fluxField+
'Err'][selected]) > 0.0))
281 refMag = (refCat[fluxField][selected][good] * units.nJy).to_value(units.ABmag)
282 refMagErr = abMagErrFromFluxErr(refCat[fluxField+
'Err'][selected][good],
283 refCat[fluxField][selected][good])
284 fgcmRefCat[
'refMag'][good, i] = refMag
285 fgcmRefCat[
'refMagErr'][good, i] = refMagErr
289 def _determine_flux_fields(self, center, filterList):
291 Determine the flux field names for a reference catalog.
293 Will set self._fluxFields, self._referenceFilter.
297 center: `lsst.geom.SpherePoint`
298 The center around which to load test sources.
300 list of `str` of camera filter names.
308 foundReferenceFilter =
False
309 for filterName
in filterList:
310 refFilterName = self.config.filterMap.get(filterName)
311 if refFilterName
is None:
315 results = self.
refObjLoaderrefObjLoader.loadSkyCircle(center,
316 0.05 * lsst.geom.degrees,
318 foundReferenceFilter =
True
326 if not foundReferenceFilter:
327 raise RuntimeError(
"Could not find any valid flux field(s) %s" %
328 (
", ".join(filterList)))
332 for filterName
in filterList:
335 refFilterName = self.config.filterMap.get(filterName)
337 if refFilterName
is not None:
339 fluxField = getRefFluxField(results.refCat.schema, filterName=refFilterName)
344 if fluxField
is None:
345 self.log.warning(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)