lsst.meas.modelfit  14.0-1-g2fa83af+31
psfContinued.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # LSST Data Management System
4 # Copyright 2008-2014 LSST Corporation.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
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 LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <http://www.lsstcorp.org/LegalNotices/>.
22 #
23 
24 from __future__ import absolute_import, division, print_function
25 
26 # all new classes here are accessed via registries, not direct imports.
27 __all__ = (
28  "GeneralPsfFitterComponentConfig",
29  "GeneralPsfFitterConfig"
30 )
31 
32 import lsst.pex.config
33 import lsst.meas.base
34 from .psf import (
35  GeneralPsfFitterControl, GeneralPsfFitterComponentControl,
36  GeneralPsfFitter, GeneralPsfFitterAlgorithm,
37  DoubleShapeletPsfApproxAlgorithm, DoubleShapeletPsfApproxControl
38 )
39 
40 
41 lsst.meas.base.wrapSimpleAlgorithm(
42  DoubleShapeletPsfApproxAlgorithm,
43  Control=DoubleShapeletPsfApproxControl,
44  module='lsst.meas.modelfit',
45  name='modelfit_DoubleShapeletPsfApprox',
46  executionOrder=lsst.meas.base.BasePlugin.SHAPE_ORDER
47 )
48 
49 
50 GeneralPsfFitterComponentConfig = lsst.pex.config.makeConfigClass(
51  GeneralPsfFitterComponentControl,
52  module='lsst.meas.modelfit'
53 )
54 GeneralPsfFitterConfig = lsst.pex.config.makeConfigClass(
55  GeneralPsfFitterControl,
56  module='lsst.meas.modelfit'
57 )
58 GeneralPsfFitter.ConfigClass = GeneralPsfFitterConfig
59 
60 
61 class GeneralShapeletPsfApproxConfig(lsst.pex.config.Config):
62  models = lsst.pex.config.ConfigDictField(
63  keytype=str,
64  itemtype=GeneralPsfFitterConfig,
65  doc="a dictionary of models that can be used to fit the PSF",
66  default={} # populated in setDefaults; can't do it on a single line
67  )
68  sequence = lsst.pex.config.ListField(
69  dtype=str,
70  doc=("a sequence of model names indicating which models should be fit,"
71  " and their order"),
72  default=["DoubleShapelet"]
73  )
74 
75  def setDefaults(self):
76  super(GeneralShapeletPsfApproxConfig, self).setDefaults()
77  self.models["SingleGaussian"] = GeneralPsfFitterConfig()
78  self.models["SingleGaussian"].inner.order = -1
79  self.models["SingleGaussian"].primary.order = 0
80  self.models["SingleGaussian"].wings.order = -1
81  self.models["SingleGaussian"].outer.order = -1
82  self.models["DoubleGaussian"] = GeneralPsfFitterConfig()
83  self.models["DoubleGaussian"].inner.order = -1
84  self.models["DoubleGaussian"].primary.order = 0
85  self.models["DoubleGaussian"].wings.order = 0
86  self.models["DoubleGaussian"].outer.order = -1
87  self.models["DoubleShapelet"] = GeneralPsfFitterConfig()
88  self.models["DoubleShapelet"].inner.order = -1
89  self.models["DoubleShapelet"].primary.order = 2
90  self.models["DoubleShapelet"].wings.order = 1
91  self.models["DoubleShapelet"].outer.order = -1
92  self.models["Full"] = GeneralPsfFitterConfig()
93  self.models["Full"].inner.order = 0
94  self.models["Full"].primary.order = 4
95  self.models["Full"].wings.order = 4
96  self.models["Full"].outer.order = 0
97 
98  def validate(self):
99  super(GeneralShapeletPsfApproxConfig, self).validate()
100  if len(self.sequence) < 1:
101  raise ValueError("sequence must have at least one element")
102  for m in self.sequence:
103  if m not in self.models:
104  raise KeyError(
105  "All elements in sequence must be keys in models dict"
106  )
107 
108 
110  """Mixin base class for fitting shapelet approximations to the PSF model
111 
112  This class does almost all of the work for its two derived classes,
113  GeneralShapeletPsfApproxSingleFramePlugin and
114  GeneralShapeletPsfApproxForcedPlugin, which simply adapt it to the
115  slightly different interfaces for single-frame and forced measurement. It
116  in turn delegates its work to the C++ GeneralPsfFitter class; it holds
117  sequence of these corresponding to different models (generally with
118  increasing complexity). Each GeneralPsfFitter starts with the result of
119  the previous one as an input, using GeneralPsfFitter::adapt to hopefully
120  allow these previous fits to reduce the time spent on the next one.
121 
122  At present, this plugin does not define any failure flags, which will
123  almost certainly have to be changed in the future. So far, however, I
124  haven't actually seen it fail on any PSFs I've given it, so I'll wait
125  until we can run on large enough data volumes to see what the actual
126  failure modes are, instead of trying to guess them in advance.
127  """
128 
129  def __init__(self, config, name, schema):
130  """Initialize the plugin, creating a sequence of GeneralPsfFitter
131  instances to do the fitting and MultiShapeletFunctionKey instances to
132  save the results to a record.
133  """
134  self.sequence = []
135  for m in config.sequence:
136  fitter = GeneralPsfFitterAlgorithm(
137  config.models[m].makeControl(),
138  schema,
139  schema[name][m].getPrefix()
140  )
141  self.sequence.append((fitter, schema[name][m].getPrefix()))
142 
143  def measure(self, measRecord, exposure):
144  """Fit the configured sequence of models the given Exposure's Psf, as
145  evaluated at measRecord.getCentroid(), then save the results to
146  measRecord.
147  """
148  if not exposure.hasPsf():
150  "GeneralShapeletPsfApprox requires Exposure to have a Psf")
151  psf = exposure.getPsf()
152  psfImage = psf.computeKernelImage(measRecord.getCentroid())
153  psfShape = psf.computeShape(measRecord.getCentroid())
154  lastError = None
155  lastModel = None
156  # Fit the first element in the sequence, using the PSFs moments to
157  # initialize the parameters For every other element in the fitting
158  # sequence, use the previous fit to initialize the parameters
159  for fitter, name in self.sequence:
160  try:
161  if lastModel is None:
162  fitter.measure(measRecord, psfImage, psfShape)
163  else:
164  fitter.measure(measRecord, psfImage,
165  fitter.adapt(lastResult, lastModel))
166  lastResult = measRecord.get(fitter.getKey())
167  lastModel = fitter.getModel()
168  except lsst.meas.base.baseMeasurement.FATAL_EXCEPTIONS:
169  raise
170  except lsst.meas.base.MeasurementError as error:
171  fitter.fail(measRecord, error.cpp)
172  lastError = error
173  except Exception as error:
174  fitter.fail(measRecord)
175  lastError = error
176  # When we are done with all the fitters, raise the last error if there
177  # was one. This gives the calling task a chance to do whatever it
178  # wants
179  if not lastError is None:
180  raise lastError
181 
182  # This plugin doesn't need to set a flag on fail, because it should have
183  # been done already by the individual fitters in the sequence
184  def fail(self, measRecord, error=None):
185  pass
186 
187 
188 class GeneralShapeletPsfApproxSingleFrameConfig(
189  lsst.meas.base.SingleFramePluginConfig,
190  GeneralShapeletPsfApproxConfig
191 ):
192 
193  def setDefaults(self):
194  lsst.meas.base.SingleFramePluginConfig.setDefaults(self)
195  GeneralShapeletPsfApproxConfig.setDefaults(self)
196 
197 
198 @lsst.meas.base.register("modelfit_GeneralShapeletPsfApprox")
200  lsst.meas.base.SingleFramePlugin,
201  GeneralShapeletPsfApproxMixin
202 ):
203  """Minimal subclass of GeneralShapeletPsfApproxMixin to conform to the
204  single-frame measurement API.
205 
206  This class simply provides __init__ and measure methods that matched the
207  SingleFramePlugin signatures and delegate to the
208  GeneralShapeletPsfApproxMixin's implementations.
209  """
210  ConfigClass = GeneralShapeletPsfApproxSingleFrameConfig
211 
212  @staticmethod
214  return 1.0
215 
216  def __init__(self, config, name, schema, metadata):
217  GeneralShapeletPsfApproxMixin.__init__(self, config, name, schema)
218  lsst.meas.base.SingleFramePlugin.__init__(self, config, name, schema,
219  metadata)
220 
221  def measure(self, measRecord, exposure):
222  GeneralShapeletPsfApproxMixin.measure(self, measRecord, exposure)
223 
224  def fail(self, measRecord, error=None):
225  GeneralShapeletPsfApproxMixin.fail(self, measRecord, error)
226 
227 
229  lsst.meas.base.ForcedPluginConfig,
230  GeneralShapeletPsfApproxConfig
231 ):
232 
233  def setDefaults(self):
234  lsst.meas.base.ForcedPluginConfig.setDefaults(self)
235  GeneralShapeletPsfApproxConfig.setDefaults(self)
236 
237 
238 @lsst.meas.base.register("modelfit_GeneralShapeletPsfApprox")
240  lsst.meas.base.ForcedPlugin,
241  GeneralShapeletPsfApproxMixin
242 ):
243  """Minimal subclass of GeneralShapeletPsfApproxMixin to conform to the
244  forced measurement API.
245 
246  This class simply provides __init__ and measure methods that matched the
247  ForcedPlugin signatures and delegate to the
248  GeneralShapeletPsfApproxMixin's implementations.
249  """
250  ConfigClass = GeneralShapeletPsfApproxForcedConfig
251 
252  @staticmethod
254  return 1.0
255 
256  def __init__(self, config, name, schemaMapper, metadata):
257  GeneralShapeletPsfApproxMixin.__init__(self, config, name,
258  schemaMapper.editOutputSchema())
259  lsst.meas.base.ForcedPlugin.__init__(self, config, name, schemaMapper,
260  metadata)
261 
262  def measure(self, measRecord, exposure, refRecord, refWcs):
263  GeneralShapeletPsfApproxMixin.measure(self, measRecord, exposure)
264 
265  def fail(self, measRecord, error=None):
266  GeneralShapeletPsfApproxMixin.fail(self, measRecord, error)
def __init__(self, config, name, schemaMapper, metadata)
def measure(self, measRecord, exposure, refRecord, refWcs)