24 __all__ = [
"getRefFluxField",
"getRefFluxKeys",
"LoadReferenceObjectsTask",
"LoadReferenceObjectsConfig",
25 "ReferenceObjectLoader"]
42 from lsst
import sphgeom
47 """Return True if this name/units combination corresponds to an 48 "old-style" reference catalog flux field. 50 unitsCheck = units !=
'nJy' 51 isFlux = name.endswith(
'_flux')
52 isFluxSigma = name.endswith(
'_fluxSigma')
53 isFluxErr = name.endswith(
'_fluxErr')
54 return (isFlux
or isFluxSigma
or isFluxErr)
and unitsCheck
58 """Return True if the units of all flux and fluxErr are correct (nJy). 67 """"Return the format version stored in a reference catalog header. 71 refCat : `lsst.afw.table.SimpleCatalog` 72 Reference catalog to inspect. 76 version : `int` or `None` 77 Format version integer, or `None` if the catalog has no metadata 78 or the metadata does not include a "REFCAT_FORMAT_VERSION" key. 80 md = refCat.getMetadata()
84 return md.getScalar(
"REFCAT_FORMAT_VERSION")
90 """Convert fluxes in a catalog from jansky to nanojansky. 94 catalog : `lsst.afw.table.SimpleCatalog` 95 The catalog to convert. 97 Log to send messages to. 98 doConvert : `bool`, optional 99 Return a converted catalog, or just identify the fields that need to be converted? 100 This supports the "write=False" mode of `bin/convert_to_nJy.py`. 104 catalog : `lsst.afw.table.SimpleCatalog` or None 105 The converted catalog, or None if ``doConvert`` is False. 109 Support for old units in reference catalogs will be removed after the 110 release of late calendar year 2019. 111 Use `meas_algorithms/bin/convert_to_nJy.py` to update your reference catalog. 119 for field
in catalog.schema:
120 oldName = field.field.getName()
121 oldUnits = field.field.getUnits()
125 if oldName.endswith(
'_fluxSigma'):
126 name = oldName.replace(
'_fluxSigma',
'_fluxErr')
130 mapper.addMapping(field.getKey(), newField)
131 input_fields.append(field.field)
132 output_fields.append(newField)
134 mapper.addMapping(field.getKey())
136 fluxFieldsStr =
'; '.join(
"(%s, '%s')" % (field.getName(), field.getUnits())
for field
in input_fields)
139 newSchema = mapper.getOutputSchema()
141 output.extend(catalog, mapper=mapper)
142 for field
in output_fields:
143 output[field.getName()] *= 1e9
144 log.info(f
"Converted refcat flux fields to nJy (name, units): {fluxFieldsStr}")
147 log.info(f
"Found old-style refcat flux fields (name, units): {fluxFieldsStr}")
152 """This is a private helper class which filters catalogs by 153 row based on the row being inside the region used to initialize 158 region : `lsst.sphgeom.Region` 159 The spatial region which all objects should lie within 165 """This call method on an instance of this class takes in a reference 166 catalog, and the region from which the catalog was generated. 168 If the catalog region is entirely contained within the region used to 169 initialize this class, then all the entries in the catalog must be 170 within the region and so the whole catalog is returned. 172 If the catalog region is not entirely contained, then the location for 173 each record is tested against the region used to initialize the class. 174 Records which fall inside this region are added to a new catalog, and 175 this catalog is then returned. 179 refCat : `lsst.afw.table.SourceCatalog` 180 SourceCatalog to be filtered. 181 catRegion : `lsst.sphgeom.Region` 182 Region in which the catalog was created 184 if catRegion.isWithin(self.
region):
188 filteredRefCat =
type(refCat)(refCat.table)
189 for record
in refCat:
191 filteredRefCat.append(record)
192 return filteredRefCat
196 """ This class facilitates loading reference catalogs with gen 3 middleware 198 The middleware preflight solver will create a list of datarefs that may 199 possibly overlap a given region. These datarefs are then used to construct 200 and instance of this class. The class instance should then be passed into 201 a task which needs reference catalogs. These tasks should then determine 202 the exact region of the sky reference catalogs will be loaded for, and 203 call a corresponding method to load the reference objects. 205 def __init__(self, dataIds, butler, config, log=None):
206 """ Constructs an instance of ReferenceObjectLoader 210 dataIds : iterable of `lsst.daf.butler.DataIds` 211 An iterable object of DataSetRefs which point to reference catalogs 212 in a gen 3 repository 213 bulter : `lsst.daf.bulter.Butler` 214 A gen 3 butler instance 216 Logger object used to write out messages. If `None` (default) the default 217 lsst logger will be used 226 def _makeBoxRegion(BBox, wcs, BBoxPadding):
233 outerLocalBBox.grow(BBoxPadding)
234 innerLocalBBox.grow(-1*BBoxPadding)
242 innerBoxCorners = innerLocalBBox.getCorners()
243 innerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in innerBoxCorners]
248 outerBoxCorners = outerLocalBBox.getCorners()
249 outerSphCorners = [wcs.pixelToSky(corner).getVector()
for corner
in outerBoxCorners]
253 return innerSkyRegion, outerSkyRegion, innerSphCorners, outerSphCorners
255 def loadPixelBox(self, bbox, wcs, filterName=None, epoch=None, photoCalib=None, bboxPadding=100):
256 """Load reference objects that are within a pixel-based rectangular region 258 This algorithm works by creating a spherical box whose corners correspond 259 to the WCS converted corners of the input bounding box (possibly padded). 260 It then defines a filtering function which will look at a reference 261 objects pixel position and accept objects that lie within the specified 264 The spherical box region and filtering function are passed to the generic 265 loadRegion method which will load and filter the reference objects from 266 the datastore and return a single catalog containing all reference objects 270 bbox : `lsst.geom.box2I` 271 Box which bounds a region in pixel space 272 wcs : `lsst.afw.geom.SkyWcs` 273 Wcs object defining the pixel to sky (and inverse) transform for the space 274 of pixels of the supplied bbox 276 Name of camera filter, or None or blank for the default filter 277 epoch : `astropy.time.Time` (optional) 278 Epoch to which to correct proper motion and parallax, 279 or None to not apply such corrections. 281 Deprecated and ignored, only included for api compatibility 283 Number describing how much to pad the input bbox by (in pixels), defaults 284 to 100. This parameter is necessary because optical distortions in telescopes 285 can cause a rectangular pixel grid to map into a non "rectangular" spherical 286 region in sky coordinates. This padding is used to create a spherical 287 "rectangle", which will for sure enclose the input box. This padding is only 288 used to determine if the reference catalog for a sky patch will be loaded from 289 the data store, this function will filter out objects which lie within the 290 padded region but fall outside the input bounding box region. 294 referenceCatalog : `lsst.afw.table.SimpleCatalog` 295 Catalog containing reference objects inside the specified bounding box 299 `lsst.pex.exception.RuntimeError` 300 Raised if no reference catalogs could be found for the specified region 302 `lsst.pex.exception.TypeError` 303 Raised if the loaded reference catalogs do not have matching schemas 305 innerSkyRegion, outerSkyRegion, _, _ = self.
_makeBoxRegion(bbox, wcs, bboxPadding)
307 def _filterFunction(refCat, region):
314 if innerSkyRegion.contains(region):
317 filteredRefCat =
type(refCat)(refCat.table)
319 for record
in refCat:
320 pixCoords = record[centroidKey]
322 filteredRefCat.append(record)
323 return filteredRefCat
324 return self.
loadRegion(outerSkyRegion, filtFunc=_filterFunction, epoch=epoch, filterName=filterName)
326 def loadRegion(self, region, filtFunc=None, filterName=None, epoch=None):
327 """ Load reference objects within a specified region 329 This function loads the DataIds used to construct an instance of this class 330 which intersect or are contained within the specified region. The reference 331 catalogs which intersect but are not fully contained within the input region are 332 further filtered by the specified filter function. This function will return a 333 single source catalog containing all reference objects inside the specified region. 337 region : `lsst.sphgeom.Region` 338 This can be any type that is derived from `lsst.sphgeom.region` and should 339 define the spatial region for which reference objects are to be loaded. 341 This optional parameter should be a callable object that takes a reference 342 catalog and its corresponding region as parameters, filters the catalog by 343 some criteria and returns the filtered reference catalog. If the value is 344 left as the default (None) than an internal filter function is used which 345 filters according to if a reference object falls within the input region. 347 Name of camera filter, or None or blank for the default filter 348 epoch : `astropy.time.Time` (optional) 349 Epoch to which to correct proper motion and parallax, 350 or None to not apply such corrections. 354 referenceCatalog : `lsst.afw.table.SourceCatalog` 355 Catalog containing reference objects which intersect the input region, 356 filtered by the specified filter function 360 `lsst.pex.exception.RuntimeError` 361 Raised if no reference catalogs could be found for the specified region 363 `lsst.pex.exception.TypeError` 364 Raised if the loaded reference catalogs do not have matching schemas 367 regionBounding = region.getBoundingBox()
368 self.
log.
info(
"Loading reference objects from region bounded by {}, {} lat lon".
format(
369 regionBounding.getLat(), regionBounding.getLon()))
378 intersects = dataId.region.intersects(region)
380 intersects = region.intersects(dataId.region)
383 overlapList.append(dataId)
385 if len(overlapList) == 0:
388 firstCat = self.
butler.get(
'ref_cat', overlapList[0])
389 refCat = filtFunc(firstCat, overlapList[0].region)
390 trimmedAmount = len(firstCat) - len(refCat)
393 for dataId
in overlapList[1:]:
394 tmpCat = self.
butler.get(
'ref_cat', dataId)
396 if tmpCat.schema != firstCat.schema:
399 filteredCat = filtFunc(tmpCat, dataId.region)
400 refCat.extend(filteredCat)
401 trimmedAmount += len(tmpCat) - len(filteredCat)
403 self.
log.
debug(f
"Trimmed {trimmedAmount} out of region objects, leaving {len(refCat)}")
404 self.
log.
info(f
"Loaded {len(refCat)} reference objects")
406 if epoch
is not None and "pm_ra" in refCat.schema:
408 if isinstance(refCat.schema[
"pm_ra"].asKey(), lsst.afw.table.KeyAngle):
411 self.
log.
warn(
"Catalog pm_ra field is not an Angle; not applying proper motion")
416 self.
log.
warn(
"Found version 0 reference catalog with old style units in schema.")
417 self.
log.
warn(
"run `meas_algorithms/bin/convert_refcat_to_nJy.py` to convert fluxes to nJy.")
418 self.
log.
warn(
"See RFC-575 for more details.")
427 if not expandedCat.isContiguous():
428 expandedCat = refCat.copy(deep=
True)
430 fluxField =
getRefFluxField(schema=expandedCat.schema, filterName=filterName)
431 return pipeBase.Struct(refCat=expandedCat, fluxField=fluxField)
434 """Load reference objects that lie within a circular region on the sky 436 This method constructs a circular region from an input center and angular radius, 437 loads reference catalogs which are contained in or intersect the circle, and 438 filters reference catalogs which intersect down to objects which lie within 443 ctrCoord : `lsst.geom.SpherePoint` 444 Point defining the center of the circular region 445 radius : `lsst.geom.Angle` 446 Defines the angular radius of the circular region 448 Name of camera filter, or None or blank for the default filter 449 epoch : `astropy.time.Time` (optional) 450 Epoch to which to correct proper motion and parallax, 451 or None to not apply such corrections. 455 referenceCatalog : `lsst.afw.table.SourceCatalog` 456 Catalog containing reference objects inside the specified bounding box 460 `lsst.pex.exception.RuntimeError` 461 Raised if no reference catalogs could be found for the specified region 463 `lsst.pex.exception.TypeError` 464 Raised if the loaded reference catalogs do not have matching schemas 467 centerVector = ctrCoord.getVector()
470 return self.
loadRegion(circularRegion, filterName=filterName, epoch=
None)
473 """Relink an unpersisted match list to sources and reference 476 A match list is persisted and unpersisted as a catalog of IDs 477 produced by afw.table.packMatches(), with match metadata 478 (as returned by the astrometry tasks) in the catalog's metadata 479 attribute. This method converts such a match catalog into a match 480 list, with links to source records and reference object records. 484 matchCat : `lsst.afw.table.BaseCatalog` 485 Unpersisted packed match list. 486 ``matchCat.table.getMetadata()`` must contain match metadata, 487 as returned by the astrometry tasks. 488 sourceCat : `lsst.afw.table.SourceCatalog` 489 Source catalog. As a side effect, the catalog will be sorted 494 matchList : `lsst.afw.table.ReferenceMatchVector` 500 def getMetadataBox(cls, bbox, wcs, filterName=None, photoCalib=None, epoch=None, bboxPadding=100):
501 """Return metadata about the load 503 This metadata is used for reloading the catalog (e.g., for 504 reconstituting a normalised match list.) 508 bbox : `lsst.geom.Box2I` 509 Bounding bos for the pixels 510 wcs : `lsst.afw.geom.SkyWcs 512 filterName : `str` or None 513 filterName of the camera filter, or None or blank for the default filter 515 Deprecated, only included for api compatibility 516 epoch : `astropy.time.Time` (optional) 517 Epoch to which to correct proper motion and parallax, 518 or None to not apply such corrections. 520 Number describing how much to pad the input bbox by (in pixels), defaults 521 to 100. This parameter is necessary because optical distortions in telescopes 522 can cause a rectangular pixel grid to map into a non "rectangular" spherical 523 region in sky coordinates. This padding is used to create a spherical 524 "rectangle", which will for sure enclose the input box. This padding is only 525 used to determine if the reference catalog for a sky patch will be loaded from 526 the data store, this function will filter out objects which lie within the 527 padded region but fall outside the input bounding box region. 530 md : `lsst.daf.base.PropertyList` 532 _, _, innerCorners, outerCorners = cls.
_makeBoxRegion(bbox, wcs, bboxPadding)
534 for box, corners
in zip((
"INNER",
"OUTER"), (innerCorners, outerCorners)):
535 for (name, corner)
in zip((
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"),
537 md.add(f
"{box}_{name}_RA",
geom.SpherePoint(corner).getRa().asDegrees(), f
"{box}_corner")
538 md.add(f
"{box}_{name}_DEC",
geom.SpherePoint(corner).getDec().asDegrees(), f
"{box}_corner")
539 md.add(
"SMATCHV", 1,
'SourceMatchVector version number')
540 filterName =
"UNKNOWN" if filterName
is None else str(filterName)
541 md.add(
'FILTER', filterName,
'filter name for photometric data')
544 md.add(
'EPOCH',
"NONE" if epoch
is None else str(epoch),
'Epoch (TAI MJD) for catalog')
549 """Return metadata about the load 551 This metadata is used for reloading the catalog (e.g. for reconstituting 552 a normalized match list.) 556 coord : `lsst.geom.SpherePoint` 557 ICRS center of a circle 558 radius : `lsst.geom.angle` 560 filterName : `str` or None 561 filterName of the camera filter, or None or blank for the default filter 563 Deprecated, only included for api compatibility 564 epoch : `astropy.time.Time` (optional) 565 Epoch to which to correct proper motion and parallax, 566 or None to not apply such corrections. 570 md : `lsst.daf.base.PropertyList` 573 md.add(
'RA', coord.getRa().asDegrees(),
'field center in degrees')
574 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
575 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
576 md.add(
'SMATCHV', 1,
'SourceMatchVector version number')
577 filterName =
"UNKNOWN" if filterName
is None else str(filterName)
578 md.add(
'FILTER', filterName,
'filter name for photometric data')
579 md.add(
'EPOCH',
"NONE" if epoch
is None else epoch,
'Epoch (TAI MJD) for catalog')
584 """This function creates a new catalog containing the information of the input refCat 585 as well as added flux columns and aliases between camera and reference flux. 589 refCat : `lsst.afw.table.SimpleCatalog` 590 Catalog of reference objects 591 defaultFilter : `str` 592 Name of the default reference filter 593 filterReferenceMap : `dict` of `str` 594 Dictionary with keys corresponding to a filter name, and values which 595 correspond to the name of the reference filter. 599 refCat : `lsst.afw.table.SimpleCatalog` 600 Reference catalog with columns added to track reference filters 605 If specified reference filter name is not a filter specifed as a key in the 606 reference filter map. 608 refCat = ReferenceObjectLoader.remapReferenceCatalogSchema(refCat,
609 filterNameList=filterReferenceMap.keys())
610 aliasMap = refCat.schema.getAliasMap()
612 if filterReferenceMap
is None:
613 filterReferenceMap = {}
614 for filterName, refFilterName
in itertools.chain([(
None, defaultFilter)],
615 filterReferenceMap.items()):
617 camFluxName = filterName +
"_camFlux" if filterName
is not None else "camFlux" 618 refFluxName = refFilterName +
"_flux" 619 if refFluxName
not in refCat.schema:
620 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
621 aliasMap.set(camFluxName, refFluxName)
623 refFluxErrName = refFluxName +
"Err" 624 camFluxErrName = camFluxName +
"Err" 625 aliasMap.set(camFluxErrName, refFluxErrName)
631 """This function takes in a reference catalog and creates a new catalog with additional 632 columns defined the remaining function arguments. 636 refCat : `lsst.afw.table.SimpleCatalog` 637 Reference catalog to map to new catalog 641 expandedCat : `lsst.afw.table.SimpleCatalog` 642 Deep copy of input reference catalog with additional columns added 645 mapper.addMinimalSchema(refCat.schema,
True)
646 mapper.editOutputSchema().disconnectAliases()
648 for filterName
in filterNameList:
649 mapper.editOutputSchema().addField(f
"{filterName}_flux",
651 doc=f
"flux in filter {filterName}",
654 mapper.editOutputSchema().addField(f
"{filterName}_fluxErr",
656 doc=f
"flux uncertanty in filter {filterName}",
661 mapper.editOutputSchema().addField(
"centroid_x", type=float, doReplace=
True)
662 mapper.editOutputSchema().addField(
"centroid_y", type=float, doReplace=
True)
663 mapper.editOutputSchema().addField(
"hasCentroid", type=
"Flag", doReplace=
True)
664 mapper.editOutputSchema().getAliasMap().
set(
"slot_Centroid",
"centroid")
667 mapper.editOutputSchema().addField(
"photometric",
669 doc=
"set if the object can be used for photometric" +
672 mapper.editOutputSchema().addField(
"resolved",
674 doc=
"set if the object is spatially resolved" 676 mapper.editOutputSchema().addField(
"variable",
678 doc=
"set if the object has variable brightness" 682 expandedCat.setMetadata(refCat.getMetadata())
683 expandedCat.extend(refCat, mapper=mapper)
689 """Get the name of a flux field from a schema. 691 if filterName is specified: 692 return *filterName*_camFlux if present 693 else return *filterName*_flux if present (camera filter name matches reference filter name) 694 else throw RuntimeError 696 return camFlux, if present, 697 else throw RuntimeError 701 schema : `lsst.afw.table.Schema` 702 Reference catalog schema. 704 Name of camera filter. 708 fluxFieldName : `str` 714 If an appropriate field is not found. 717 raise RuntimeError(
"schema=%s is not a schema" % (schema,))
719 fluxFieldList = [filterName +
"_camFlux", filterName +
"_flux"]
721 fluxFieldList = [
"camFlux"]
722 for fluxField
in fluxFieldList:
723 if fluxField
in schema:
726 raise RuntimeError(
"Could not find flux field(s) %s" % (
", ".join(fluxFieldList)))
730 """Return keys for flux and flux error. 734 schema : `lsst.afw.table.Schema` 735 Reference catalog schema. 737 Name of camera filter. 741 keys : `tuple` of (`lsst.afw.table.Key`, `lsst.afw.table.Key`) 745 - flux error key, if present, else None 750 If flux field not found. 753 fluxErrField = fluxField +
"Err" 754 fluxKey = schema[fluxField].asKey()
756 fluxErrKey = schema[fluxErrField].asKey()
759 return (fluxKey, fluxErrKey)
763 pixelMargin = pexConfig.RangeField(
764 doc=
"Padding to add to 4 all edges of the bounding box (pixels)",
769 defaultFilter = pexConfig.Field(
770 doc=
"Default reference catalog filter to use if filter not specified in exposure; " 771 "if blank then filter must be specified in exposure",
775 filterMap = pexConfig.DictField(
776 doc=
"Mapping of camera filter name: reference catalog filter name; " 777 "each reference filter must exist",
782 requireProperMotion = pexConfig.Field(
783 doc=
"Require that the fields needed to correct proper motion " 784 "(epoch, pm_ra and pm_dec) are present?",
799 r"""!Abstract base class to load objects from reference catalogs 801 @anchor LoadReferenceObjectsTask_ 803 @section meas_algorithms_loadReferenceObjects_Contents Contents 805 - @ref meas_algorithms_loadReferenceObjects_Purpose 806 - @ref meas_algorithms_loadReferenceObjects_Initialize 807 - @ref meas_algorithms_loadReferenceObjects_IO 808 - @ref meas_algorithms_loadReferenceObjects_Schema 809 - @ref meas_algorithms_loadReferenceObjects_Config 811 @section meas_algorithms_loadReferenceObjects_Purpose Description 813 Abstract base class for tasks that load objects from a reference catalog 814 in a particular region of the sky. 816 Implementations must subclass this class, override the loadSkyCircle method, 817 and will typically override the value of ConfigClass with a task-specific config class. 819 @section meas_algorithms_loadReferenceObjects_Initialize Task initialisation 821 @copydoc \_\_init\_\_ 823 @section meas_algorithms_loadReferenceObjects_IO Invoking the Task 825 @copydoc loadPixelBox 827 @section meas_algorithms_loadReferenceObjects_Schema Schema of the reference object catalog 829 Reference object catalogs are instances of lsst.afw.table.SimpleCatalog with the following schema 830 (other fields may also be present). 831 The units use astropy quantity conventions, so a 2 suffix means squared. 832 See also makeMinimalSchema. 833 - coord: ICRS position of star on sky (an lsst.geom.SpherePoint) 834 - centroid: position of star on an exposure, if relevant (an lsst.afw.Point2D) 835 - hasCentroid: is centroid usable? (a Flag) 836 - *referenceFilterName*_flux: brightness in the specified reference catalog filter (nJy) 837 Note: you can use astropy.units to convert from AB Magnitude to nJy: 838 `u.Magnitude(value, u.ABmag).to_value(u.nJy)` 839 - *referenceFilterName*_fluxErr (optional): brightness standard deviation (nJy); 840 omitted if no data is available; possibly nan if data is available for some objects but not others 841 - camFlux: brightness in default camera filter (nJy); omitted if defaultFilter not specified 842 - camFluxErr: brightness standard deviation for default camera filter; 843 omitted if defaultFilter not specified or standard deviation not available that filter 844 - *cameraFilterName*_camFlux: brightness in specified camera filter (nJy) 845 - *cameraFilterName*_camFluxErr (optional): brightness standard deviation 846 in specified camera filter (nJy); omitted if no data is available; 847 possibly nan if data is available for some objects but not others 848 - photometric (optional): is the object usable for photometric calibration? (a Flag) 849 - resolved (optional): is the object spatially resolved? (a Flag) 850 - variable (optional): does the object have variable brightness? (a Flag) 851 - coord_raErr: uncertainty in `coord` along the direction of right ascension (radian, an Angle) 852 = uncertainty in ra * cos(dec); nan if unknown 853 - coord_decErr: uncertainty in `coord` along the direction of declination (radian, an Angle); 856 The following are optional; fields should only be present if the 857 information is available for at least some objects. 858 Numeric values are `nan` if unknown: 859 - epoch: date of observation as TAI MJD (day) 861 - pm_ra: proper motion along the direction of right ascension (rad/year, an Angle) = dra/dt * cos(dec) 862 - pm_dec: proper motion along the direction of declination (rad/year, and Angle) 863 - pm_raErr: uncertainty in `pm_ra` (rad/year) 864 - pm_decErr: uncertainty in `pm_dec` (rad/year) 865 - pm_ra_dec_Cov: covariance between pm_ra and pm_dec (rad2/year2) 866 - pm_flag: set if proper motion, error or covariance is bad 868 - parallax: parallax (rad, an Angle) 869 - parallaxErr: uncertainty in `parallax` (rad) 870 - parallax_flag: set if parallax value or parallaxErr is bad 872 - coord_ra_pm_ra_Cov: covariance between coord_ra and pm_ra (rad2/year) 873 - coord_ra_pm_dec_Cov: covariance between coord_ra and pm_dec (rad2/year) 874 - coord_ra_parallax_Cov: covariance between coord_ra and parallax (rad2/year) 875 - coord_dec_pm_ra_Cov: covariance between coord_dec and pm_ra (rad2/year) 876 - coord_dec_pm_dec_Cov: covariance between coord_dec and pm_dec (rad2/year) 877 - coord_dec_parallax_Cov: covariance between coord_dec and parallax (rad2/year) 878 - pm_ra_parallax_Cov: covariance between pm_ra and parallax (rad2/year) 879 - pm_dec_parallax_Cov: covariance between pm_dec and parallax (rad2/year) 881 @section meas_algorithms_loadReferenceObjects_Config Configuration parameters 883 See @ref LoadReferenceObjectsConfig for a base set of configuration parameters. 884 Most subclasses will add configuration variables. 886 ConfigClass = LoadReferenceObjectsConfig
887 _DefaultName =
"LoadReferenceObjects" 890 """Construct a LoadReferenceObjectsTask 894 butler : `lsst.daf.persistence.Butler` 895 Data butler, for access reference catalogs. 897 pipeBase.Task.__init__(self, *args, **kwargs)
901 def loadPixelBox(self, bbox, wcs, filterName=None, photoCalib=None, epoch=None):
902 """Load reference objects that overlap a rectangular pixel region. 906 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 907 Bounding box for pixels. 908 wcs : `lsst.afw.geom.SkyWcs` 909 WCS; used to convert pixel positions to sky coordinates 912 Name of filter, or `None` or `""` for the default filter. 913 This is used for flux values in case we have flux limits 914 (which are not yet implemented). 915 photoCalib : `lsst.afw.image.PhotoCalib` (optional) 916 Calibration, or `None` if unknown. 917 epoch : `astropy.time.Time` (optional) 918 Epoch to which to correct proper motion and parallax, 919 or None to not apply such corrections. 923 results : `lsst.pipe.base.Struct` 924 A Struct containing the following fields: 925 refCat : `lsst.afw.catalog.SimpleCatalog` 926 A catalog of reference objects with the standard 927 schema, as documented in the main doc string for 928 `LoadReferenceObjects`. 929 The catalog is guaranteed to be contiguous. 931 Name of flux field for specified `filterName`. 935 The search algorithm works by searching in a region in sky 936 coordinates whose center is the center of the bbox and radius 937 is large enough to just include all 4 corners of the bbox. 938 Stars that lie outside the bbox are then trimmed from the list. 943 self.log.
info(
"Loading reference objects using center %s and radius %s deg" %
944 (circle.coord, circle.radius.asDegrees()))
945 loadRes = self.
loadSkyCircle(circle.coord, circle.radius, filterName)
946 refCat = loadRes.refCat
947 numFound = len(refCat)
950 refCat = self.
_trimToBBox(refCat=refCat, bbox=circle.bbox, wcs=wcs)
951 numTrimmed = numFound - len(refCat)
952 self.log.
debug(
"trimmed %d out-of-bbox objects, leaving %d", numTrimmed, len(refCat))
953 self.log.
info(
"Loaded %d reference objects", len(refCat))
956 if not refCat.isContiguous():
957 loadRes.refCat = refCat.copy(deep=
True)
963 """Load reference objects that overlap a circular sky region. 967 ctrCoord : `lsst.geom.SpherePoint` 968 ICRS center of search region. 969 radius : `lsst.geom.Angle` 970 Radius of search region. 971 filterName : `str` (optional) 972 Name of filter, or `None` or `""` for the default filter. 973 This is used for flux values in case we have flux limits 974 (which are not yet implemented). 975 epoch : `astropy.time.Time` (optional) 976 Epoch to which to correct proper motion and parallax, 977 or None to not apply such corrections. 981 results : `lsst.pipe.base.Struct` 982 A Struct containing the following fields: 983 refCat : `lsst.afw.catalog.SimpleCatalog` 984 A catalog of reference objects with the standard 985 schema, as documented in the main doc string for 986 `LoadReferenceObjects`. 987 The catalog is guaranteed to be contiguous. 989 Name of flux field for specified `filterName`. 993 Note that subclasses are responsible for performing the proper motion 994 correction, since this is the lowest-level interface for retrieving 1000 def _trimToBBox(refCat, bbox, wcs):
1001 """Remove objects outside a given pixel bounding box and set 1002 centroid and hasCentroid fields. 1006 refCat : `lsst.afw.table.SimpleCatalog` 1007 A catalog of objects. The schema must include fields 1008 "coord", "centroid" and "hasCentroid". 1009 The "coord" field is read. 1010 The "centroid" and "hasCentroid" fields are set. 1011 bbox : `lsst.geom.Box2D` 1013 wcs : `lsst.afw.geom.SkyWcs` 1014 WCS; used to convert sky coordinates to pixel positions. 1016 @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set 1020 retStarCat =
type(refCat)(refCat.table)
1022 point = star.get(centroidKey)
1023 if bbox.contains(point):
1024 retStarCat.append(star)
1027 def _addFluxAliases(self, schema):
1028 """Add aliases for camera filter fluxes to the schema. 1030 If self.config.defaultFilter then adds these aliases: 1031 camFlux: <defaultFilter>_flux 1032 camFluxErr: <defaultFilter>_fluxErr, if the latter exists 1034 For each camFilter: refFilter in self.config.filterMap adds these aliases: 1035 <camFilter>_camFlux: <refFilter>_flux 1036 <camFilter>_camFluxErr: <refFilter>_fluxErr, if the latter exists 1040 schema : `lsst.afw.table.Schema` 1041 Schema for reference catalog. 1046 If any reference flux field is missing from the schema. 1048 aliasMap = schema.getAliasMap()
1050 def addAliasesForOneFilter(filterName, refFilterName):
1051 """Add aliases for a single filter 1055 filterName : `str` (optional) 1056 Camera filter name. The resulting alias name is 1057 <filterName>_camFlux, or simply "camFlux" if `filterName` 1059 refFilterName : `str` 1060 Reference catalog filter name; the field 1061 <refFilterName>_flux must exist. 1063 camFluxName = filterName +
"_camFlux" if filterName
is not None else "camFlux" 1064 refFluxName = refFilterName +
"_flux" 1065 if refFluxName
not in schema:
1066 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
1067 aliasMap.set(camFluxName, refFluxName)
1068 refFluxErrName = refFluxName +
"Err" 1069 if refFluxErrName
in schema:
1070 camFluxErrName = camFluxName +
"Err" 1071 aliasMap.set(camFluxErrName, refFluxErrName)
1073 if self.config.defaultFilter:
1074 addAliasesForOneFilter(
None, self.config.defaultFilter)
1076 for filterName, refFilterName
in self.config.filterMap.items():
1077 addAliasesForOneFilter(filterName, refFilterName)
1081 addIsPhotometric=False, addIsResolved=False,
1082 addIsVariable=False, coordErrDim=2,
1083 addProperMotion=False, properMotionErrDim=2,
1084 addParallax=False, addParallaxErr=True):
1085 """Make a standard schema for reference object catalogs. 1089 filterNameList : `list` of `str` 1090 List of filter names. Used to create <filterName>_flux fields. 1091 addIsPhotometric : `bool` 1092 If True then add field "photometric". 1093 addIsResolved : `bool` 1094 If True then add field "resolved". 1095 addIsVariable : `bool` 1096 If True then add field "variable". 1098 Number of coord error fields; must be one of 0, 2, 3: 1100 - If 2 or 3: add fields "coord_raErr" and "coord_decErr". 1101 - If 3: also add field "coord_radecErr". 1102 addProperMotion : `bool` 1103 If True add fields "epoch", "pm_ra", "pm_dec" and "pm_flag". 1104 properMotionErrDim : `int` 1105 Number of proper motion error fields; must be one of 0, 2, 3; 1106 ignored if addProperMotion false: 1107 - If 2 or 3: add fields "pm_raErr" and "pm_decErr". 1108 - If 3: also add field "pm_radecErr". 1109 addParallax : `bool` 1110 If True add fields "epoch", "parallax", "parallaxErr" 1111 and "parallax_flag". 1112 addParallaxErr : `bool` 1113 If True add field "parallaxErr"; ignored if addParallax false. 1117 schema : `lsst.afw.table.Schema` 1118 Schema for reference catalog, an 1119 `lsst.afw.table.SimpleCatalog`. 1123 Reference catalogs support additional covariances, such as 1124 covariance between RA and proper motion in declination, 1125 that are not supported by this method, but can be added after 1126 calling this method. 1128 schema = afwTable.SimpleTable.makeMinimalSchema()
1130 afwTable.Point2DKey.addFields(
1133 "centroid on an exposure, if relevant",
1137 field=
"hasCentroid",
1139 doc=
"is position known?",
1141 for filterName
in filterNameList:
1143 field=
"%s_flux" % (filterName,),
1145 doc=
"flux in filter %s" % (filterName,),
1148 for filterName
in filterNameList:
1150 field=
"%s_fluxErr" % (filterName,),
1152 doc=
"flux uncertainty in filter %s" % (filterName,),
1155 if addIsPhotometric:
1157 field=
"photometric",
1159 doc=
"set if the object can be used for photometric calibration",
1165 doc=
"set if the object is spatially resolved",
1171 doc=
"set if the object has variable brightness",
1173 if coordErrDim
not in (0, 2, 3):
1174 raise ValueError(
"coordErrDim={}; must be (0, 2, 3)".
format(coordErrDim))
1176 afwTable.CovarianceMatrix2fKey.addFields(
1179 names=[
"ra",
"dec"],
1180 units=[
"rad",
"rad"],
1181 diagonalOnly=(coordErrDim == 2),
1184 if addProperMotion
or addParallax:
1188 doc=
"date of observation (TAI, MJD)",
1196 doc=
"proper motion in the right ascension direction = dra/dt * cos(dec)",
1202 doc=
"proper motion in the declination direction",
1205 if properMotionErrDim
not in (0, 2, 3):
1206 raise ValueError(
"properMotionErrDim={}; must be (0, 2, 3)".
format(properMotionErrDim))
1207 if properMotionErrDim > 0:
1208 afwTable.CovarianceMatrix2fKey.addFields(
1211 names=[
"ra",
"dec"],
1212 units=[
"rad/year",
"rad/year"],
1213 diagonalOnly=(properMotionErrDim == 2),
1218 doc=
"Set if proper motion or proper motion error is bad",
1230 field=
"parallaxErr",
1232 doc=
"uncertainty in parallax",
1236 field=
"parallax_flag",
1238 doc=
"Set if parallax or parallax error is bad",
1242 def _calculateCircle(self, bbox, wcs):
1243 """Compute on-sky center and radius of search region. 1247 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 1249 wcs : `lsst.afw.geom.SkyWcs` 1250 WCS; used to convert pixel positions to sky coordinates. 1254 results : `lsst.pipe.base.Struct` 1255 A Struct containing: 1257 - coord : `lsst.geom.SpherePoint` 1258 ICRS center of the search region. 1259 - radius : `lsst.geom.Angle` 1260 Radius of the search region. 1261 - bbox : `lsst.geom.Box2D` 1262 Bounding box used to compute the circle. 1265 bbox.grow(self.config.pixelMargin)
1266 coord = wcs.pixelToSky(bbox.getCenter())
1267 radius =
max(coord.separation(wcs.pixelToSky(pp))
for pp
in bbox.getCorners())
1268 return pipeBase.Struct(coord=coord, radius=radius, bbox=bbox)
1271 """Return metadata about the load. 1273 This metadata is used for reloading the catalog (e.g., for 1274 reconstituting a normalised match list. 1278 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 1280 wcs : `lsst.afw.geom.SkyWcs` 1281 WCS; used to convert pixel positions to sky coordinates. 1283 Name of camera filter, or `None` or `""` for the default 1285 photoCalib : `lsst.afw.image.PhotoCalib` (optional) 1286 Calibration, or `None` if unknown. 1287 epoch : `astropy.time.Time` (optional) 1288 Epoch to which to correct proper motion and parallax, 1289 or None to not apply such corrections. 1293 metadata : lsst.daf.base.PropertyList 1294 Metadata about the load. 1297 return self.
getMetadataCircle(circle.coord, circle.radius, filterName, photoCalib)
1300 """Return metadata about the load. 1302 This metadata is used for reloading the catalog (e.g., for 1303 reconstituting a normalised match list. 1307 coord : `lsst.geom.SpherePoint` 1308 ICRS center of the search region. 1309 radius : `lsst.geom.Angle` 1310 Radius of the search region. 1312 Name of camera filter, or `None` or `""` for the default 1314 photoCalib : `lsst.afw.image.PhotoCalib` (optional) 1315 Calibration, or `None` if unknown. 1316 epoch : `astropy.time.Time` (optional) 1317 Epoch to which to correct proper motion and parallax, 1318 or None to not apply such corrections. 1322 metadata : lsst.daf.base.PropertyList 1323 Metadata about the load 1326 md.add(
'RA', coord.getRa().asDegrees(),
'field center in degrees')
1327 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
1328 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
1329 md.add(
'SMATCHV', 1,
'SourceMatchVector version number')
1330 filterName =
"UNKNOWN" if filterName
is None else str(filterName)
1331 md.add(
'FILTER', filterName,
'filter name for photometric data')
1332 md.add(
'EPOCH',
"NONE" if epoch
is None else epoch,
'Epoch (TAI MJD) for catalog')
1336 """Relink an unpersisted match list to sources and reference 1339 A match list is persisted and unpersisted as a catalog of IDs 1340 produced by afw.table.packMatches(), with match metadata 1341 (as returned by the astrometry tasks) in the catalog's metadata 1342 attribute. This method converts such a match catalog into a match 1343 list, with links to source records and reference object records. 1347 matchCat : `lsst.afw.table.BaseCatalog` 1348 Unperisted packed match list. 1349 ``matchCat.table.getMetadata()`` must contain match metadata, 1350 as returned by the astrometry tasks. 1351 sourceCat : `lsst.afw.table.SourceCatalog` 1352 Source catalog. As a side effect, the catalog will be sorted 1357 matchList : `lsst.afw.table.ReferenceMatchVector` 1363 """Apply proper motion correction to a reference catalog. 1365 Adjust position and position error in the ``catalog`` 1366 for proper motion to the specified ``epoch``, 1367 modifying the catalong in place. 1371 catalog : `lsst.afw.table.SimpleCatalog` 1372 Catalog of positions, containing: 1374 - Coordinates, retrieved by the table's coordinate key. 1375 - ``coord_raErr`` : Error in Right Ascension (rad). 1376 - ``coord_decErr`` : Error in Declination (rad). 1377 - ``pm_ra`` : Proper motion in Right Ascension (rad/yr, 1379 - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional. 1380 - ``pm_dec`` : Proper motion in Declination (rad/yr, 1382 - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional. 1383 - ``epoch`` : Mean epoch of object (an astropy.time.Time) 1384 epoch : `astropy.time.Time` (optional) 1385 Epoch to which to correct proper motion and parallax, 1386 or None to not apply such corrections. 1388 if (
"epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema):
1389 if self.config.requireProperMotion:
1390 raise RuntimeError(
"Proper motion correction required but not available from catalog")
1391 self.log.
warn(
"Proper motion correction not available from catalog")
1397 """Relink an unpersisted match list to sources and reference 1400 A match list is persisted and unpersisted as a catalog of IDs 1401 produced by afw.table.packMatches(), with match metadata 1402 (as returned by the astrometry tasks) in the catalog's metadata 1403 attribute. This method converts such a match catalog into a match 1404 list, with links to source records and reference object records. 1409 Reference object loader to use in getting reference objects 1410 matchCat : `lsst.afw.table.BaseCatalog` 1411 Unperisted packed match list. 1412 ``matchCat.table.getMetadata()`` must contain match metadata, 1413 as returned by the astrometry tasks. 1414 sourceCat : `lsst.afw.table.SourceCatalog` 1415 Source catalog. As a side effect, the catalog will be sorted 1420 matchList : `lsst.afw.table.ReferenceMatchVector` 1423 matchmeta = matchCat.table.getMetadata()
1424 version = matchmeta.getInt(
'SMATCHV')
1426 raise ValueError(
'SourceMatchVector version number is %i, not 1.' % version)
1427 filterName = matchmeta.getString(
'FILTER').
strip()
1429 epoch = matchmeta.getDouble(
'EPOCH')
1432 if 'RADIUS' in matchmeta:
1435 matchmeta.getDouble(
'DEC'), lsst.geom.degrees)
1436 rad = matchmeta.getDouble(
'RADIUS') * lsst.geom.degrees
1437 refCat = refObjLoader.loadSkyCircle(ctrCoord, rad, filterName, epoch=epoch).refCat
1438 elif "INNER_UPPER_LEFT_RA" in matchmeta:
1444 for place
in (
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"):
1446 matchmeta.getDouble(f
"OUTER_{place}_DEC"),
1447 lsst.geom.degrees).getVector()
1450 refCat = refObjLoader.loadRegion(outerBox, filterName=filterName, epoch=epoch).refCat
1458 """Apply proper motion correction to a reference catalog. 1460 Adjust position and position error in the ``catalog`` 1461 for proper motion to the specified ``epoch``, 1462 modifying the catalong in place. 1466 log : `lsst.log.log` 1467 log object to write to 1468 catalog : `lsst.afw.table.SimpleCatalog` 1469 Catalog of positions, containing: 1471 - Coordinates, retrieved by the table's coordinate key. 1472 - ``coord_raErr`` : Error in Right Ascension (rad). 1473 - ``coord_decErr`` : Error in Declination (rad). 1474 - ``pm_ra`` : Proper motion in Right Ascension (rad/yr, 1476 - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional. 1477 - ``pm_dec`` : Proper motion in Declination (rad/yr, 1479 - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional. 1480 - ``epoch`` : Mean epoch of object (an astropy.time.Time) 1481 epoch : `astropy.time.Time` (optional) 1482 Epoch to which to correct proper motion and parallax, 1483 or None to not apply such corrections. 1485 if "epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema:
1486 log.warn(
"Proper motion correction not available from catalog")
1488 if not catalog.isContiguous():
1489 raise RuntimeError(
"Catalog must be contiguous")
1490 catEpoch = astropy.time.Time(catalog[
"epoch"], scale=
"tai", format=
"mjd")
1491 log.debug(
"Correcting reference catalog for proper motion to %r", epoch)
1493 timeDiffsYears = (epoch.tai - catEpoch).
to(astropy.units.yr).value
1494 coordKey = catalog.table.getCoordKey()
1497 pmRaRad = catalog[
"pm_ra"]
1498 pmDecRad = catalog[
"pm_dec"]
1499 offsetsRaRad = pmRaRad*timeDiffsYears
1500 offsetsDecRad = pmDecRad*timeDiffsYears
1508 offsetBearingsRad = numpy.arctan2(pmDecRad*1e6, pmRaRad*1e6)
1509 offsetAmountsRad = numpy.hypot(offsetsRaRad, offsetsDecRad)
1510 for record, bearingRad, amountRad
in zip(catalog, offsetBearingsRad, offsetAmountsRad):
1511 record.set(coordKey,
1512 record.get(coordKey).offset(bearing=bearingRad*lsst.geom.radians,
1513 amount=amountRad*lsst.geom.radians))
1515 if "coord_raErr" in catalog.schema:
1516 catalog[
"coord_raErr"] = numpy.hypot(catalog[
"coord_raErr"],
1517 catalog[
"pm_raErr"]*timeDiffsYears)
1518 if "coord_decErr" in catalog.schema:
1519 catalog[
"coord_decErr"] = numpy.hypot(catalog[
"coord_decErr"],
1520 catalog[
"pm_decErr"]*timeDiffsYears)
def getMetadataCircle(self, coord, radius, filterName, photoCalib=None, epoch=None)
static Log getDefaultLogger()
Return default logger instance, same as default constructor.
Defines the fields and offsets for a table.
def joinMatchListWithCatalog(self, matchCat, sourceCat)
def __call__(self, refCat, catRegion)
def loadPixelBox(self, bbox, wcs, filterName=None, epoch=None, photoCalib=None, bboxPadding=100)
bool contains(VertexIterator const begin, VertexIterator const end, UnitVector3d const &v)
def addFluxAliases(refCat, defaultFilter, filterReferenceMap)
static Schema makeMinimalSchema()
Return a minimal schema for Simple tables and records.
A floating-point coordinate rectangle geometry.
def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None)
Class for storing ordered metadata with comments.
def hasNanojanskyFluxUnits(schema)
A mapping between the keys of two Schemas, used to copy data between them.
def getFormatVersionFromRefCat(refCat)
def getMetadataBox(self, bbox, wcs, filterName=None, photoCalib=None, epoch=None)
def __init__(self, butler=None, args, kwargs)
def applyProperMotions(self, catalog, epoch)
def _trimToBBox(refCat, bbox, wcs)
daf::base::PropertySet * set
def getMetadataCircle(coord, radius, filterName, photoCalib=None, epoch=None)
Box represents a rectangle in spherical coordinate space that contains its boundary.
void updateRefCentroids(geom::SkyWcs const &wcs, ReferenceCollection &refList)
Update centroids in a collection of reference objects.
def convertToNanojansky(catalog, log, doConvert=True)
def loadPixelBox(self, bbox, wcs, filterName=None, photoCalib=None, epoch=None)
Reports attempts to access elements using an invalid key.
def __init__(self, region)
def joinMatchListWithCatalog(self, matchCat, sourceCat)
def getRefFluxField(schema, filterName=None)
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
def loadRegion(self, region, filtFunc=None, filterName=None, epoch=None)
def getRefFluxKeys(schema, filterName=None)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
A description of a field in a table.
Abstract base class to load objects from reference catalogs.
def applyProperMotionsImpl(log, catalog, epoch)
def isOldFluxField(name, units)
def makeMinimalSchema(filterNameList, addCentroid=True, addIsPhotometric=False, addIsResolved=False, addIsVariable=False, coordErrDim=2, addProperMotion=False, properMotionErrDim=2, addParallax=False, addParallaxErr=True)
Circle is a circular region on the unit sphere that contains its boundary.
ConvexPolygon is a closed convex polygon on the unit sphere.
def _makeBoxRegion(BBox, wcs, BBoxPadding)
def joinMatchListWithCatalogImpl(refObjLoader, matchCat, sourceCat)
Angle represents an angle in radians.
Point in an unspecified spherical coordinate system.
def __init__(self, dataIds, butler, config, log=None)
def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None)
template SourceMatchVector unpackMatches(BaseCatalog const &, SourceCatalog const &, SourceCatalog const &)
def getMetadataBox(cls, bbox, wcs, filterName=None, photoCalib=None, epoch=None, bboxPadding=100)
Reports errors from accepting an object of an unexpected or inappropriate type.
def _calculateCircle(self, bbox, wcs)
def remapReferenceCatalogSchema(refCat, filterNameList=None, position=False, photometric=False)
Reports errors that are due to events beyond the control of the program.