LSSTApplications  18.1.0
LSSTDataManagementBasePackage
loadAstrometryNetObjects.py
Go to the documentation of this file.
1 from __future__ import absolute_import, division, print_function
2 
3 __all__ = ["LoadAstrometryNetObjectsTask", "LoadAstrometryNetObjectsConfig"]
4 
5 from builtins import object
6 
7 import lsst.pipe.base as pipeBase
8 from lsst.meas.algorithms import LoadReferenceObjectsTask, getRefFluxField
9 from lsst.meas.algorithms.loadReferenceObjects import convertToNanojansky
10 from . import astrometry_net
11 from .multiindex import AstrometryNetCatalog, getConfigFromEnvironment
12 
13 LoadAstrometryNetObjectsConfig = LoadReferenceObjectsTask.ConfigClass
14 
15 # The following block adds links to this task from the Task Documentation page.
16 
22 
23 
25  """!Load reference objects from astrometry.net index files
26 
27  @anchor LoadAstrometryNetObjectsTask_
28 
29  @section meas_astrom_loadAstrometryNetObjects_Contents Contents
30 
31  - @ref meas_astrom_loadAstrometryNetObjects_Purpose
32  - @ref meas_astrom_loadAstrometryNetObjects_Initialize
33  - @ref meas_astrom_loadAstrometryNetObjects_IO
34  - @ref meas_algorithms_loadReferenceObjects_Schema
35  - @ref meas_astrom_loadAstrometryNetObjects_Config
36  - @ref meas_astrom_loadAstrometryNetObjects_Example
37  - @ref meas_astrom_loadAstrometryNetObjects_Debug
38 
39  @section meas_astrom_loadAstrometryNetObjects_Purpose Description
40 
41  Load reference objects from astrometry.net index files.
42 
43  @section meas_astrom_loadAstrometryNetObjects_Initialize Task initialisation
44 
45  @copydoc \_\_init\_\_
46 
47  @section meas_astrom_loadAstrometryNetObjects_IO Invoking the Task
48 
49  @copydoc loadObjectsInBBox
50 
51  @section meas_astrom_loadAstrometryNetObjects_Config Configuration parameters
52 
53  See @ref LoadAstrometryNetObjectsConfig
54 
55  @section meas_astrom_loadAstrometryNetObjects_Example A complete example of using
56  LoadAstrometryNetObjectsTask
57 
58  LoadAstrometryNetObjectsTask is a subtask of AstrometryTask, which is called by PhotoCalTask.
59  See \ref pipe_tasks_photocal_Example.
60 
61  @section meas_astrom_loadAstrometryNetObjects_Debug Debug variables
62 
63  LoadAstrometryNetObjectsTask does not support any debug variables.
64  """
65  ConfigClass = LoadAstrometryNetObjectsConfig
66 
67  def __init__(self, config=None, andConfig=None, **kwargs):
68  """!Create a LoadAstrometryNetObjectsTask
69 
70  @param[in] config configuration (an instance of self.ConfigClass); if None use self.ConfigClass()
71  @param[in] andConfig astrometry.net data config (an instance of AstromNetDataConfig, or None);
72  if None then use andConfig.py in the astrometry_net_data product (which must be setup)
73  @param[in] kwargs additional keyword arguments for pipe_base Task.\_\_init\_\_
74 
75  @throw RuntimeError if andConfig is None and the configuration cannot be found,
76  either because astrometry_net_data is not setup in eups
77  or because the setup version does not include the file "andConfig.py"
78  """
79  LoadReferenceObjectsTask.__init__(self, config=config, **kwargs)
80  self.andConfig = andConfig
81  self.haveIndexFiles = False # defer reading index files until we know they are needed
82  # because astrometry may not be used, in which case it may not be properly configured
83 
84  @pipeBase.timeMethod
85  def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None):
86  """!Load reference objects that overlap a circular sky region
87 
88  @param[in] ctrCoord center of search region (an afwGeom.Coord)
89  @param[in] radius radius of search region (an afwGeom.Angle)
90  @param[in] filterName name of filter, or None for the default filter;
91  used for flux values in case we have flux limits (which are not yet implemented)
92  @param[in] epoch Epoch for proper motion and parallax correction
93  (an astropy.time.Time), or None
94 
95  No proper motion correction is made, since our astrometry.net catalogs
96  typically don't support that, and even if they do they format is uncertain.
97  Users interested in proper motion corrections should use the
98  lsst.meas.algorithms.LoadIndexedReferenceObjectsTask or they will need to
99  subclass and define how the proper motion correction is to be done.
100 
101  @return an lsst.pipe.base.Struct containing:
102  - refCat a catalog of reference objects with the
103  \link meas_algorithms_loadReferenceObjects_Schema standard schema \endlink
104  as documented in LoadReferenceObjects, including photometric, resolved and variable;
105  hasCentroid is False for all objects.
106  - fluxField = name of flux field for specified filterName
107  """
108  self._readIndexFiles()
109 
110  names = []
111  mcols = []
112  ecols = []
113  for col, mcol in self.andConfig.magColumnMap.items():
114  names.append(col)
115  mcols.append(mcol)
116  ecols.append(self.andConfig.magErrorColumnMap.get(col, ''))
117  margs = (names, mcols, ecols)
118 
119  solver = self._getSolver()
120 
121  # Find multi-index files within range
122  multiInds = self._getMIndexesWithinRange(ctrCoord, radius)
123 
124  # compute solver.getCatalog arguments that follow the list of star kd-trees:
125  # - center equatorial angle (e.g. RA) in deg
126  # - center polar angle (e.g. Dec) in deg
127  # - radius, in deg
128  # - idColumn
129  # - (margs)
130  # - star-galaxy column
131  # - variability column
132  fixedArgTuple = (
133  ctrCoord,
134  radius,
135  self.andConfig.idColumn,
136  ) + margs + (
137  self.andConfig.starGalaxyColumn,
138  self.andConfig.variableColumn,
139  True, # eliminate duplicate IDs
140  )
141 
142  self.log.debug("search for objects at %s with radius %s deg", ctrCoord, radius.asDegrees())
143  with LoadMultiIndexes(multiInds):
144  # We just want to pass the star kd-trees, so just pass the
145  # first element of each multi-index.
146  inds = tuple(mi[0] for mi in multiInds)
147  refCat = solver.getCatalog(inds, *fixedArgTuple)
148 
149  self._addFluxAliases(schema=refCat.schema)
150 
151  fluxField = getRefFluxField(schema=refCat.schema, filterName=filterName)
152 
153  # NOTE: sourceSelectors require contiguous catalogs, so ensure
154  # contiguity now, so views are preserved from here on.
155  if not refCat.isContiguous():
156  refCat = refCat.copy(deep=True)
157 
158  # Update flux fields to be nJy. a.net catalogs do not have a conversion script.
159  self.log.warn("Loading A.net reference catalog with old style units in schema.")
160  self.log.warn("A.net reference catalogs will not be supported in the future.")
161  self.log.warn("See RFC-562 and RFC-575 for more details.")
162  refCat = convertToNanojansky(refCat, self.log)
163 
164  self.log.debug("found %d objects", len(refCat))
165  return pipeBase.Struct(
166  refCat=refCat,
167  fluxField=fluxField,
168  )
169 
170  @pipeBase.timeMethod
171  def _readIndexFiles(self):
172  """!Read all astrometry.net index files, if not already read
173  """
174  if self.haveIndexFiles:
175  return
176 
177  self.log.debug("read index files")
178  self.haveIndexFiles = True # just try once
179 
180  if self.andConfig is None:
182 
184 
185  def _getMIndexesWithinRange(self, ctrCoord, radius):
186  """!Get list of muti-index objects within range
187 
188  @param[in] ctrCoord center of search region (an afwGeom.Coord)
189  @param[in] radius radius of search region (an afwGeom.Angle)
190 
191  @return list of multiindex objects
192  """
193  return [mi for mi in self.multiInds if mi.isWithinRange(ctrCoord, radius)]
194 
195  def _getSolver(self):
196  solver = astrometry_net.Solver()
197  # HACK, set huge default pixel scale range.
198  lo, hi = 0.01, 3600.
199  solver.setPixelScaleRange(lo, hi)
200  return solver
201 
202 
204  """Context manager for loading and unloading astrometry.net multi-index files
205  """
206 
207  def __init__(self, multiInds):
208  self.multiInds = multiInds
209 
210  def __enter__(self):
211  for mi in self.multiInds:
212  mi.reload()
213  return self.multiInds
214 
215  def __exit__(self, typ, val, trace):
216  for mi in self.multiInds:
217  mi.unload()
def _readIndexFiles(self)
Read all astrometry.net index files, if not already read.
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
def convertToNanojansky(catalog, log, doConvert=True)
def _getMIndexesWithinRange(self, ctrCoord, radius)
Get list of muti-index objects within range.
Abstract base class to load objects from reference catalogs.
def loadSkyCircle(self, ctrCoord, radius, filterName=None, epoch=None)
Load reference objects that overlap a circular sky region.
def __init__(self, config=None, andConfig=None, kwargs)
Create a LoadAstrometryNetObjectsTask.