24 __all__ = [
"getRefFluxField",
"getRefFluxKeys",
"LoadReferenceObjectsTask",
"LoadReferenceObjectsConfig"]
41 """Get the name of a flux field from a schema. 43 if filterName is specified: 44 return *filterName*_camFlux if present 45 else return *filterName*_flux if present (camera filter name matches reference filter name) 46 else throw RuntimeError 48 return camFlux, if present, 49 else throw RuntimeError 53 schema : `lsst.afw.table.Schema` 54 Reference catalog schema. 56 Name of camera filter. 66 If an appropriate field is not found. 68 if not isinstance(schema, afwTable.Schema):
69 raise RuntimeError(
"schema=%s is not a schema" % (schema,))
71 fluxFieldList = [filterName +
"_camFlux", filterName +
"_flux"]
73 fluxFieldList = [
"camFlux"]
74 for fluxField
in fluxFieldList:
75 if fluxField
in schema:
78 raise RuntimeError(
"Could not find flux field(s) %s" % (
", ".join(fluxFieldList)))
82 """Return keys for flux and flux error. 86 schema : `lsst.afw.table.Schema` 87 Reference catalog schema. 89 Name of camera filter. 93 keys : `tuple` of (`lsst.afw.table.Key`, `lsst.afw.table.Key`) 97 - flux error key, if present, else None 102 If flux field not found. 105 fluxErrField = fluxField +
"Err" 106 fluxKey = schema[fluxField].asKey()
108 fluxErrKey = schema[fluxErrField].asKey()
111 return (fluxKey, fluxErrKey)
115 pixelMargin = pexConfig.RangeField(
116 doc=
"Padding to add to 4 all edges of the bounding box (pixels)",
121 defaultFilter = pexConfig.Field(
122 doc=
"Default reference catalog filter to use if filter not specified in exposure; " 123 "if blank then filter must be specified in exposure",
127 filterMap = pexConfig.DictField(
128 doc=
"Mapping of camera filter name: reference catalog filter name; " 129 "each reference filter must exist",
134 requireProperMotion = pexConfig.Field(
135 doc=
"Require that the fields needed to correct proper motion " 136 "(epoch, pm_ra and pm_dec) are present?",
151 r"""!Abstract base class to load objects from reference catalogs 153 @anchor LoadReferenceObjectsTask_ 155 @section meas_algorithms_loadReferenceObjects_Contents Contents 157 - @ref meas_algorithms_loadReferenceObjects_Purpose 158 - @ref meas_algorithms_loadReferenceObjects_Initialize 159 - @ref meas_algorithms_loadReferenceObjects_IO 160 - @ref meas_algorithms_loadReferenceObjects_Schema 161 - @ref meas_algorithms_loadReferenceObjects_Config 163 @section meas_algorithms_loadReferenceObjects_Purpose Description 165 Abstract base class for tasks that load objects from a reference catalog 166 in a particular region of the sky. 168 Implementations must subclass this class, override the loadSkyCircle method, 169 and will typically override the value of ConfigClass with a task-specific config class. 171 @section meas_algorithms_loadReferenceObjects_Initialize Task initialisation 173 @copydoc \_\_init\_\_ 175 @section meas_algorithms_loadReferenceObjects_IO Invoking the Task 177 @copydoc loadPixelBox 179 @section meas_algorithms_loadReferenceObjects_Schema Schema of the reference object catalog 181 Reference object catalogs are instances of lsst.afw.table.SimpleCatalog with the following schema 182 (other fields may also be present). 183 The units use astropy quantity conventions, so a 2 suffix means squared. 184 See also makeMinimalSchema. 185 - coord: ICRS position of star on sky (an lsst.geom.SpherePoint) 186 - centroid: position of star on an exposure, if relevant (an lsst.afw.Point2D) 187 - hasCentroid: is centroid usable? (a Flag) 188 - *referenceFilterName*_flux: brightness in the specified reference catalog filter (Jy) 189 Note: the function lsst.afw.image.abMagFromFlux will convert flux in Jy to AB Magnitude. 190 - *referenceFilterName*_fluxErr (optional): brightness standard deviation (Jy); 191 omitted if no data is available; possibly nan if data is available for some objects but not others 192 - camFlux: brightness in default camera filter (Jy); omitted if defaultFilter not specified 193 - camFluxErr: brightness standard deviation for default camera filter; 194 omitted if defaultFilter not specified or standard deviation not available that filter 195 - *cameraFilterName*_camFlux: brightness in specified camera filter (Jy) 196 - *cameraFilterName*_camFluxErr (optional): brightness standard deviation 197 in specified camera filter (Jy); omitted if no data is available; 198 possibly nan if data is available for some objects but not others 199 - photometric (optional): is the object usable for photometric calibration? (a Flag) 200 - resolved (optional): is the object spatially resolved? (a Flag) 201 - variable (optional): does the object have variable brightness? (a Flag) 202 - coord_raErr: uncertainty in `coord` along the direction of right ascension (radian, an Angle) 203 = uncertainty in ra * cos(dec); nan if unknown 204 - coord_decErr: uncertainty in `coord` along the direction of declination (radian, an Angle); 207 The following are optional; fields should only be present if the 208 information is available for at least some objects. 209 Numeric values are `nan` if unknown: 210 - epoch: date of observation as TAI MJD (day) 212 - pm_ra: proper motion along the direction of right ascension (rad/year, an Angle) = dra/dt * cos(dec) 213 - pm_dec: proper motion along the direction of declination (rad/year, and Angle) 214 - pm_raErr: uncertainty in `pm_ra` (rad/year) 215 - pm_decErr: uncertainty in `pm_dec` (rad/year) 216 - pm_ra_dec_Cov: covariance between pm_ra and pm_dec (rad2/year2) 217 - pm_flag: set if proper motion, error or covariance is bad 219 - parallax: parallax (rad, an Angle) 220 - parallaxErr: uncertainty in `parallax` (rad) 221 - parallax_flag: set if parallax value or parallaxErr is bad 223 - coord_ra_pm_ra_Cov: covariance between coord_ra and pm_ra (rad2/year) 224 - coord_ra_pm_dec_Cov: covariance between coord_ra and pm_dec (rad2/year) 225 - coord_ra_parallax_Cov: covariance between coord_ra and parallax (rad2/year) 226 - coord_dec_pm_ra_Cov: covariance between coord_dec and pm_ra (rad2/year) 227 - coord_dec_pm_dec_Cov: covariance between coord_dec and pm_dec (rad2/year) 228 - coord_dec_parallax_Cov: covariance between coord_dec and parallax (rad2/year) 229 - pm_ra_parallax_Cov: covariance between pm_ra and parallax (rad2/year) 230 - pm_dec_parallax_Cov: covariance between pm_dec and parallax (rad2/year) 232 @section meas_algorithms_loadReferenceObjects_Config Configuration parameters 234 See @ref LoadReferenceObjectsConfig for a base set of configuration parameters. 235 Most subclasses will add configuration variables. 237 ConfigClass = LoadReferenceObjectsConfig
238 _DefaultName =
"LoadReferenceObjects" 241 """Construct a LoadReferenceObjectsTask 245 butler : `lsst.daf.persistence.Butler` 246 Data butler, for access reference catalogs. 248 pipeBase.Task.__init__(self, *args, **kwargs)
252 def loadPixelBox(self, bbox, wcs, filterName=None, calib=None, epoch=None):
253 """Load reference objects that overlap a rectangular pixel region. 257 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 258 Bounding box for pixels. 259 wcs : `lsst.afw.geom.SkyWcs` 260 WCS; used to convert pixel positions to sky coordinates 263 Name of filter, or `None` or `""` for the default filter. 264 This is used for flux values in case we have flux limits 265 (which are not yet implemented). 266 calib : `lsst.afw.image.Calib` (optional) 267 Calibration, or `None` if unknown. 268 epoch : `astropy.time.Time` (optional) 269 Epoch to which to correct proper motion and parallax, 270 or None to not apply such corrections. 274 results : `lsst.pipe.base.Struct` 275 A Struct containing the following fields: 276 refCat : `lsst.afw.catalog.SimpleCatalog` 277 A catalog of reference objects with the standard 278 schema, as documented in the main doc string for 279 `LoadReferenceObjects`. 280 The catalog is guaranteed to be contiguous. 282 Name of flux field for specified `filterName`. 286 The search algorithm works by searching in a region in sky 287 coordinates whose center is the center of the bbox and radius 288 is large enough to just include all 4 corners of the bbox. 289 Stars that lie outside the bbox are then trimmed from the list. 294 self.log.info(
"Loading reference objects using center %s and radius %s deg" %
295 (circle.coord, circle.radius.asDegrees()))
296 loadRes = self.
loadSkyCircle(circle.coord, circle.radius, filterName)
297 refCat = loadRes.refCat
298 numFound = len(refCat)
301 refCat = self.
_trimToBBox(refCat=refCat, bbox=circle.bbox, wcs=wcs)
302 numTrimmed = numFound - len(refCat)
303 self.log.debug(
"trimmed %d out-of-bbox objects, leaving %d", numTrimmed, len(refCat))
304 self.log.info(
"Loaded %d reference objects", len(refCat))
307 if not refCat.isContiguous():
308 loadRes.refCat = refCat.copy(deep=
True)
314 """Load reference objects that overlap a circular sky region. 318 ctrCoord : `lsst.geom.SpherePoint` 319 ICRS center of search region. 320 radius : `lsst.geom.Angle` 321 Radius of search region. 322 filterName : `str` (optional) 323 Name of filter, or `None` or `""` for the default filter. 324 This is used for flux values in case we have flux limits 325 (which are not yet implemented). 326 epoch : `astropy.time.Time` (optional) 327 Epoch to which to correct proper motion and parallax, 328 or None to not apply such corrections. 332 results : `lsst.pipe.base.Struct` 333 A Struct containing the following fields: 334 refCat : `lsst.afw.catalog.SimpleCatalog` 335 A catalog of reference objects with the standard 336 schema, as documented in the main doc string for 337 `LoadReferenceObjects`. 338 The catalog is guaranteed to be contiguous. 340 Name of flux field for specified `filterName`. 344 Note that subclasses are responsible for performing the proper motion 345 correction, since this is the lowest-level interface for retrieving 351 def _trimToBBox(refCat, bbox, wcs):
352 """Remove objects outside a given pixel bounding box and set 353 centroid and hasCentroid fields. 357 refCat : `lsst.afw.table.SimpleCatalog` 358 A catalog of objects. The schema must include fields 359 "coord", "centroid" and "hasCentroid". 360 The "coord" field is read. 361 The "centroid" and "hasCentroid" fields are set. 362 bbox : `lsst.geom.Box2D` 364 wcs : `lsst.afw.geom.SkyWcs` 365 WCS; used to convert sky coordinates to pixel positions. 367 @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set 369 afwTable.updateRefCentroids(wcs, refCat)
370 centroidKey = afwTable.Point2DKey(refCat.schema[
"centroid"])
371 retStarCat = type(refCat)(refCat.table)
373 point = star.get(centroidKey)
374 if bbox.contains(point):
375 retStarCat.append(star)
378 def _addFluxAliases(self, schema):
379 """Add aliases for camera filter fluxes to the schema. 381 If self.config.defaultFilter then adds these aliases: 382 camFlux: <defaultFilter>_flux 383 camFluxErr: <defaultFilter>_fluxErr, if the latter exists 385 For each camFilter: refFilter in self.config.filterMap adds these aliases: 386 <camFilter>_camFlux: <refFilter>_flux 387 <camFilter>_camFluxErr: <refFilter>_fluxErr, if the latter exists 391 schema : `lsst.afw.table.Schema` 392 Schema for reference catalog. 397 If any reference flux field is missing from the schema. 399 aliasMap = schema.getAliasMap()
401 def addAliasesForOneFilter(filterName, refFilterName):
402 """Add aliases for a single filter 406 filterName : `str` (optional) 407 Camera filter name. The resulting alias name is 408 <filterName>_camFlux, or simply "camFlux" if `filterName` 410 refFilterName : `str` 411 Reference catalog filter name; the field 412 <refFilterName>_flux must exist. 414 camFluxName = filterName +
"_camFlux" if filterName
is not None else "camFlux" 415 refFluxName = refFilterName +
"_flux" 416 if refFluxName
not in schema:
417 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
418 aliasMap.set(camFluxName, refFluxName)
419 refFluxErrName = refFluxName +
"Err" 420 if refFluxErrName
in schema:
421 camFluxErrName = camFluxName +
"Err" 422 aliasMap.set(camFluxErrName, refFluxErrName)
424 if self.config.defaultFilter:
425 addAliasesForOneFilter(
None, self.config.defaultFilter)
427 for filterName, refFilterName
in self.config.filterMap.items():
428 addAliasesForOneFilter(filterName, refFilterName)
432 addIsPhotometric=False, addIsResolved=False,
433 addIsVariable=False, coordErrDim=2,
434 addProperMotion=False, properMotionErrDim=2,
435 addParallax=False, addParallaxErr=True):
436 """Make a standard schema for reference object catalogs. 440 filterNameList : `list` of `str` 441 List of filter names. Used to create <filterName>_flux fields. 442 addIsPhotometric : `bool` 443 If True then add field "photometric". 444 addIsResolved : `bool` 445 If True then add field "resolved". 446 addIsVariable : `bool` 447 If True then add field "variable". 449 Number of coord error fields; must be one of 0, 2, 3: 451 - If 2 or 3: add fields "coord_raErr" and "coord_decErr". 452 - If 3: also add field "coord_radecErr". 453 addProperMotion : `bool` 454 If True add fields "epoch", "pm_ra", "pm_dec" and "pm_flag". 455 properMotionErrDim : `int` 456 Number of proper motion error fields; must be one of 0, 2, 3; 457 ignored if addProperMotion false: 458 - If 2 or 3: add fields "pm_raErr" and "pm_decErr". 459 - If 3: also add field "pm_radecErr". 461 If True add fields "epoch", "parallax", "parallaxErr" 463 addParallaxErr : `bool` 464 If True add field "parallaxErr"; ignored if addParallax false. 468 schema : `lsst.afw.table.Schema` 469 Schema for reference catalog, an 470 `lsst.afw.table.SimpleCatalog`. 474 Reference catalogs support additional covariances, such as 475 covariance between RA and proper motion in declination, 476 that are not supported by this method, but can be added after 479 schema = afwTable.SimpleTable.makeMinimalSchema()
481 afwTable.Point2DKey.addFields(
484 "centroid on an exposure, if relevant",
490 doc=
"is position known?",
492 for filterName
in filterNameList:
494 field=
"%s_flux" % (filterName,),
496 doc=
"flux in filter %s" % (filterName,),
499 for filterName
in filterNameList:
501 field=
"%s_fluxErr" % (filterName,),
503 doc=
"flux uncertainty in filter %s" % (filterName,),
510 doc=
"set if the object can be used for photometric calibration",
516 doc=
"set if the object is spatially resolved",
522 doc=
"set if the object has variable brightness",
524 if coordErrDim
not in (0, 2, 3):
525 raise ValueError(
"coordErrDim={}; must be (0, 2, 3)".format(coordErrDim))
527 afwTable.CovarianceMatrix2fKey.addFields(
531 units=[
"rad",
"rad"],
532 diagonalOnly=(coordErrDim == 2),
535 if addProperMotion
or addParallax:
539 doc=
"date of observation (TAI, MJD)",
547 doc=
"proper motion in the right ascension direction = dra/dt * cos(dec)",
553 doc=
"proper motion in the declination direction",
556 if properMotionErrDim
not in (0, 2, 3):
557 raise ValueError(
"properMotionErrDim={}; must be (0, 2, 3)".format(properMotionErrDim))
558 if properMotionErrDim > 0:
559 afwTable.CovarianceMatrix2fKey.addFields(
563 units=[
"rad/year",
"rad/year"],
564 diagonalOnly=(properMotionErrDim == 2),
569 doc=
"Set if proper motion or proper motion error is bad",
583 doc=
"uncertainty in parallax",
587 field=
"parallax_flag",
589 doc=
"Set if parallax or parallax error is bad",
593 def _calculateCircle(self, bbox, wcs):
594 """Compute on-sky center and radius of search region. 598 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 600 wcs : `lsst.afw.geom.SkyWcs` 601 WCS; used to convert pixel positions to sky coordinates. 605 results : `lsst.pipe.base.Struct` 608 - coord : `lsst.geom.SpherePoint` 609 ICRS center of the search region. 610 - radius : `lsst.geom.Angle` 611 Radius of the search region. 612 - bbox : `lsst.afw.geom.Box2D` 613 Bounding box used to compute the circle. 616 bbox.grow(self.config.pixelMargin)
617 coord = wcs.pixelToSky(bbox.getCenter())
618 radius = max(coord.separation(wcs.pixelToSky(pp))
for pp
in bbox.getCorners())
619 return pipeBase.Struct(coord=coord, radius=radius, bbox=bbox)
622 """Return metadata about the load. 624 This metadata is used for reloading the catalog (e.g., for 625 reconstituting a normalised match list. 629 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 631 wcs : `lsst.afw.geom.SkyWcs` 632 WCS; used to convert pixel positions to sky coordinates. 634 Name of camera filter, or `None` or `""` for the default 636 calib : `lsst.afw.image.Calib` (optional) 637 Calibration, or `None` if unknown. 638 epoch : `astropy.time.Time` (optional) 639 Epoch to which to correct proper motion and parallax, 640 or None to not apply such corrections. 644 metadata : lsst.daf.base.PropertyList 645 Metadata about the load. 651 """Return metadata about the load. 653 This metadata is used for reloading the catalog (e.g., for 654 reconstituting a normalised match list. 658 coord : `lsst.geom.SpherePoint` 659 ICRS center of the search region. 660 radius : `lsst.geom.Angle` 661 Radius of the search region. 663 Name of camera filter, or `None` or `""` for the default 665 calib : `lsst.afw.image.Calib` (optional) 666 Calibration, or `None` if unknown. 667 epoch : `astropy.time.Time` (optional) 668 Epoch to which to correct proper motion and parallax, 669 or None to not apply such corrections. 673 metadata : lsst.daf.base.PropertyList 674 Metadata about the load 677 md.add(
'RA', coord.getRa().asDegrees(),
'field center in degrees')
678 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
679 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
680 md.add(
'SMATCHV', 1,
'SourceMatchVector version number')
681 filterName =
"UNKNOWN" if filterName
is None else str(filterName)
682 md.add(
'FILTER', filterName,
'filter name for photometric data')
683 md.add(
'EPOCH',
"NONE" if epoch
is None else epoch,
'Epoch (TAI MJD) for catalog')
687 """Relink an unpersisted match list to sources and reference 690 A match list is persisted and unpersisted as a catalog of IDs 691 produced by afw.table.packMatches(), with match metadata 692 (as returned by the astrometry tasks) in the catalog's metadata 693 attribute. This method converts such a match catalog into a match 694 list, with links to source records and reference object records. 698 matchCat : `lsst.afw.table.BaseCatalog` 699 Unperisted packed match list. 700 ``matchCat.table.getMetadata()`` must contain match metadata, 701 as returned by the astrometry tasks. 702 sourceCat : `lsst.afw.table.SourceCatalog` 703 Source catalog. As a side effect, the catalog will be sorted 708 matchList : `lsst.afw.table.ReferenceMatchVector` 711 matchmeta = matchCat.table.getMetadata()
712 version = matchmeta.getInt(
'SMATCHV')
714 raise ValueError(
'SourceMatchVector version number is %i, not 1.' % version)
715 filterName = matchmeta.getString(
'FILTER').strip()
717 matchmeta.getDouble(
'DEC'), lsst.geom.degrees)
718 rad = matchmeta.getDouble(
'RADIUS') * lsst.geom.degrees
720 epoch = matchmeta.getDouble(
'EPOCH')
721 except (pexExcept.NotFoundError, pexExcept.TypeError):
723 refCat = self.
loadSkyCircle(ctrCoord, rad, filterName, epoch=epoch).refCat
726 return afwTable.unpackMatches(matchCat, refCat, sourceCat)
729 """Apply proper motion correction to a reference catalog. 731 Adjust position and position error in the ``catalog`` 732 for proper motion to the specified ``epoch``, 733 modifying the catalong in place. 737 catalog : `lsst.afw.table.SimpleCatalog` 738 Catalog of positions, containing: 740 - Coordinates, retrieved by the table's coordinate key. 741 - ``coord_raErr`` : Error in Right Ascension (rad). 742 - ``coord_decErr`` : Error in Declination (rad). 743 - ``pm_ra`` : Proper motion in Right Ascension (rad/yr, 745 - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional. 746 - ``pm_dec`` : Proper motion in Declination (rad/yr, 748 - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional. 749 - ``epoch`` : Mean epoch of object (an astropy.time.Time) 750 epoch : `astropy.time.Time` (optional) 751 Epoch to which to correct proper motion and parallax, 752 or None to not apply such corrections. 754 if (
"epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema):
755 if self.config.requireProperMotion:
756 raise RuntimeError(
"Proper motion correction required but not available from catalog")
757 self.log.warn(
"Proper motion correction not available from catalog")
759 if not catalog.isContiguous():
760 raise RuntimeError(
"Catalog must be contiguous")
761 catEpoch = astropy.time.Time(catalog[
"epoch"], scale=
"tai", format=
"mjd")
762 self.log.debug(
"Correcting reference catalog for proper motion to %r", epoch)
764 timeDiffsYears = (epoch.tai - catEpoch).to(astropy.units.yr).value
765 coordKey = catalog.table.getCoordKey()
768 pmRaRad = catalog[
"pm_ra"]
769 pmDecRad = catalog[
"pm_dec"]
770 offsetsRaRad = pmRaRad*timeDiffsYears
771 offsetsDecRad = pmDecRad*timeDiffsYears
779 offsetBearingsRad = numpy.arctan2(pmDecRad*1e6, pmRaRad*1e6)
780 offsetAmountsRad = numpy.hypot(offsetsRaRad, offsetsDecRad)
781 for record, bearingRad, amountRad
in zip(catalog, offsetBearingsRad, offsetAmountsRad):
783 record.get(coordKey).offset(bearing=bearingRad*lsst.geom.radians,
784 amount=amountRad*lsst.geom.radians))
786 if "coord_raErr" in catalog.schema:
787 catalog[
"coord_raErr"] = numpy.hypot(catalog[
"coord_raErr"],
788 catalog[
"pm_raErr"]*timeDiffsYears)
789 if "coord_decErr" in catalog.schema:
790 catalog[
"coord_decErr"] = numpy.hypot(catalog[
"coord_decErr"],
791 catalog[
"pm_decErr"]*timeDiffsYears)
def loadPixelBox(self, bbox, wcs, filterName=None, calib=None, epoch=None)
def joinMatchListWithCatalog(self, matchCat, sourceCat)
def __init__(self, butler=None, args, kwargs)
def applyProperMotions(self, catalog, epoch)
def _trimToBBox(refCat, bbox, wcs)
def getMetadataBox(self, bbox, wcs, filterName=None, calib=None, epoch=None)
def getRefFluxField(schema, filterName=None)
def getRefFluxKeys(schema, filterName=None)
Abstract base class to load objects from reference catalogs.
def makeMinimalSchema(filterNameList, addCentroid=True, addIsPhotometric=False, addIsResolved=False, addIsVariable=False, coordErrDim=2, addProperMotion=False, properMotionErrDim=2, addParallax=False, addParallaxErr=True)
def getMetadataCircle(self, coord, radius, filterName, calib=None, epoch=None)
def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None)
def _calculateCircle(self, bbox, wcs)