LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
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 # all new classes here are accessed via registries, not direct imports.
25 __all__ = (
26  "GeneralPsfFitterComponentConfig",
27  "GeneralPsfFitterConfig"
28 )
29 
30 import lsst.pex.config
31 import lsst.meas.base
32 from .psf import (
33  GeneralPsfFitterControl, GeneralPsfFitterComponentControl,
34  GeneralPsfFitter, GeneralPsfFitterAlgorithm,
35  DoubleShapeletPsfApproxAlgorithm, DoubleShapeletPsfApproxControl
36 )
37 
38 
39 lsst.meas.base.wrapSimpleAlgorithm(
40  DoubleShapeletPsfApproxAlgorithm,
41  Control=DoubleShapeletPsfApproxControl,
42  module='lsst.meas.modelfit',
43  name='modelfit_DoubleShapeletPsfApprox',
44  executionOrder=lsst.meas.base.BasePlugin.SHAPE_ORDER
45 )
46 
47 
48 GeneralPsfFitterComponentConfig = lsst.pex.config.makeConfigClass(
49  GeneralPsfFitterComponentControl,
50  module='lsst.meas.modelfit'
51 )
52 GeneralPsfFitterConfig = lsst.pex.config.makeConfigClass(
53  GeneralPsfFitterControl,
54  module='lsst.meas.modelfit'
55 )
56 GeneralPsfFitter.ConfigClass = GeneralPsfFitterConfig
57 
58 
61  keytype=str,
62  itemtype=GeneralPsfFitterConfig,
63  doc="a dictionary of models that can be used to fit the PSF",
64  default={} # populated in setDefaults; can't do it on a single line
65  )
66  sequence = lsst.pex.config.ListField(
67  dtype=str,
68  doc=("a sequence of model names indicating which models should be fit,"
69  " and their order"),
70  default=["DoubleShapelet"]
71  )
72 
73  def setDefaults(self):
74  super(GeneralShapeletPsfApproxConfig, self).setDefaults()
75  self.models["SingleGaussian"] = GeneralPsfFitterConfig()
76  self.models["SingleGaussian"].inner.order = -1
77  self.models["SingleGaussian"].primary.order = 0
78  self.models["SingleGaussian"].wings.order = -1
79  self.models["SingleGaussian"].outer.order = -1
80  self.models["DoubleGaussian"] = GeneralPsfFitterConfig()
81  self.models["DoubleGaussian"].inner.order = -1
82  self.models["DoubleGaussian"].primary.order = 0
83  self.models["DoubleGaussian"].wings.order = 0
84  self.models["DoubleGaussian"].outer.order = -1
85  self.models["DoubleShapelet"] = GeneralPsfFitterConfig()
86  self.models["DoubleShapelet"].inner.order = -1
87  self.models["DoubleShapelet"].primary.order = 2
88  self.models["DoubleShapelet"].wings.order = 1
89  self.models["DoubleShapelet"].outer.order = -1
90  self.models["Full"] = GeneralPsfFitterConfig()
91  self.models["Full"].inner.order = 0
92  self.models["Full"].primary.order = 4
93  self.models["Full"].wings.order = 4
94  self.models["Full"].outer.order = 0
95 
96  def validate(self):
97  super(GeneralShapeletPsfApproxConfig, self).validate()
98  if len(self.sequence) < 1:
99  raise ValueError("sequence must have at least one element")
100  for m in self.sequence:
101  if m not in self.models:
102  raise KeyError(
103  "All elements in sequence must be keys in models dict"
104  )
105 
106 
108  """Mixin base class for fitting shapelet approximations to the PSF model
109 
110  This class does almost all of the work for its two derived classes,
111  GeneralShapeletPsfApproxSingleFramePlugin and
112  GeneralShapeletPsfApproxForcedPlugin, which simply adapt it to the
113  slightly different interfaces for single-frame and forced measurement. It
114  in turn delegates its work to the C++ GeneralPsfFitter class; it holds
115  sequence of these corresponding to different models (generally with
116  increasing complexity). Each GeneralPsfFitter starts with the result of
117  the previous one as an input, using GeneralPsfFitter::adapt to hopefully
118  allow these previous fits to reduce the time spent on the next one.
119 
120  At present, this plugin does not define any failure flags, which will
121  almost certainly have to be changed in the future. So far, however, I
122  haven't actually seen it fail on any PSFs I've given it, so I'll wait
123  until we can run on large enough data volumes to see what the actual
124  failure modes are, instead of trying to guess them in advance.
125  """
126 
127  def __init__(self, config, name, schema):
128  """Initialize the plugin, creating a sequence of GeneralPsfFitter
129  instances to do the fitting and MultiShapeletFunctionKey instances to
130  save the results to a record.
131  """
132  self.sequencesequence = []
133  for m in config.sequence:
134  fitter = GeneralPsfFitterAlgorithm(
135  config.models[m].makeControl(),
136  schema,
137  schema[name][m].getPrefix()
138  )
139  self.sequencesequence.append((fitter, schema[name][m].getPrefix()))
140 
141  def measure(self, measRecord, exposure):
142  """Fit the configured sequence of models the given Exposure's Psf, as
143  evaluated at measRecord.getCentroid(), then save the results to
144  measRecord.
145  """
146  if not exposure.hasPsf():
148  "GeneralShapeletPsfApprox requires Exposure to have a Psf")
149  psf = exposure.getPsf()
150  psfImage = psf.computeKernelImage(measRecord.getCentroid())
151  psfShape = psf.computeShape(measRecord.getCentroid())
152  lastError = None
153  lastModel = None
154  # Fit the first element in the sequence, using the PSFs moments to
155  # initialize the parameters For every other element in the fitting
156  # sequence, use the previous fit to initialize the parameters
157  lastResult = None
158  for fitter, name in self.sequencesequence:
159  try:
160  if lastModel is None:
161  fitter.measure(measRecord, psfImage, psfShape)
162  else:
163  fitter.measure(measRecord, psfImage,
164  fitter.adapt(lastResult, lastModel))
165  lastResult = measRecord.get(fitter.getKey())
166  lastModel = fitter.getModel()
167  except lsst.meas.base.baseMeasurement.FATAL_EXCEPTIONS:
168  raise
169  except lsst.meas.base.MeasurementError as error:
170  fitter.fail(measRecord, error.cpp)
171  lastError = error
172  except Exception as error:
173  fitter.fail(measRecord)
174  lastError = error
175  # When we are done with all the fitters, raise the last error if there
176  # was one. This gives the calling task a chance to do whatever it
177  # wants
178  if lastError is not None:
179  raise lastError
180 
181  # This plugin doesn't need to set a flag on fail, because it should have
182  # been done already by the individual fitters in the sequence
183  def fail(self, measRecord, error=None):
184  pass
185 
186 
187 class GeneralShapeletPsfApproxSingleFrameConfig(
189  GeneralShapeletPsfApproxConfig
190 ):
191 
192  def setDefaults(self):
193  lsst.meas.base.SingleFramePluginConfig.setDefaults(self)
194  GeneralShapeletPsfApproxConfig.setDefaults(self)
195 
196 
197 @lsst.meas.base.register("modelfit_GeneralShapeletPsfApprox")
200  GeneralShapeletPsfApproxMixin
201 ):
202  """Minimal subclass of GeneralShapeletPsfApproxMixin to conform to the
203  single-frame measurement API.
204 
205  This class simply provides __init__ and measure methods that matched the
206  SingleFramePlugin signatures and delegate to the
207  GeneralShapeletPsfApproxMixin's implementations.
208  """
209  ConfigClass = GeneralShapeletPsfApproxSingleFrameConfig
210 
211  @staticmethod
213  return 1.0
214 
215  def __init__(self, config, name, schema, metadata):
216  GeneralShapeletPsfApproxMixin.__init__(self, config, name, schema)
217  lsst.meas.base.SingleFramePlugin.__init__(self, config, name, schema,
218  metadata)
219 
220  def measure(self, measRecord, exposure):
221  GeneralShapeletPsfApproxMixin.measure(self, measRecord, exposure)
222 
223  def fail(self, measRecord, error=None):
224  GeneralShapeletPsfApproxMixin.fail(self, measRecord, error)
225 
226 
229  GeneralShapeletPsfApproxConfig
230 ):
231 
232  def setDefaults(self):
233  lsst.meas.base.ForcedPluginConfig.setDefaults(self)
234  GeneralShapeletPsfApproxConfig.setDefaults(self)
235 
236 
237 @lsst.meas.base.register("modelfit_GeneralShapeletPsfApprox")
240  GeneralShapeletPsfApproxMixin
241 ):
242  """Minimal subclass of GeneralShapeletPsfApproxMixin to conform to the
243  forced measurement API.
244 
245  This class simply provides __init__ and measure methods that matched the
246  ForcedPlugin signatures and delegate to the
247  GeneralShapeletPsfApproxMixin's implementations.
248  """
249  ConfigClass = GeneralShapeletPsfApproxForcedConfig
250 
251  @staticmethod
253  return 1.0
254 
255  def __init__(self, config, name, schemaMapper, metadata):
256  GeneralShapeletPsfApproxMixin.__init__(self, config, name,
257  schemaMapper.editOutputSchema())
258  lsst.meas.base.ForcedPlugin.__init__(self, config, name, schemaMapper,
259  metadata)
260 
261  def measure(self, measRecord, exposure, refRecord, refWcs):
262  GeneralShapeletPsfApproxMixin.measure(self, measRecord, exposure)
263 
264  def fail(self, measRecord, error=None):
265  GeneralShapeletPsfApproxMixin.fail(self, measRecord, error)
Exception to be thrown when a measurement algorithm experiences a fatal error.
Definition: exceptions.h:76
Exception to be thrown when a measurement algorithm experiences a known failure mode.
Definition: exceptions.h:48
def __init__(self, config, name, schemaMapper, metadata)
def measure(self, measRecord, exposure, refRecord, refWcs)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33