lsst.ip.diffim g1ab274d823+f0a0a53c05
Loading...
Searching...
No Matches
DipoleAlgorithms.cc
Go to the documentation of this file.
1/*
2 * LSST Data Management System
3 * Copyright 2008-2015 AURA/LSST
4 *
5 * This product includes software developed by the
6 * LSST Project (http://www.lsst.org/).
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the LSST License Statement and
19 * the GNU General Public License along with this program. If not,
20 * see <http://www.lsstcorp.org/LegalNotices/>.
21 */
22
26#include <iostream> // std::cout
27#include <algorithm> // std::sort
28#include <limits> // std::numeric_limits
29#include <cmath> // std::sqrt
30
31#if !defined(DOXYGEN)
32# include "Minuit2/FCNBase.h"
33# include "Minuit2/FunctionMinimum.h"
34# include "Minuit2/MnMigrad.h"
35# include "Minuit2/MnMinos.h"
36# include "Minuit2/MnPrint.h"
37#endif
38
39#include <memory>
40#include "lsst/pex/exceptions.h"
41#include "lsst/afw/image.h"
42#include "lsst/afw/detection.h"
43#include "lsst/afw/table.h"
44#include "lsst/afw/math.h"
45#include "lsst/geom.h"
47#include "ndarray/eigen.h"
48
51namespace afwImage = lsst::afw::image;
52namespace afwMath = lsst::afw::math;
53namespace geom = lsst::geom;
54
55namespace lsst { namespace ip { namespace diffim {
56
57namespace {
58meas::base::FlagDefinitionList dipoleFluxFlagDefinitions;
59}
60
61meas::base::FlagDefinition const DipoleFluxAlgorithm::FAILURE = dipoleFluxFlagDefinitions.addFailureFlag("general failure flag, set if anything went wrong");
62meas::base::FlagDefinition const DipoleFluxAlgorithm::POS_FLAG = dipoleFluxFlagDefinitions.add("pos_flag", "failure flag for positive, set if anything went wrong");
63meas::base::FlagDefinition const DipoleFluxAlgorithm::NEG_FLAG = dipoleFluxFlagDefinitions.add("neg_flag", "failure flag for negative, set if anything went wrong");
64
66 return dipoleFluxFlagDefinitions;
67}
68
69namespace {
70meas::base::FlagDefinitionList dipoleCentroidFlagDefinitions;
71}
72
73meas::base::FlagDefinition const DipoleCentroidAlgorithm::FAILURE = dipoleCentroidFlagDefinitions.addFailureFlag("general failure flag, set if anything went wrong");
74meas::base::FlagDefinition const DipoleCentroidAlgorithm::POS_FLAG = dipoleCentroidFlagDefinitions.add("pos_flag", "failure flag for positive, set if anything went wrong");
75meas::base::FlagDefinition const DipoleCentroidAlgorithm::NEG_FLAG = dipoleCentroidFlagDefinitions.add("neg_flag", "failure flag for negative, set if anything went wrong");
76
78 return dipoleCentroidFlagDefinitions;
79}
80
81 int const NEGCENTXPAR(0); // Parameter for the x-component of the negative lobe centroid
82 int const NEGCENTYPAR(1); // Parameter for the y-component of the negative lobe centroid
83 int const NEGFLUXPAR(2); // Parameter for the flux of the negative lobe
84 int const POSCENTXPAR(3); // Parameter for the x-component of the positive lobe centroid
85 int const POSCENTYPAR(4); // Parameter for the y-component of the positive lobe centroid
86 int const POSFLUXPAR(5); // Parameter for the flux of the positive lobe
87
88
92class MinimizeDipoleChi2 : public ROOT::Minuit2::FCNBase {
93public:
94 explicit MinimizeDipoleChi2(PsfDipoleFlux const& psfDipoleFlux,
96 afw::image::Exposure<float> const& exposure
97 ) : _errorDef(1.0),
98 _nPar(6),
99 _maxPix(1e4),
100 _bigChi2(1e10),
101 _psfDipoleFlux(psfDipoleFlux),
102 _source(source),
103 _exposure(exposure)
104 {}
105 double Up() const { return _errorDef; }
106 void setErrorDef(double def) { _errorDef = def; }
107 int getNpar() const { return _nPar; }
108 int getMaxPix() const { return _maxPix; }
109 void setMaxPix(int maxPix) { _maxPix = maxPix; }
110
111 // Evaluate our cost function (in this case chi^2)
112 virtual double operator()(std::vector<double> const & params) const {
113 double negCenterX = params[NEGCENTXPAR];
114 double negCenterY = params[NEGCENTYPAR];
115 double negFlux = params[NEGFLUXPAR];
116 double posCenterX = params[POSCENTXPAR];
117 double posCenterY = params[POSCENTYPAR];
118 double posFlux = params[POSFLUXPAR];
119
120 /* Restrict negative dipole to be negative; positive to be positive */
121 if ((negFlux > 0.0) || (posFlux < 0.0)) {
122 return _bigChi2;
123 }
124
125 std::pair<double,int> fit = _psfDipoleFlux.chi2(_source, _exposure, negCenterX, negCenterY, negFlux,
126 posCenterX, posCenterY, posFlux);
127 double chi2 = fit.first;
128 int nPix = fit.second;
129 if (nPix > _maxPix) {
130 return _bigChi2;
131 }
132
133 return chi2;
134 }
135
136private:
137 double _errorDef; // how much cost function has changed at the +- 1 error points
138 int _nPar; // number of parameters in the fit; hard coded for MinimizeDipoleChi2
139 int _maxPix; // maximum number of pixels that shoud be in the footprint;
140 // prevents too much centroid wander
141 double _bigChi2; // large value to tell fitter when it has gone into bad region of parameter space
142
143 PsfDipoleFlux const& _psfDipoleFlux;
144 afw::table::SourceRecord & _source;
145 afw::image::Exposure<float> const& _exposure;
146};
147
150 afw::image::Exposure<float> const& exposure,
151 double negCenterX, double negCenterY, double negFlux,
152 double posCenterX, double posCenterY, double posFlux
153) const {
154
155 geom::Point2D negCenter(negCenterX, negCenterY);
156 geom::Point2D posCenter(posCenterX, posCenterY);
157
158 std::shared_ptr<afw::detection::Footprint const> footprint = source.getFootprint();
159
160 /*
161 * Fit for the superposition of Psfs at the two centroids.
162 */
164 std::shared_ptr<afwImage::Image<afwMath::Kernel::Pixel>> negPsf = psf->computeImage(negCenter);
165 std::shared_ptr<afwImage::Image<afwMath::Kernel::Pixel>> posPsf = psf->computeImage(posCenter);
166
167 afwImage::Image<double> negModel(footprint->getBBox());
168 afwImage::Image<double> posModel(footprint->getBBox());
169 afwImage::Image<float> data(*(exposure.getMaskedImage().getImage()),footprint->getBBox());
171 footprint->getBBox());
172
173 geom::Box2I negPsfBBox = negPsf->getBBox();
174 geom::Box2I posPsfBBox = posPsf->getBBox();
175 geom::Box2I negModelBBox = negModel.getBBox();
176 geom::Box2I posModelBBox = posModel.getBBox();
177
178 // Portion of the negative Psf that overlaps the model
179 int negXmin = std::max(negPsfBBox.getMinX(), negModelBBox.getMinX());
180 int negYmin = std::max(negPsfBBox.getMinY(), negModelBBox.getMinY());
181 int negXmax = std::min(negPsfBBox.getMaxX(), negModelBBox.getMaxX());
182 int negYmax = std::min(negPsfBBox.getMaxY(), negModelBBox.getMaxY());
183 geom::Box2I negBBox = geom::Box2I(geom::Point2I(negXmin, negYmin),
184 geom::Point2I(negXmax, negYmax));
185 afwImage::Image<afwMath::Kernel::Pixel> negSubim(*negPsf, negBBox);
186 afwImage::Image<double> negModelSubim(negModel, negBBox);
187 negModelSubim += negSubim;
188
189 // Portion of the positive Psf that overlaps the model
190 int posXmin = std::max(posPsfBBox.getMinX(), posModelBBox.getMinX());
191 int posYmin = std::max(posPsfBBox.getMinY(), posModelBBox.getMinY());
192 int posXmax = std::min(posPsfBBox.getMaxX(), posModelBBox.getMaxX());
193 int posYmax = std::min(posPsfBBox.getMaxY(), posModelBBox.getMaxY());
194 geom::Box2I posBBox = geom::Box2I(geom::Point2I(posXmin, posYmin),
195 geom::Point2I(posXmax, posYmax));
196 afwImage::Image<afwMath::Kernel::Pixel> posSubim(*posPsf, posBBox);
197 afwImage::Image<double> posModelSubim(posModel, posBBox);
198 posModelSubim += posSubim;
199
200 negModel *= negFlux; // scale negative model to image
201 posModel *= posFlux; // scale positive model to image
202 afwImage::Image<double> residuals(negModel, true); // full model contains negative lobe...
203 residuals += posModel; // plus positive lobe...
204 residuals -= data; // minus the data...
205 residuals *= residuals; // squared...
206 residuals /= var; // divided by the variance : [(model-data)/sigma]**2
208 double chi2 = stats.getValue(afwMath::SUM);
209 int nPix = stats.getValue(afwMath::NPOINT);
210 return std::pair<double,int>(chi2, nPix);
211}
212
215 afw::image::Exposure<float> const & exposure
216) const {
217
219
220 std::shared_ptr<afw::detection::Footprint const> footprint = source.getFootprint();
221 if (!footprint) {
223 (boost::format("No footprint for source %d") % source.getId()).str());
224 }
225
226 afw::detection::PeakCatalog peakCatalog = afw::detection::PeakCatalog(footprint->getPeaks());
227
228 if (peakCatalog.size() == 0) {
230 (boost::format("No peak for source %d") % source.getId()).str());
231 }
232 else if (peakCatalog.size() == 1) {
233 // No deblending to do
234 return;
235 }
236
237 // For N>=2, just measure the brightest-positive and brightest-negative
238 // peaks. peakCatalog is automatically ordered by peak flux, with the most
239 // positive one (brightest) being first
240 afw::detection::PeakRecord const& positivePeak = peakCatalog.front();
241 afw::detection::PeakRecord const& negativePeak = peakCatalog.back();
242
243 // Set up fit parameters and param names
244 ROOT::Minuit2::MnUserParameters fitPar;
245
246 fitPar.Add((boost::format("P%d")%NEGCENTXPAR).str(), negativePeak.getFx(), _ctrl.stepSizeCoord);
247 fitPar.Add((boost::format("P%d")%NEGCENTYPAR).str(), negativePeak.getFy(), _ctrl.stepSizeCoord);
248 fitPar.Add((boost::format("P%d")%NEGFLUXPAR).str(), negativePeak.getPeakValue(), _ctrl.stepSizeFlux);
249 fitPar.Add((boost::format("P%d")%POSCENTXPAR).str(), positivePeak.getFx(), _ctrl.stepSizeCoord);
250 fitPar.Add((boost::format("P%d")%POSCENTYPAR).str(), positivePeak.getFy(), _ctrl.stepSizeCoord);
251 fitPar.Add((boost::format("P%d")%POSFLUXPAR).str(), positivePeak.getPeakValue(), _ctrl.stepSizeFlux);
252
253 // Create the minuit object that knows how to minimise our functor
254 //
255 MinimizeDipoleChi2 minimizerFunc(*this, source, exposure);
256 minimizerFunc.setErrorDef(_ctrl.errorDef);
257
258 //
259 // tell minuit about it
260 //
261 ROOT::Minuit2::MnMigrad migrad(minimizerFunc, fitPar);
262
263 //
264 // And let it loose
265 //
266 ROOT::Minuit2::FunctionMinimum min = migrad(_ctrl.maxFnCalls);
267
268 float minChi2 = min.Fval();
269 bool const isValid = min.IsValid() && std::isfinite(minChi2);
270
271 if (true || isValid) { // calculate coeffs even in minuit is unhappy
272
273 /* I need to call chi2 one more time to grab nPix to calculate chi2/dof.
274 Turns out that the Minuit operator method has to be const, and the
275 measurement _apply method has to be const, so I can't store nPix as a
276 private member variable anywhere. Consted into a corner.
277 */
278 std::pair<double,int> fit = chi2(source, exposure,
279 min.UserState().Value(NEGCENTXPAR),
280 min.UserState().Value(NEGCENTYPAR),
281 min.UserState().Value(NEGFLUXPAR),
282 min.UserState().Value(POSCENTXPAR),
283 min.UserState().Value(POSCENTYPAR),
284 min.UserState().Value(POSFLUXPAR));
285 double evalChi2 = fit.first;
286 int nPix = fit.second;
287
288 std::shared_ptr<geom::Point2D> minNegCentroid(new geom::Point2D(min.UserState().Value(NEGCENTXPAR),
289 min.UserState().Value(NEGCENTYPAR)));
290 source.set(getNegativeKeys().getInstFlux(), min.UserState().Value(NEGFLUXPAR));
291 source.set(getNegativeKeys().getInstFluxErr(), min.UserState().Error(NEGFLUXPAR));
292
293 std::shared_ptr<geom::Point2D> minPosCentroid(new geom::Point2D(min.UserState().Value(POSCENTXPAR),
294 min.UserState().Value(POSCENTYPAR)));
295 source.set(getPositiveKeys().getInstFlux(), min.UserState().Value(POSFLUXPAR));
296 source.set(getPositiveKeys().getInstFluxErr(), min.UserState().Error(POSFLUXPAR));
297
298 source.set(_chi2dofKey, evalChi2 / (nPix - minimizerFunc.getNpar()));
299 source.set(_negCentroid.getX(), minNegCentroid->getX());
300 source.set(_negCentroid.getY(), minNegCentroid->getY());
301 source.set(_posCentroid.getX(), minPosCentroid->getX());
302 source.set(_posCentroid.getY(), minPosCentroid->getY());
303 source.set(_avgCentroid.getX(), 0.5*(minNegCentroid->getX() + minPosCentroid->getX()));
304 source.set(_avgCentroid.getY(), 0.5*(minNegCentroid->getY() + minPosCentroid->getY()));
305
306 }
307}
308
310 _flagHandler.handleFailure(measRecord, error);
311}
312}}} // namespace lsst::ip::diffim
#define LSST_EXCEPT(type,...)
MaskedImageT getMaskedImage()
std::shared_ptr< lsst::afw::detection::Psf const > getPsf() const
MaskedImage< ImageT, MaskT, VarianceT > MaskedImageT
lsst::geom::Box2I getBBox(ImageOrigin origin=PARENT) const
VariancePtr getVariance() const
double getValue(Property const prop=NOTHING) const
size_type size() const
reference back() const
reference front() const
int getMinY() const noexcept
int getMinX() const noexcept
int getMaxX() const noexcept
int getMaxY() const noexcept
static meas::base::FlagDefinition const FAILURE
static meas::base::FlagDefinition const POS_FLAG
static meas::base::FlagDefinition const NEG_FLAG
static meas::base::FlagDefinitionList const & getFlagDefinitions()
static meas::base::FlagDefinitionList const & getFlagDefinitions()
ResultKey const & getPositiveKeys() const
Return the standard flux keys registered by this algorithm.
static meas::base::FlagDefinition const POS_FLAG
static meas::base::FlagDefinition const NEG_FLAG
ResultKey const & getNegativeKeys() const
static meas::base::FlagDefinition const FAILURE
Class to minimize PsfDipoleFlux; this is the object that Minuit minimizes.
virtual double operator()(std::vector< double > const &params) const
MinimizeDipoleChi2(PsfDipoleFlux const &psfDipoleFlux, afw::table::SourceRecord &source, afw::image::Exposure< float > const &exposure)
Implementation of Psf dipole flux.
std::pair< double, int > chi2(afw::table::SourceRecord &source, afw::image::Exposure< float > const &exposure, double negCenterX, double negCenterY, double negFlux, double posCenterX, double poCenterY, double posFlux) const
void measure(afw::table::SourceRecord &measRecord, afw::image::Exposure< float > const &exposure) const
void fail(afw::table::SourceRecord &measRecord, meas::base::MeasurementError *error=NULL) const
T isfinite(T... args)
T max(T... args)
T min(T... args)
afw::table::CatalogT< PeakRecord > PeakCatalog
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Point< double, 2 > Point2D
Point< int, 2 > Point2I
int const NEGFLUXPAR(2)
int const NEGCENTXPAR(0)
int const NEGCENTYPAR(1)
int const POSFLUXPAR(5)
int const POSCENTXPAR(3)
int const POSCENTYPAR(4)