24 __all__ = [
"getRefFluxField",
"getRefFluxKeys",
"LoadReferenceObjectsTask",
"LoadReferenceObjectsConfig",
25 "ReferenceObjectLoader"]
36 import lsst.pex.config
as pexConfig
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, refCats, 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 refCats : Iterable of `lsst.daf.butler.DeferedDatasetHandle` 214 Handles to load refCats on demand 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, refCat))
385 if len(overlapList) == 0:
388 firstCat = overlapList[0][1].get()
389 refCat = filtFunc(firstCat, overlapList[0][0].region)
390 trimmedAmount = len(firstCat) - len(refCat)
393 for dataId, inputRefCat
in overlapList[1:]:
394 tmpCat = inputRefCat.get()
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()
611 if filterReferenceMap
is None:
612 filterReferenceMap = {}
613 for filterName, refFilterName
in itertools.chain([(
None, defaultFilter)],
614 filterReferenceMap.items()):
616 camFluxName = filterName +
"_camFlux" if filterName
is not None else "camFlux" 617 refFluxName = refFilterName +
"_flux" 618 if refFluxName
not in refCat.schema:
619 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
620 aliasMap.set(camFluxName, refFluxName)
622 refFluxErrName = refFluxName +
"Err" 623 camFluxErrName = camFluxName +
"Err" 624 aliasMap.set(camFluxErrName, refFluxErrName)
630 """This function takes in a reference catalog and creates a new catalog with additional 631 columns defined the remaining function arguments. 635 refCat : `lsst.afw.table.SimpleCatalog` 636 Reference catalog to map to new catalog 640 expandedCat : `lsst.afw.table.SimpleCatalog` 641 Deep copy of input reference catalog with additional columns added 644 mapper.addMinimalSchema(refCat.schema,
True)
645 mapper.editOutputSchema().disconnectAliases()
647 for filterName
in filterNameList:
648 mapper.editOutputSchema().addField(f
"{filterName}_flux",
650 doc=f
"flux in filter {filterName}",
653 mapper.editOutputSchema().addField(f
"{filterName}_fluxErr",
655 doc=f
"flux uncertanty in filter {filterName}",
660 mapper.editOutputSchema().addField(
"centroid_x", type=float, doReplace=
True)
661 mapper.editOutputSchema().addField(
"centroid_y", type=float, doReplace=
True)
662 mapper.editOutputSchema().addField(
"hasCentroid", type=
"Flag", doReplace=
True)
663 mapper.editOutputSchema().getAliasMap().
set(
"slot_Centroid",
"centroid")
666 mapper.editOutputSchema().addField(
"photometric",
668 doc=
"set if the object can be used for photometric" +
671 mapper.editOutputSchema().addField(
"resolved",
673 doc=
"set if the object is spatially resolved" 675 mapper.editOutputSchema().addField(
"variable",
677 doc=
"set if the object has variable brightness" 681 expandedCat.setMetadata(refCat.getMetadata())
682 expandedCat.extend(refCat, mapper=mapper)
688 """Get the name of a flux field from a schema. 690 if filterName is specified: 691 return *filterName*_camFlux if present 692 else return *filterName*_flux if present (camera filter name matches reference filter name) 693 else throw RuntimeError 695 return camFlux, if present, 696 else throw RuntimeError 700 schema : `lsst.afw.table.Schema` 701 Reference catalog schema. 703 Name of camera filter. 707 fluxFieldName : `str` 713 If an appropriate field is not found. 716 raise RuntimeError(
"schema=%s is not a schema" % (schema,))
718 fluxFieldList = [filterName +
"_camFlux", filterName +
"_flux"]
720 fluxFieldList = [
"camFlux"]
721 for fluxField
in fluxFieldList:
722 if fluxField
in schema:
725 raise RuntimeError(
"Could not find flux field(s) %s" % (
", ".join(fluxFieldList)))
729 """Return keys for flux and flux error. 733 schema : `lsst.afw.table.Schema` 734 Reference catalog schema. 736 Name of camera filter. 740 keys : `tuple` of (`lsst.afw.table.Key`, `lsst.afw.table.Key`) 744 - flux error key, if present, else None 749 If flux field not found. 752 fluxErrField = fluxField +
"Err" 753 fluxKey = schema[fluxField].asKey()
755 fluxErrKey = schema[fluxErrField].asKey()
758 return (fluxKey, fluxErrKey)
762 pixelMargin = pexConfig.RangeField(
763 doc=
"Padding to add to 4 all edges of the bounding box (pixels)",
768 defaultFilter = pexConfig.Field(
769 doc=
"Default reference catalog filter to use if filter not specified in exposure; " 770 "if blank then filter must be specified in exposure",
774 filterMap = pexConfig.DictField(
775 doc=
"Mapping of camera filter name: reference catalog filter name; " 776 "each reference filter must exist",
781 requireProperMotion = pexConfig.Field(
782 doc=
"Require that the fields needed to correct proper motion " 783 "(epoch, pm_ra and pm_dec) are present?",
798 r"""!Abstract base class to load objects from reference catalogs 800 @anchor LoadReferenceObjectsTask_ 802 @section meas_algorithms_loadReferenceObjects_Contents Contents 804 - @ref meas_algorithms_loadReferenceObjects_Purpose 805 - @ref meas_algorithms_loadReferenceObjects_Initialize 806 - @ref meas_algorithms_loadReferenceObjects_IO 807 - @ref meas_algorithms_loadReferenceObjects_Schema 808 - @ref meas_algorithms_loadReferenceObjects_Config 810 @section meas_algorithms_loadReferenceObjects_Purpose Description 812 Abstract base class for tasks that load objects from a reference catalog 813 in a particular region of the sky. 815 Implementations must subclass this class, override the loadSkyCircle method, 816 and will typically override the value of ConfigClass with a task-specific config class. 818 @section meas_algorithms_loadReferenceObjects_Initialize Task initialisation 820 @copydoc \_\_init\_\_ 822 @section meas_algorithms_loadReferenceObjects_IO Invoking the Task 824 @copydoc loadPixelBox 826 @section meas_algorithms_loadReferenceObjects_Schema Schema of the reference object catalog 828 Reference object catalogs are instances of lsst.afw.table.SimpleCatalog with the following schema 829 (other fields may also be present). 830 The units use astropy quantity conventions, so a 2 suffix means squared. 831 See also makeMinimalSchema. 832 - coord: ICRS position of star on sky (an lsst.geom.SpherePoint) 833 - centroid: position of star on an exposure, if relevant (an lsst.afw.Point2D) 834 - hasCentroid: is centroid usable? (a Flag) 835 - *referenceFilterName*_flux: brightness in the specified reference catalog filter (nJy) 836 Note: you can use astropy.units to convert from AB Magnitude to nJy: 837 `u.Magnitude(value, u.ABmag).to_value(u.nJy)` 838 - *referenceFilterName*_fluxErr (optional): brightness standard deviation (nJy); 839 omitted if no data is available; possibly nan if data is available for some objects but not others 840 - camFlux: brightness in default camera filter (nJy); omitted if defaultFilter not specified 841 - camFluxErr: brightness standard deviation for default camera filter; 842 omitted if defaultFilter not specified or standard deviation not available that filter 843 - *cameraFilterName*_camFlux: brightness in specified camera filter (nJy) 844 - *cameraFilterName*_camFluxErr (optional): brightness standard deviation 845 in specified camera filter (nJy); omitted if no data is available; 846 possibly nan if data is available for some objects but not others 847 - photometric (optional): is the object usable for photometric calibration? (a Flag) 848 - resolved (optional): is the object spatially resolved? (a Flag) 849 - variable (optional): does the object have variable brightness? (a Flag) 850 - coord_raErr: uncertainty in `coord` along the direction of right ascension (radian, an Angle) 851 = uncertainty in ra * cos(dec); nan if unknown 852 - coord_decErr: uncertainty in `coord` along the direction of declination (radian, an Angle); 855 The following are optional; fields should only be present if the 856 information is available for at least some objects. 857 Numeric values are `nan` if unknown: 858 - epoch: date of observation as TAI MJD (day) 860 - pm_ra: proper motion along the direction of right ascension (rad/year, an Angle) = dra/dt * cos(dec) 861 - pm_dec: proper motion along the direction of declination (rad/year, and Angle) 862 - pm_raErr: uncertainty in `pm_ra` (rad/year) 863 - pm_decErr: uncertainty in `pm_dec` (rad/year) 864 - pm_ra_dec_Cov: covariance between pm_ra and pm_dec (rad2/year2) 865 - pm_flag: set if proper motion, error or covariance is bad 867 - parallax: parallax (rad, an Angle) 868 - parallaxErr: uncertainty in `parallax` (rad) 869 - parallax_flag: set if parallax value or parallaxErr is bad 871 - coord_ra_pm_ra_Cov: covariance between coord_ra and pm_ra (rad2/year) 872 - coord_ra_pm_dec_Cov: covariance between coord_ra and pm_dec (rad2/year) 873 - coord_ra_parallax_Cov: covariance between coord_ra and parallax (rad2/year) 874 - coord_dec_pm_ra_Cov: covariance between coord_dec and pm_ra (rad2/year) 875 - coord_dec_pm_dec_Cov: covariance between coord_dec and pm_dec (rad2/year) 876 - coord_dec_parallax_Cov: covariance between coord_dec and parallax (rad2/year) 877 - pm_ra_parallax_Cov: covariance between pm_ra and parallax (rad2/year) 878 - pm_dec_parallax_Cov: covariance between pm_dec and parallax (rad2/year) 880 @section meas_algorithms_loadReferenceObjects_Config Configuration parameters 882 See @ref LoadReferenceObjectsConfig for a base set of configuration parameters. 883 Most subclasses will add configuration variables. 885 ConfigClass = LoadReferenceObjectsConfig
886 _DefaultName =
"LoadReferenceObjects" 889 """Construct a LoadReferenceObjectsTask 893 butler : `lsst.daf.persistence.Butler` 894 Data butler, for access reference catalogs. 896 pipeBase.Task.__init__(self, *args, **kwargs)
900 def loadPixelBox(self, bbox, wcs, filterName=None, photoCalib=None, epoch=None):
901 """Load reference objects that overlap a rectangular pixel region. 905 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 906 Bounding box for pixels. 907 wcs : `lsst.afw.geom.SkyWcs` 908 WCS; used to convert pixel positions to sky coordinates 911 Name of filter, or `None` or `""` for the default filter. 912 This is used for flux values in case we have flux limits 913 (which are not yet implemented). 914 photoCalib : `lsst.afw.image.PhotoCalib` (optional) 915 Calibration, or `None` if unknown. 916 epoch : `astropy.time.Time` (optional) 917 Epoch to which to correct proper motion and parallax, 918 or None to not apply such corrections. 922 results : `lsst.pipe.base.Struct` 923 A Struct containing the following fields: 924 refCat : `lsst.afw.catalog.SimpleCatalog` 925 A catalog of reference objects with the standard 926 schema, as documented in the main doc string for 927 `LoadReferenceObjects`. 928 The catalog is guaranteed to be contiguous. 930 Name of flux field for specified `filterName`. 934 The search algorithm works by searching in a region in sky 935 coordinates whose center is the center of the bbox and radius 936 is large enough to just include all 4 corners of the bbox. 937 Stars that lie outside the bbox are then trimmed from the list. 942 self.log.
info(
"Loading reference objects using center %s and radius %s deg" %
943 (circle.coord, circle.radius.asDegrees()))
944 loadRes = self.
loadSkyCircle(circle.coord, circle.radius, filterName, centroids=
True)
945 refCat = loadRes.refCat
946 numFound = len(refCat)
949 refCat = self.
_trimToBBox(refCat=refCat, bbox=circle.bbox, wcs=wcs)
950 numTrimmed = numFound - len(refCat)
951 self.log.
debug(
"trimmed %d out-of-bbox objects, leaving %d", numTrimmed, len(refCat))
952 self.log.
info(
"Loaded %d reference objects", len(refCat))
955 if not refCat.isContiguous():
956 loadRes.refCat = refCat.copy(deep=
True)
961 def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None, centroids=False):
962 """Load reference objects that overlap a circular sky region. 966 ctrCoord : `lsst.geom.SpherePoint` 967 ICRS center of search region. 968 radius : `lsst.geom.Angle` 969 Radius of search region. 970 filterName : `str` (optional) 971 Name of filter, or `None` or `""` for the default filter. 972 This is used for flux values in case we have flux limits 973 (which are not yet implemented). 974 epoch : `astropy.time.Time` (optional) 975 Epoch to which to correct proper motion and parallax, 976 or None to not apply such corrections. 977 centroids : `bool` (optional) 978 Add centroid fields to the loaded Schema. ``loadPixelBox`` expects 979 these fields to exist. 983 results : `lsst.pipe.base.Struct` 984 A Struct containing the following fields: 985 refCat : `lsst.afw.catalog.SimpleCatalog` 986 A catalog of reference objects with the standard 987 schema, as documented in the main doc string for 988 `LoadReferenceObjects`. 989 The catalog is guaranteed to be contiguous. 991 Name of flux field for specified `filterName`. 995 Note that subclasses are responsible for performing the proper motion 996 correction, since this is the lowest-level interface for retrieving 1002 def _trimToBBox(refCat, bbox, wcs):
1003 """Remove objects outside a given pixel bounding box and set 1004 centroid and hasCentroid fields. 1008 refCat : `lsst.afw.table.SimpleCatalog` 1009 A catalog of objects. The schema must include fields 1010 "coord", "centroid" and "hasCentroid". 1011 The "coord" field is read. 1012 The "centroid" and "hasCentroid" fields are set. 1013 bbox : `lsst.geom.Box2D` 1015 wcs : `lsst.afw.geom.SkyWcs` 1016 WCS; used to convert sky coordinates to pixel positions. 1018 @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set 1022 retStarCat =
type(refCat)(refCat.table)
1024 point = star.get(centroidKey)
1025 if bbox.contains(point):
1026 retStarCat.append(star)
1029 def _addFluxAliases(self, schema):
1030 """Add aliases for camera filter fluxes to the schema. 1032 If self.config.defaultFilter then adds these aliases: 1033 camFlux: <defaultFilter>_flux 1034 camFluxErr: <defaultFilter>_fluxErr, if the latter exists 1036 For each camFilter: refFilter in self.config.filterMap adds these aliases: 1037 <camFilter>_camFlux: <refFilter>_flux 1038 <camFilter>_camFluxErr: <refFilter>_fluxErr, if the latter exists 1042 schema : `lsst.afw.table.Schema` 1043 Schema for reference catalog. 1048 If any reference flux field is missing from the schema. 1050 aliasMap = schema.getAliasMap()
1052 def addAliasesForOneFilter(filterName, refFilterName):
1053 """Add aliases for a single filter 1057 filterName : `str` (optional) 1058 Camera filter name. The resulting alias name is 1059 <filterName>_camFlux, or simply "camFlux" if `filterName` 1061 refFilterName : `str` 1062 Reference catalog filter name; the field 1063 <refFilterName>_flux must exist. 1065 camFluxName = filterName +
"_camFlux" if filterName
is not None else "camFlux" 1066 refFluxName = refFilterName +
"_flux" 1067 if refFluxName
not in schema:
1068 raise RuntimeError(
"Unknown reference filter %s" % (refFluxName,))
1069 aliasMap.set(camFluxName, refFluxName)
1070 refFluxErrName = refFluxName +
"Err" 1071 if refFluxErrName
in schema:
1072 camFluxErrName = camFluxName +
"Err" 1073 aliasMap.set(camFluxErrName, refFluxErrName)
1075 if self.config.defaultFilter:
1076 addAliasesForOneFilter(
None, self.config.defaultFilter)
1078 for filterName, refFilterName
in self.config.filterMap.items():
1079 addAliasesForOneFilter(filterName, refFilterName)
1083 addIsPhotometric=False, addIsResolved=False,
1084 addIsVariable=False, coordErrDim=2,
1085 addProperMotion=False, properMotionErrDim=2,
1087 """Make a standard schema for reference object catalogs. 1091 filterNameList : `list` of `str` 1092 List of filter names. Used to create <filterName>_flux fields. 1093 addIsPhotometric : `bool` 1094 If True then add field "photometric". 1095 addIsResolved : `bool` 1096 If True then add field "resolved". 1097 addIsVariable : `bool` 1098 If True then add field "variable". 1100 Number of coord error fields; must be one of 0, 2, 3: 1102 - If 2 or 3: add fields "coord_raErr" and "coord_decErr". 1103 - If 3: also add field "coord_radecErr". 1104 addProperMotion : `bool` 1105 If True add fields "epoch", "pm_ra", "pm_dec" and "pm_flag". 1106 properMotionErrDim : `int` 1107 Number of proper motion error fields; must be one of 0, 2, 3; 1108 ignored if addProperMotion false: 1109 - If 2 or 3: add fields "pm_raErr" and "pm_decErr". 1110 - If 3: also add field "pm_radecErr". 1111 addParallax : `bool` 1112 If True add fields "epoch", "parallax", "parallaxErr" 1113 and "parallax_flag". 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",
1229 field=
"parallaxErr",
1231 doc=
"uncertainty in parallax",
1235 field=
"parallax_flag",
1237 doc=
"Set if parallax or parallax error is bad",
1241 def _calculateCircle(self, bbox, wcs):
1242 """Compute on-sky center and radius of search region. 1246 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 1248 wcs : `lsst.afw.geom.SkyWcs` 1249 WCS; used to convert pixel positions to sky coordinates. 1253 results : `lsst.pipe.base.Struct` 1254 A Struct containing: 1256 - coord : `lsst.geom.SpherePoint` 1257 ICRS center of the search region. 1258 - radius : `lsst.geom.Angle` 1259 Radius of the search region. 1260 - bbox : `lsst.geom.Box2D` 1261 Bounding box used to compute the circle. 1264 bbox.grow(self.config.pixelMargin)
1265 coord = wcs.pixelToSky(bbox.getCenter())
1266 radius =
max(coord.separation(wcs.pixelToSky(pp))
for pp
in bbox.getCorners())
1267 return pipeBase.Struct(coord=coord, radius=radius, bbox=bbox)
1270 """Return metadata about the load. 1272 This metadata is used for reloading the catalog (e.g., for 1273 reconstituting a normalised match list. 1277 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D` 1279 wcs : `lsst.afw.geom.SkyWcs` 1280 WCS; used to convert pixel positions to sky coordinates. 1282 Name of camera filter, or `None` or `""` for the default 1284 photoCalib : `lsst.afw.image.PhotoCalib` (optional) 1285 Calibration, or `None` if unknown. 1286 epoch : `astropy.time.Time` (optional) 1287 Epoch to which to correct proper motion and parallax, 1288 or None to not apply such corrections. 1292 metadata : lsst.daf.base.PropertyList 1293 Metadata about the load. 1296 return self.
getMetadataCircle(circle.coord, circle.radius, filterName, photoCalib)
1299 """Return metadata about the load. 1301 This metadata is used for reloading the catalog (e.g., for 1302 reconstituting a normalised match list. 1306 coord : `lsst.geom.SpherePoint` 1307 ICRS center of the search region. 1308 radius : `lsst.geom.Angle` 1309 Radius of the search region. 1311 Name of camera filter, or `None` or `""` for the default 1313 photoCalib : `lsst.afw.image.PhotoCalib` (optional) 1314 Calibration, or `None` if unknown. 1315 epoch : `astropy.time.Time` (optional) 1316 Epoch to which to correct proper motion and parallax, 1317 or None to not apply such corrections. 1321 metadata : lsst.daf.base.PropertyList 1322 Metadata about the load 1325 md.add(
'RA', coord.getRa().asDegrees(),
'field center in degrees')
1326 md.add(
'DEC', coord.getDec().asDegrees(),
'field center in degrees')
1327 md.add(
'RADIUS', radius.asDegrees(),
'field radius in degrees, minimum')
1328 md.add(
'SMATCHV', 1,
'SourceMatchVector version number')
1329 filterName =
"UNKNOWN" if filterName
is None else str(filterName)
1330 md.add(
'FILTER', filterName,
'filter name for photometric data')
1331 md.add(
'EPOCH',
"NONE" if epoch
is None else epoch,
'Epoch (TAI MJD) for catalog')
1335 """Relink an unpersisted match list to sources and reference 1338 A match list is persisted and unpersisted as a catalog of IDs 1339 produced by afw.table.packMatches(), with match metadata 1340 (as returned by the astrometry tasks) in the catalog's metadata 1341 attribute. This method converts such a match catalog into a match 1342 list, with links to source records and reference object records. 1346 matchCat : `lsst.afw.table.BaseCatalog` 1347 Unperisted packed match list. 1348 ``matchCat.table.getMetadata()`` must contain match metadata, 1349 as returned by the astrometry tasks. 1350 sourceCat : `lsst.afw.table.SourceCatalog` 1351 Source catalog. As a side effect, the catalog will be sorted 1356 matchList : `lsst.afw.table.ReferenceMatchVector` 1362 """Apply proper motion correction to a reference catalog. 1364 Adjust position and position error in the ``catalog`` 1365 for proper motion to the specified ``epoch``, 1366 modifying the catalong in place. 1370 catalog : `lsst.afw.table.SimpleCatalog` 1371 Catalog of positions, containing: 1373 - Coordinates, retrieved by the table's coordinate key. 1374 - ``coord_raErr`` : Error in Right Ascension (rad). 1375 - ``coord_decErr`` : Error in Declination (rad). 1376 - ``pm_ra`` : Proper motion in Right Ascension (rad/yr, 1378 - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional. 1379 - ``pm_dec`` : Proper motion in Declination (rad/yr, 1381 - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional. 1382 - ``epoch`` : Mean epoch of object (an astropy.time.Time) 1383 epoch : `astropy.time.Time` (optional) 1384 Epoch to which to correct proper motion and parallax, 1385 or None to not apply such corrections. 1387 if (
"epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema):
1388 if self.config.requireProperMotion:
1389 raise RuntimeError(
"Proper motion correction required but not available from catalog")
1390 self.log.
warn(
"Proper motion correction not available from catalog")
1396 """Relink an unpersisted match list to sources and reference 1399 A match list is persisted and unpersisted as a catalog of IDs 1400 produced by afw.table.packMatches(), with match metadata 1401 (as returned by the astrometry tasks) in the catalog's metadata 1402 attribute. This method converts such a match catalog into a match 1403 list, with links to source records and reference object records. 1408 Reference object loader to use in getting reference objects 1409 matchCat : `lsst.afw.table.BaseCatalog` 1410 Unperisted packed match list. 1411 ``matchCat.table.getMetadata()`` must contain match metadata, 1412 as returned by the astrometry tasks. 1413 sourceCat : `lsst.afw.table.SourceCatalog` 1414 Source catalog. As a side effect, the catalog will be sorted 1419 matchList : `lsst.afw.table.ReferenceMatchVector` 1422 matchmeta = matchCat.table.getMetadata()
1423 version = matchmeta.getInt(
'SMATCHV')
1425 raise ValueError(
'SourceMatchVector version number is %i, not 1.' % version)
1426 filterName = matchmeta.getString(
'FILTER').
strip()
1428 epoch = matchmeta.getDouble(
'EPOCH')
1431 if 'RADIUS' in matchmeta:
1434 matchmeta.getDouble(
'DEC'), lsst.geom.degrees)
1435 rad = matchmeta.getDouble(
'RADIUS') * lsst.geom.degrees
1436 refCat = refObjLoader.loadSkyCircle(ctrCoord, rad, filterName, epoch=epoch).refCat
1437 elif "INNER_UPPER_LEFT_RA" in matchmeta:
1443 for place
in (
"UPPER_LEFT",
"UPPER_RIGHT",
"LOWER_LEFT",
"LOWER_RIGHT"):
1445 matchmeta.getDouble(f
"OUTER_{place}_DEC"),
1446 lsst.geom.degrees).getVector()
1449 refCat = refObjLoader.loadRegion(outerBox, filterName=filterName, epoch=epoch).refCat
1457 """Apply proper motion correction to a reference catalog. 1459 Adjust position and position error in the ``catalog`` 1460 for proper motion to the specified ``epoch``, 1461 modifying the catalong in place. 1465 log : `lsst.log.log` 1466 log object to write to 1467 catalog : `lsst.afw.table.SimpleCatalog` 1468 Catalog of positions, containing: 1470 - Coordinates, retrieved by the table's coordinate key. 1471 - ``coord_raErr`` : Error in Right Ascension (rad). 1472 - ``coord_decErr`` : Error in Declination (rad). 1473 - ``pm_ra`` : Proper motion in Right Ascension (rad/yr, 1475 - ``pm_raErr`` : Error in ``pm_ra`` (rad/yr), optional. 1476 - ``pm_dec`` : Proper motion in Declination (rad/yr, 1478 - ``pm_decErr`` : Error in ``pm_dec`` (rad/yr), optional. 1479 - ``epoch`` : Mean epoch of object (an astropy.time.Time) 1480 epoch : `astropy.time.Time` (optional) 1481 Epoch to which to correct proper motion and parallax, 1482 or None to not apply such corrections. 1484 if "epoch" not in catalog.schema
or "pm_ra" not in catalog.schema
or "pm_dec" not in catalog.schema:
1485 log.warn(
"Proper motion correction not available from catalog")
1487 if not catalog.isContiguous():
1488 raise RuntimeError(
"Catalog must be contiguous")
1489 catEpoch = astropy.time.Time(catalog[
"epoch"], scale=
"tai", format=
"mjd")
1490 log.debug(
"Correcting reference catalog for proper motion to %r", epoch)
1492 timeDiffsYears = (epoch.tai - catEpoch).
to(astropy.units.yr).value
1493 coordKey = catalog.table.getCoordKey()
1496 pmRaRad = catalog[
"pm_ra"]
1497 pmDecRad = catalog[
"pm_dec"]
1498 offsetsRaRad = pmRaRad*timeDiffsYears
1499 offsetsDecRad = pmDecRad*timeDiffsYears
1507 offsetBearingsRad = numpy.arctan2(pmDecRad*1e6, pmRaRad*1e6)
1508 offsetAmountsRad = numpy.hypot(offsetsRaRad, offsetsDecRad)
1509 for record, bearingRad, amountRad
in zip(catalog, offsetBearingsRad, offsetAmountsRad):
1510 record.set(coordKey,
1511 record.get(coordKey).offset(bearing=bearingRad*lsst.geom.radians,
1512 amount=amountRad*lsst.geom.radians))
1514 if "coord_raErr" in catalog.schema:
1515 catalog[
"coord_raErr"] = numpy.hypot(catalog[
"coord_raErr"],
1516 catalog[
"pm_raErr"]*timeDiffsYears)
1517 if "coord_decErr" in catalog.schema:
1518 catalog[
"coord_decErr"] = numpy.hypot(catalog[
"coord_decErr"],
1519 catalog[
"pm_decErr"]*timeDiffsYears)
def getMetadataCircle(self, coord, radius, filterName, photoCalib=None, epoch=None)
def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None, centroids=False)
static Log getDefaultLogger()
Return default logger instance, same as default constructor.
Defines the fields and offsets for a table.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def joinMatchListWithCatalog(self, matchCat, sourceCat)
def __call__(self, refCat, catRegion)
def loadPixelBox(self, bbox, wcs, filterName=None, epoch=None, photoCalib=None, bboxPadding=100)
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 __init__(self, dataIds, refCats, config, log=None)
def loadRegion(self, region, filtFunc=None, filterName=None, epoch=None)
def getRefFluxKeys(schema, filterName=None)
A description of a field in a table.
Abstract base class to load objects from reference catalogs.
def makeMinimalSchema(filterNameList, addCentroid=False, addIsPhotometric=False, addIsResolved=False, addIsVariable=False, coordErrDim=2, addProperMotion=False, properMotionErrDim=2, addParallax=False)
def applyProperMotionsImpl(log, catalog, epoch)
def isOldFluxField(name, units)
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.
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.