LSST Applications g063fba187b+cac8b7c890,g0f08755f38+6aee506743,g1653933729+a8ce1bb630,g168dd56ebc+a8ce1bb630,g1a2382251a+b4475c5878,g1dcb35cd9c+8f9bc1652e,g20f6ffc8e0+6aee506743,g217e2c1bcf+73dee94bd0,g28da252d5a+1f19c529b9,g2bbee38e9b+3f2625acfc,g2bc492864f+3f2625acfc,g3156d2b45e+6e55a43351,g32e5bea42b+1bb94961c2,g347aa1857d+3f2625acfc,g35bb328faa+a8ce1bb630,g3a166c0a6a+3f2625acfc,g3e281a1b8c+c5dd892a6c,g3e8969e208+a8ce1bb630,g414038480c+5927e1bc1e,g41af890bb2+8a9e676b2a,g7af13505b9+809c143d88,g80478fca09+6ef8b1810f,g82479be7b0+f568feb641,g858d7b2824+6aee506743,g89c8672015+f4add4ffd5,g9125e01d80+a8ce1bb630,ga5288a1d22+2903d499ea,gb58c049af0+d64f4d3760,gc28159a63d+3f2625acfc,gcab2d0539d+b12535109e,gcf0d15dbbd+46a3f46ba9,gda6a2b7d83+46a3f46ba9,gdaeeff99f8+1711a396fd,ge79ae78c31+3f2625acfc,gef2f8181fd+0a71e47438,gf0baf85859+c1f95f4921,gfa517265be+6aee506743,gfa999e8aa5+17cd334064,w.2024.51
LSST Data Management Base Package
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 */
163 std::shared_ptr<afwDet::Psf const> psf = exposure.getPsf();
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());
170 afwImage::Image<afwImage::VariancePixel> var(*(exposure.getMaskedImage().getVariance()),
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
char * data
Definition BaseRecord.cc:61
int min
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
Record class that represents a peak in a Footprint.
Definition Peak.h:42
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition Exposure.h:72
lsst::geom::Box2I getBBox(ImageOrigin origin=PARENT) const
Definition ImageBase.h:445
A class to represent a 2-dimensional array of pixels.
Definition Image.h:51
A class to manipulate images, masks, and variance as a single object.
Definition MaskedImage.h:74
A class to evaluate image statistics.
Definition Statistics.h:222
size_type size() const
Return the number of elements in the catalog.
Definition Catalog.h:412
reference back() const
Return the last record.
Definition Catalog.h:460
reference front() const
Return the first record.
Definition Catalog.h:457
T Value
The data type for get and set.
Definition FunctorKey.h:77
Record class that contains measurements made on a single exposure.
Definition Source.h:78
An integer coordinate rectangle.
Definition Box.h:55
int getMinY() const noexcept
Definition Box.h:158
int getMinX() const noexcept
Definition Box.h:157
int getMaxX() const noexcept
Definition Box.h:161
int getMaxY() const noexcept
Definition Box.h:162
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)
float stepSizeCoord
"Default initial step size for coordinates in non-linear fitter" ;
double errorDef
"How many sigma the error bars of the non-linear fitter represent" ;
float stepSizeFlux
"Default initial step size for flux in non-linear fitter" ;
int maxFnCalls
"Maximum function calls for non-linear fitter; 0 = unlimited" ;
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
Called to measure a single child source in an image.
void fail(afw::table::SourceRecord &measRecord, meas::base::MeasurementError *error=NULL) const
Handle an exception thrown by the current algorithm by setting flags in the given record.
afw::table::Key< CentroidElement > getX() const
Return a Key for the x coordinate.
afw::table::Key< CentroidElement > getY() const
Return a Key for the y coordinate.
vector-type utility class to build a collection of FlagDefinitions
Definition FlagHandler.h:60
void handleFailure(afw::table::BaseRecord &record, MeasurementError const *error=nullptr) const
Handle an expected or unexpected Exception thrown by a measurement algorithm.
Exception to be thrown when a measurement algorithm experiences a known failure mode.
Definition exceptions.h:48
Reports errors that are due to events beyond the control of the program.
Definition Runtime.h:104
bool isValid
Definition fits.cc:404
T isfinite(T... args)
T max(T... args)
T min(T... args)
Definition __init__.py:1
afw::table::CatalogT< PeakRecord > PeakCatalog
Definition Peak.h:244
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Definition Statistics.h:361
@ SUM
find sum of pixels in the image
Definition Statistics.h:68
@ NPOINT
number of sample points
Definition Statistics.h:56
int const NEGFLUXPAR(2)
int const NEGCENTXPAR(0)
int const NEGCENTYPAR(1)
int const POSFLUXPAR(5)
int const POSCENTXPAR(3)
int const POSCENTYPAR(4)
Simple class used to define and document flags The name and doc constitute the identity of the FlagDe...
Definition FlagHandler.h:40
Key< int > psf
Definition Exposure.cc:65