LSST Applications g0f08755f38+82efc23009,g12f32b3c4e+e7bdf1200e,g1653933729+a8ce1bb630,g1a0ca8cf93+50eff2b06f,g28da252d5a+52db39f6a5,g2bbee38e9b+37c5a29d61,g2bc492864f+37c5a29d61,g2cdde0e794+c05ff076ad,g3156d2b45e+41e33cbcdc,g347aa1857d+37c5a29d61,g35bb328faa+a8ce1bb630,g3a166c0a6a+37c5a29d61,g3e281a1b8c+fb992f5633,g414038480c+7f03dfc1b0,g41af890bb2+11b950c980,g5fbc88fb19+17cd334064,g6b1c1869cb+12dd639c9a,g781aacb6e4+a8ce1bb630,g80478fca09+72e9651da0,g82479be7b0+04c31367b4,g858d7b2824+82efc23009,g9125e01d80+a8ce1bb630,g9726552aa6+8047e3811d,ga5288a1d22+e532dc0a0b,gae0086650b+a8ce1bb630,gb58c049af0+d64f4d3760,gc28159a63d+37c5a29d61,gcf0d15dbbd+2acd6d4d48,gd7358e8bfb+778a810b6e,gda3e153d99+82efc23009,gda6a2b7d83+2acd6d4d48,gdaeeff99f8+1711a396fd,ge2409df99d+6b12de1076,ge79ae78c31+37c5a29d61,gf0baf85859+d0a5978c5a,gf3967379c6+4954f8c433,gfb92a5be7c+82efc23009,gfec2e1e490+2aaed99252,w.2024.46
LSST Data Management Base Package
Loading...
Searching...
No Matches
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
30import lsst.pex.config
31import lsst.meas.base
32from .._modelfitLib import (
33 GeneralPsfFitterControl, GeneralPsfFitterComponentControl,
34 GeneralPsfFitter, GeneralPsfFitterAlgorithm,
35 DoubleShapeletPsfApproxAlgorithm, DoubleShapeletPsfApproxControl
36)
37
38
39lsst.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
48GeneralPsfFitterComponentConfig = lsst.pex.config.makeConfigClass(
49 GeneralPsfFitterComponentControl,
50 module='lsst.meas.modelfit'
51)
52GeneralPsfFitterConfig = lsst.pex.config.makeConfigClass(
53 GeneralPsfFitterControl,
54 module='lsst.meas.modelfit'
55)
56GeneralPsfFitter.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 )
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.sequence = []
133 for m in config.sequence:
134 fitter = GeneralPsfFitterAlgorithm(
135 config.models[m].makeControl(),
136 schema,
137 schema[name][m].getPrefix()
138 )
139 self.sequence.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.sequence:
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
187class 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