LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
FittedPsf.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
25 #if 0
26 #include <valarray>
27 #include <fstream>
28 #include <CCfits/CCfits>
29 #endif
30 
35 #if 0
36 #include "Name.h"
37 #include "WlVersion.h"
38 #include "WriteParam.h"
39 #endif
40 
41 namespace lsst {
42 namespace meas {
43 namespace algorithms {
44 namespace shapelet {
45 
46  static DVector definePXY(
47  int order, double x, double xMin, double xMax)
48  {
49  DVector temp(order+1);
50  double newX = (2.*x-xMin-xMax)/(xMax-xMin);
51  temp[0] = 1.;
52  if(order>0) temp[1] = newX;
53  for(int i=2;i<=order;++i) {
54  temp[i] = ((2.*i-1.)*newX*temp[i-1] - (i-1.)*temp[i-2])/i;
55  }
56  return temp;
57  }
58 
59 #ifdef USE_TMV
60  static void setPRow(
61  int fitOrder, Position pos, const Bounds& bounds, DVectorView pRow)
62 #else
63  static void setPRow(
64  int fitOrder, Position pos, const Bounds& bounds, DVector& pRow)
65 #endif
66  {
67  Assert(int(pRow.size()) == (fitOrder+1)*(fitOrder+2)/2);
68  DVector px =
69  definePXY(fitOrder,pos.getX(),bounds.getXMin(),bounds.getXMax());
70  DVector py =
71  definePXY(fitOrder,pos.getY(),bounds.getYMin(),bounds.getYMax());
72  int pq = 0;
73  for(int n=0;n<=fitOrder;++n) {
74  for(int p=n,q=n-p;q<=n;--p,++q) {
75  Assert(pq < int(pRow.size()));
76  pRow(pq) = px[p]*py[q];
77  ++pq;
78  }
79  }
80  Assert(pq == int(pRow.size()));
81  }
82 
84  const std::vector<Position>& pos,
85  const std::vector<BVec>& psf,
86  const std::vector<double>& nu,
87  std::vector<long>& flags)
88  {
89  const int nStars = pos.size();
90  const int psfSize = (_psfOrder+1)*(_psfOrder+2)/2;
91  const double nSigmaClip = _params.read("fitpsf_nsigma_outlier",3);
92 
93  // This is an empirical fit to the chisq level that corresponds to
94  // a 3-sigma outlier for more than 1 dimension.
95  // I calculated the values up to n=100 and for n>30, they form a pretty
96  // good approximation to a straight line.
97  // This is almost certainly wrong for nSigmaClip != 3, so if we start
98  // choosing other values for nSigmaClip, it might be worth doing this
99  // right.
100  // That means calculating the 1-d critical value for the given nSigma.
101  // e.g. nSigma = 3 -> alpha = P(chisq > 9) = 0.0027.
102  // Then calculate the critical value of chisq for that alpha with
103  // the full degrees of freedom = psfSize
104  const double chisqLevel = 0.14*psfSize + 2.13;
105 
106  const double outlierThresh = nSigmaClip * nSigmaClip * chisqLevel;
107  dbg<<"outlierThresh = "<<outlierThresh<<std::endl;
108 
109  int nGoodPsf, nOutliers, dof;
110  double chisq;
111  do {
112 
113  // Calculate the average psf vector
114  _avePsf.reset(new DVector(psfSize));
115  Assert(psfSize == int(_avePsf->size()));
116  _avePsf->setZero();
117  nGoodPsf = 0;
118  for(int n=0;n<nStars;++n) if ( flags[n]==0 ) {
119  Assert(psf[n].getSigma() == _sigma);
120  *_avePsf += psf[n].vec();
121  ++nGoodPsf;
122  }
123  if (nGoodPsf == 0) {
124  dbg<<"ngoodpsf = 0 in FittedPsf::calculate\n";
125  throw ProcessingException("No good stars found for interpolation.");
126  }
127  *_avePsf /= double(nGoodPsf);
128 
129  // Rotate the vectors into their eigen directions.
130  // The matrix V is stored to let us get back to the original basis.
131  DMatrix mM(nGoodPsf,psfSize);
132  DDiagMatrix inverseSigma(nGoodPsf);
133  int i=0;
134  for(int n=0;n<nStars;++n) if ( flags[n]==0 ) {
135  Assert(int(psf[n].size()) == psfSize);
136  Assert(i < nGoodPsf);
137  mM.row(i) = psf[n].vec() - *_avePsf;
138  inverseSigma(i) = nu[n];
139  _bounds += pos[n];
140  ++i;
141  }
142  Assert(i == nGoodPsf);
143  xdbg<<"bounds = "<<_bounds<<std::endl;
144  mM = inverseSigma EIGEN_asDiag() * mM;
145 
146  int nPcaTot = std::min(nGoodPsf,psfSize);
147  DDiagMatrix mS(nPcaTot);
148 #ifdef USE_TMV
149  DMatrixView mU = mM.colRange(0,nPcaTot);
150  _mV.reset(new tmv::Matrix<double,tmv::RowMajor>(nPcaTot,psfSize));
151  if (nGoodPsf > psfSize) {
152  SV_Decompose(mU.view(),mS.view(),_mV->view(),true);
153  } else {
154  *_mV = mM;
155  SV_Decompose(_mV->transpose(),mS.view(),mU.transpose());
156  }
157  xdbg<<"In FittedPSF: SVD S = "<<mS.diag()<<std::endl;
158 #else
159  DMatrix mU(mM.TMV_colsize(),nPcaTot);
160  _mV_transpose.reset(new DMatrix(psfSize,nPcaTot));
161  if (nGoodPsf > psfSize) {
162  Eigen::JacobiSVD<DMatrix> svd(
163  TMV_colRange(mM,0,nPcaTot), Eigen::ComputeThinU | Eigen::ComputeThinV
164  );
165  mU = svd.matrixU();
166  mS = svd.singularValues();
167  *_mV_transpose = svd.matrixV().transpose();
168  } else {
169  Eigen::JacobiSVD<DMatrix> svd(
170  mM, Eigen::ComputeThinU | Eigen::ComputeThinV
171  );
172  mU = svd.matrixU();
173  mS = svd.singularValues();
174  *_mV_transpose = svd.matrixV().transpose();
175  }
176  xdbg<<"In FittedPSF: SVD S = "<<EIGEN_Transpose(mS)<<std::endl;
177 #endif
178  if (_params.keyExists("fitpsf_npca")) {
179  _nPca = _params["fitpsf_npca"];
180  dbg<<"npca = "<<_nPca<<" from parameter file\n";
181  } else {
182  double thresh = mS(0);
183  if (_params.keyExists("fitpsf_pca_thresh"))
184  thresh *= double(_params["fitpsf_pca_thresh"]);
185  else thresh *= std::numeric_limits<double>::epsilon();
186  dbg<<"thresh = "<<thresh<<std::endl;
187  for(_nPca=1;_nPca<int(mM.TMV_rowsize());++_nPca) {
188  if (mS(_nPca) < thresh) break;
189  }
190  dbg<<"npca = "<<_nPca<<std::endl;
191  }
192 #ifdef USE_TMV
193  mU.colRange(0,_nPca) *= mS.subDiagMatrix(0,_nPca);
194 #else
195  TMV_colRange(mU,0,_nPca) *= mS.TMV_subVector(0,_nPca).asDiagonal();
196 #endif
197  xdbg<<"After U *= S\n";
198  // U S = M(orig) * Vt
199 
200  while (nGoodPsf <= _fitSize && _fitSize > 1) {
201  --_fitOrder;
202  _fitSize = (_fitOrder+1)*(_fitOrder+2)/2;
203  dbg<<"Too few good stars... reducing order of fit to "<<
204  _fitOrder<<std::endl;
205  }
206  DMatrix mP(nGoodPsf,_fitSize);
207  mP.setZero();
208  i=0;
209  for(int n=0;n<nStars;++n) if ( flags[n]==0 ) {
210  xdbg<<"n = "<<n<<" / "<<nStars<<std::endl;
211 #ifdef USE_TMV
212  setPRow(_fitOrder,pos[n],_bounds,mP.row(i));
213 #else
214  DVector mProwi(mP.TMV_rowsize());
215  setPRow(_fitOrder,pos[n],_bounds,mProwi);
216  mP.row(i) = mProwi.transpose();
217 #endif
218  ++i;
219  }
220  Assert(i == nGoodPsf);
221  mP = inverseSigma EIGEN_asDiag() * mP;
222  xdbg<<"after mP = sigma * mP\n";
223 
224 #ifdef USE_TMV
225  _f.reset(new DMatrix(TMV_colRange(mU,0,_nPca)/mP));
226 #else
227  _f.reset(new DMatrix(_fitSize,_nPca));
228  *_f = mP.colPivHouseholderQr().solve(TMV_colRange(mU,0,_nPca));
229 #endif
230  xdbg<<"Done making FittedPSF\n";
231 
232  //
233  // Remove outliers from the fit using the empirical covariance matrix
234  // of the data with respect to the fitted values.
235  //
236  xdbg<<"Checking for outliers:\n";
237 
238  // Calculate the covariance matrix
239  DMatrix cov(psfSize,psfSize);
240  cov.setZero();
241  for(int n=0;n<nStars;++n) if ( flags[n]==0 ) {
242  const BVec& data = psf[n];
243  DVector fit(psfSize);
244  interpolateVector(pos[n],TMV_vview(fit));
245  DVector diff = data.vec() - fit;
246 #ifdef USE_TMV
247  cov += diff ^ diff;
248 #else
249  cov += diff * diff.transpose();
250 #endif
251  }
252 
253  chisq = (mP * *_f - TMV_colRange(mU,0,_nPca)).TMV_normSq();
254  dbg<<"chisq calculation #1 = "<<chisq<<std::endl;
255  dof = nGoodPsf - _fitSize;
256 
257  if (dof > 0) {
258  cov /= double(dof);
259  }
260 #ifdef USE_TMV
261  cov.divideUsing(tmv::SV);
262  cov.saveDiv();
263  cov.setDiv();
264  dbg<<"cov S = "<<cov.svd().getS().diag()<<std::endl;
265 #else
266  Eigen::JacobiSVD<DMatrix> cov_svd(cov, Eigen::ComputeThinU | Eigen::ComputeThinV);
267  dbg<<"cov S = "<<EIGEN_Transpose(cov_svd.singularValues())<<std::endl;
268 
269 #endif
270 
271  // Clip out 3 sigma outliers:
272  nOutliers = 0;
273  chisq = 0;
274  for(int n=0;n<nStars;++n) if ( flags[n]==0 ) {
275  const BVec& data = psf[n];
276  DVector fit(psfSize);
277  interpolateVector(pos[n],TMV_vview(fit));
278  DVector diff = data.vec() - fit;
279 #ifdef USE_TMV
280  double dev = diff * cov.inverse() * diff;
281 #else
282  DVector temp(psfSize);
283  temp = cov_svd.solve(diff);
284  double dev = (diff.transpose() * temp)(0,0);
285 #endif
286  chisq += dev;
287  if (dev > outlierThresh) {
288  xdbg<<"n = "<<n<<" is an outlier.\n";
289  xdbg<<"data = "<<data.vec()<<std::endl;
290  xdbg<<"fit = "<<fit<<std::endl;
291  xdbg<<"diff = "<<diff<<std::endl;
292 #ifdef USE_TMV
293  xdbg<<"diff/cov = "<<diff/cov<<std::endl;
294 #else
295  xdbg<<"diff/cov = "<<temp<<std::endl;
296 #endif
297  xdbg<<"dev = "<<dev<<std::endl;
298  ++nOutliers;
299  flags[n] |= PSF_INTERP_OUTLIER;
300  }
301  }
302  dbg<<"ngoodpsf = "<<nGoodPsf<<std::endl;
303  dbg<<"nOutliers = "<<nOutliers<<std::endl;
304  dbg<<"chisq calculation #2 = "<<chisq<<std::endl;
305 
306  } while (nOutliers > 0);
307  }
308 
310  _params(params), _psfOrder(_params.read<int>("psf_order")),
311  _fitOrder(_params.read<int>("fitpsf_order")),
312  _fitSize((_fitOrder+1)*(_fitOrder+2)/2)
313  { }
314 
316  {
317  DVector P(_fitSize);
318 #ifdef USE_TMV
319  setPRow(_fitOrder,pos,_bounds,P.view());
320  DVector b1 = P * (*_f);
321  double bi = b1 * _mV->col(i,0,_nPca);
322 #else
323  setPRow(_fitOrder,pos,_bounds,P);
324  DVector b1 = _f->transpose() * P;
325  double bi = EIGEN_ToScalar(
326  EIGEN_Transpose(b1) * _mV_transpose->TMV_rowpart(i,0,_nPca));
327 #endif
328  bi += (*_avePsf)(i);
329  return bi;
330  }
331 
333  {
334  DVector P(_fitSize);
335 #ifdef USE_TMV
336  setPRow(_fitOrder,pos,_bounds,P.view());
337  DVector b1 = P * (*_f);
338  b = b1 * _mV->rowRange(0,_nPca);
339 #else
340  setPRow(_fitOrder,pos,_bounds,P);
341  DVector b1 = _f->transpose() * P;
342  b = TMV_colRange(*_mV_transpose,0,_nPca) * b1;
343 #endif
344  b += *_avePsf;
345  }
346 
347 }}}}
Eigen::Block< DVector, Eigen::Dynamic, 1 > DVectorView
Definition: MyMatrix.h:130
#define TMV_vview(v)
Definition: MyMatrix.h:202
void calculate(const std::vector< Position > &pos, const std::vector< BVec > &psf, const std::vector< double > &nu, std::vector< long > &flags)
Definition: FittedPsf.cc:83
#define EIGEN_ToScalar(m)
Definition: MyMatrix.h:212
FittedPsf(const ConfigFile &params)
Definition: FittedPsf.cc:309
std::auto_ptr< DVector > _avePsf
Definition: FittedPsf.h:99
double interpolateSingleElement(Position pos, int i) const
Definition: FittedPsf.cc:315
#define TMV_normSq()
Definition: MyMatrix.h:186
bool keyExists(const std::string &key) const
Definition: ConfigFile.cc:139
#define TMV_colRange(m, j1, j2)
Definition: MyMatrix.h:203
Eigen::Block< DMatrix > DMatrixView
Definition: MyMatrix.h:133
double x
void interpolateVector(Position pos, DVectorView b) const
Definition: FittedPsf.cc:332
double chisq
#define EIGEN_asDiag()
Definition: MyMatrix.h:180
std::auto_ptr< DMatrix > _mV_transpose
Definition: FittedPsf.h:103
afw::table::Key< double > b
#define xdbg
Definition: dbg.h:70
#define dbg
Definition: dbg.h:69
#define EIGEN_Transpose(m)
Definition: MyMatrix.h:211
#define Assert(x)
Definition: dbg.h:73