lsst.meas.base  20.0.0-6-gdef485e+daef151ab3
applyApCorr.py
Go to the documentation of this file.
1 # This file is part of meas_base.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (https://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 
22 import math
23 
24 import numpy as np
25 
26 import lsst.pex.config
28 import lsst.afw.image
29 import lsst.pipe.base
30 from .apCorrRegistry import getApCorrNameSet
31 
32 # If True then scale instFlux error by apCorr; if False then use a more complex computation
33 # that over-estimates instFlux error (often grossly so) because it double-counts photon noise.
34 # This flag is intended to be temporary until we figure out a better way to compute
35 # the effects of aperture correction on instFlux uncertainty
36 UseNaiveFluxErr = True
37 
38 __all__ = ("ApplyApCorrConfig", "ApplyApCorrTask")
39 
40 
41 class ApCorrInfo:
42  """Catalog field names and keys needed to aperture correct a particular
43  instrument flux.
44 
45  Parameters
46  ----------
47  schema : `lsst.afw.table`
48  Source catalog schema. Three fields are used to generate keys:
49  - ``{name}_instFlux``
50  - ``{name}_instFluxErr``
51  - ``{name}_flag``
52  Three fields are added:
53  - ``{name}_apCorr`` (only if not already added by proxy)
54  - ``{name}_apCorrErr`` (only if not already added by proxy)
55  - ``{name}_flag_apCorr``
56  model : `str`
57  Field name prefix for instFlux with aperture correction model, e.g.
58  "base_PsfFlux"
59  name : `str`
60  Field name prefix for instFlux needing aperture correction; may be
61  `None` if it is the same as ``model``
62 
63  Notes
64  -----
65  The aperture correction can be derived from the meaasurements in the
66  column being aperture-corrected or from measurements in a different
67  column (a "proxy"). In the first case, we will add columns to contain
68  the aperture correction values; in the second case (using a proxy),
69  we will add an alias to the proxy's aperture correction values. In
70  all cases, we add a flag.
71  """
72 
73  name = None
74  """Field name prefix for flux needing aperture correction (`str`).
75  """
76 
77  modelName = None
78  """Field name for aperture correction model for flux (`str`).
79  """
80 
81  modelSigmaName = None
82  """Field name for aperture correction model for fluxErr (`str`).
83  """
84 
85  doApCorrColumn = None
86  """Should we write the aperture correction values (`bool`)?
87 
88  They should not be written if they're already being written by a proxy.
89  """
90 
91  instFluxName = None
92  """Name of ``instFlux`` field (`str`).
93  """
94 
95  instFluxErrName = None
96  """Name of ``instFlux`` sigma field (`str`).
97  """
98 
99  instFluxKey = None
100  """Key to ``instFlux`` field (`lsst.afw.table.schema.Key`).
101  """
102 
103  instFluxErrKey = None
104  """Key to ``instFlux`` sigma field (`lsst.afw.table.schema.Key`).
105  """
106 
107  fluxFlagKey = None
108  """Key to the flux flag field (`lsst.afw.table.schema.Key`).
109  """
110 
111  apCorrKey = None
112  """Key to new aperture correction field (`lsst.afw.table.schema.Key`).
113  """
114 
115  apCorrErrKey = None
116  """Key to new aperture correction sigma field (`lsst.afw.table.schema.Key`).
117  """
118 
119  apCorrFlagKey = None
120  """Key to new aperture correction flag field (`lsst.afw.table.schema.Key`).
121  """
122 
123  def __init__(self, schema, model, name=None):
124  if name is None:
125  name = model
126  self.name = name
127  self.modelName = model + "_instFlux"
128  self.modelSigmaName = model + "_instFluxErr"
129  self.instFluxName = name + "_instFlux"
130  self.instFluxErrName = name + "_instFluxErr"
131  self.instFluxKey = schema.find(self.instFluxName).key
132  self.instFluxErrKey = schema.find(self.instFluxErrName).key
133  self.fluxFlagKey = schema.find(name + "_flag").key
134 
135  # No need to write the same aperture corrections multiple times
136  self.doApCorrColumn = (name == model or model + "_apCorr" not in schema)
137  if self.doApCorrColumn:
138  self.apCorrKey = schema.addField(
139  name + "_apCorr",
140  doc="aperture correction applied to %s" % (name,),
141  type=np.float64,
142  )
143  self.apCorrErrKey = schema.addField(
144  name + "_apCorrErr",
145  doc="standard deviation of aperture correction applied to %s" % (name,),
146  type=np.float64,
147  )
148  else:
149  aliases = schema.getAliasMap()
150  aliases.set(name + "_apCorr", model + "_apCorr")
151  aliases.set(name + "_apCorrErr", model + "_apCorrErr")
152  self.apCorrKey = schema.find(name + "_apCorr").key
153  self.apCorrErrKey = schema.find(name + "_apCorrErr").key
154 
155  self.apCorrFlagKey = schema.addField(
156  name + "_flag_apCorr",
157  doc="set if unable to aperture correct %s" % (name,),
158  type="Flag",
159  )
160 
161 
162 class ApplyApCorrConfig(lsst.pex.config.Config):
163  """Aperture correction configuration.
164  """
165 
166  ignoreList = lsst.pex.config.ListField(
167  doc="flux measurement algorithms in getApCorrNameSet() to ignore; "
168  "if a name is listed that does not appear in getApCorrNameSet() then a warning is logged",
169  dtype=str,
170  optional=False,
171  default=(),
172  )
173  doFlagApCorrFailures = lsst.pex.config.Field(
174  doc="set the general failure flag for a flux when it cannot be aperture-corrected?",
175  dtype=bool,
176  default=True,
177  )
178  proxies = lsst.pex.config.DictField(
179  doc="flux measurement algorithms to be aperture-corrected by reference to another algorithm; "
180  "this is a mapping alg1:alg2, where 'alg1' is the algorithm being corrected, and 'alg2' "
181  "is the algorithm supplying the corrections",
182  keytype=str,
183  itemtype=str,
184  default={},
185  )
186 
187 
188 class ApplyApCorrTask(lsst.pipe.base.Task):
189  """Apply aperture corrections.
190 
191  Parameters
192  ----------
193  schema : `lsst.afw.table.Schema`
194  """
195  ConfigClass = ApplyApCorrConfig
196  _DefaultName = "applyApCorr"
197 
198  def __init__(self, schema, **kwds):
199  lsst.pipe.base.Task.__init__(self, **kwds)
200 
201  self.apCorrInfoDict = dict()
202  apCorrNameSet = getApCorrNameSet()
203  ignoreSet = set(self.config.ignoreList)
204  missingNameSet = ignoreSet - set(apCorrNameSet)
205  if missingNameSet:
206  self.log.warn("Fields in ignoreList that are not in fluxCorrectList: %s",
207  sorted(list(missingNameSet)))
208  for name in sorted(list(apCorrNameSet - ignoreSet)):
209  if name + "_instFlux" not in schema:
210  # if a field in the registry is missing from the schema, silently ignore it.
211  continue
212  self.apCorrInfoDict[name] = ApCorrInfo(schema=schema, model=name)
213 
214  for name, model in self.config.proxies.items():
215  if name in apCorrNameSet:
216  # Already done or ignored
217  continue
218  if name + "_instFlux" not in schema:
219  # Silently ignore
220  continue
221  self.apCorrInfoDict[name] = ApCorrInfo(schema=schema, model=model, name=name)
222 
223  def run(self, catalog, apCorrMap):
224  """Apply aperture corrections to a catalog of sources.
225 
226  Parameters
227  ----------
228  catalog : `lsst.afw.table.SourceCatalog`
229  Catalog of sources. Will be updated in place.
230  apCorrMap : `lsst.afw.image.ApCorrMap`
231  Aperture correction map
232 
233  Notes
234  -----
235  If you show debug-level log messages then you will see statistics for
236  the effects of aperture correction.
237  """
238  self.log.info("Applying aperture corrections to %d instFlux fields", len(self.apCorrInfoDict))
239  if UseNaiveFluxErr:
240  self.log.debug("Use naive instFlux sigma computation")
241  else:
242  self.log.debug("Use complex instFlux sigma computation that double-counts photon noise "
243  "and thus over-estimates instFlux uncertainty")
244  for apCorrInfo in self.apCorrInfoDict.values():
245  apCorrModel = apCorrMap.get(apCorrInfo.modelName)
246  apCorrErrModel = apCorrMap.get(apCorrInfo.modelSigmaName)
247  if None in (apCorrModel, apCorrErrModel):
248  missingNames = [(apCorrInfo.modelName, apCorrInfo.modelSigmaName)[i]
249  for i, model in enumerate((apCorrModel, apCorrErrModel)) if model is None]
250  self.log.warn("Cannot aperture correct %s because could not find %s in apCorrMap" %
251  (apCorrInfo.name, " or ".join(missingNames),))
252  for source in catalog:
253  source.set(apCorrInfo.apCorrFlagKey, True)
254  continue
255 
256  for source in catalog:
257  center = source.getCentroid()
258  # say we've failed when we start; we'll unset these flags when we succeed
259  source.set(apCorrInfo.apCorrFlagKey, True)
260  oldFluxFlagState = False
261  if self.config.doFlagApCorrFailures:
262  oldFluxFlagState = source.get(apCorrInfo.fluxFlagKey)
263  source.set(apCorrInfo.fluxFlagKey, True)
264 
265  apCorr = 1.0
266  apCorrErr = 0.0
267  try:
268  apCorr = apCorrModel.evaluate(center)
269  if not UseNaiveFluxErr:
270  apCorrErr = apCorrErrModel.evaluate(center)
272  continue
273 
274  if apCorrInfo.doApCorrColumn:
275  source.set(apCorrInfo.apCorrKey, apCorr)
276  source.set(apCorrInfo.apCorrErrKey, apCorrErr)
277 
278  if apCorr <= 0.0 or apCorrErr < 0.0:
279  continue
280 
281  instFlux = source.get(apCorrInfo.instFluxKey)
282  instFluxErr = source.get(apCorrInfo.instFluxErrKey)
283  source.set(apCorrInfo.instFluxKey, instFlux*apCorr)
284  if UseNaiveFluxErr:
285  source.set(apCorrInfo.instFluxErrKey, instFluxErr*apCorr)
286  else:
287  a = instFluxErr/instFlux
288  b = apCorrErr/apCorr
289  source.set(apCorrInfo.instFluxErrKey, abs(instFlux*apCorr)*math.sqrt(a*a + b*b))
290  source.set(apCorrInfo.apCorrFlagKey, False)
291  if self.config.doFlagApCorrFailures:
292  source.set(apCorrInfo.fluxFlagKey, oldFluxFlagState)
293 
294  if self.log.getLevel() <= self.log.DEBUG:
295  # log statistics on the effects of aperture correction
296  apCorrArr = np.array([s.get(apCorrInfo.apCorrKey) for s in catalog])
297  apCorrErrArr = np.array([s.get(apCorrInfo.apCorrErrKey) for s in catalog])
298  self.log.debug("For instFlux field %r: mean apCorr=%s, stdDev apCorr=%s, "
299  "mean apCorrErr=%s, stdDev apCorrErr=%s for %s sources",
300  apCorrInfo.name, apCorrArr.mean(), apCorrArr.std(),
301  apCorrErrArr.mean(), apCorrErrArr.std(), len(catalog))
lsst::afw::image
lsst::meas::base.applyApCorr.ApCorrInfo.modelSigmaName
modelSigmaName
Definition: applyApCorr.py:81
lsst::meas::base.applyApCorr.ApCorrInfo.apCorrErrKey
apCorrErrKey
Definition: applyApCorr.py:115
lsst::meas::base.applyApCorr.ApCorrInfo
Definition: applyApCorr.py:41
lsst::meas::base.applyApCorr.ApplyApCorrConfig
Definition: applyApCorr.py:162
lsst::meas::base.applyApCorr.ApCorrInfo.fluxFlagKey
fluxFlagKey
Definition: applyApCorr.py:107
lsst::meas::base.applyApCorr.ApCorrInfo.instFluxErrKey
instFluxErrKey
Definition: applyApCorr.py:103
lsst::meas::base.applyApCorr.ApplyApCorrTask.__init__
def __init__(self, schema, **kwds)
Definition: applyApCorr.py:198
lsst::meas::base.applyApCorr.ApCorrInfo.instFluxErrName
instFluxErrName
Definition: applyApCorr.py:95
lsst::pex::exceptions::DomainError
lsst::meas::base.applyApCorr.ApplyApCorrTask
Definition: applyApCorr.py:188
lsst::meas::base.applyApCorr.ApCorrInfo.modelName
modelName
Definition: applyApCorr.py:77
lsst::pex::config
lsst::meas::base.applyApCorr.ApCorrInfo.__init__
def __init__(self, schema, model, name=None)
Definition: applyApCorr.py:123
lsst::meas::base.applyApCorr.ApCorrInfo.apCorrKey
apCorrKey
Definition: applyApCorr.py:111
lsst::meas::base.applyApCorr.ApCorrInfo.instFluxName
instFluxName
Definition: applyApCorr.py:91
lsst::meas::base.apCorrRegistry.getApCorrNameSet
def getApCorrNameSet()
Definition: apCorrRegistry.py:51
lsst::meas::base.applyApCorr.ApplyApCorrTask.apCorrInfoDict
apCorrInfoDict
Definition: applyApCorr.py:201
lsst::meas::base.applyApCorr.ApplyApCorrTask.run
def run(self, catalog, apCorrMap)
Definition: applyApCorr.py:223
lsst::meas::base.applyApCorr.ApCorrInfo.apCorrFlagKey
apCorrFlagKey
Definition: applyApCorr.py:119
lsst::pex::exceptions
lsst::meas::base.applyApCorr.ApCorrInfo.name
name
Definition: applyApCorr.py:73
lsst::meas::base.applyApCorr.ApCorrInfo.instFluxKey
instFluxKey
Definition: applyApCorr.py:99
lsst::meas::base.applyApCorr.ApCorrInfo.doApCorrColumn
doApCorrColumn
Definition: applyApCorr.py:85