LSSTApplications  16.0-10-g4f78f78+16,16.0-10-gc1446dd+42,16.0-11-g09ed895+1,16.0-13-g7649090,16.0-14-g0a28612+1,16.0-14-g6c7ed55+16,16.0-15-ga29f190+1,16.0-16-g89065d4+14,16.0-16-gd8e3590+16,16.0-16-ge6a35c8+6,16.0-17-g7e0e4ff+10,16.0-17-ga3d2e9f,16.0-19-gb830ed4e+16,16.0-2-g0febb12+21,16.0-2-g9d5294e+61,16.0-2-ga8830df+5,16.0-24-gc1c7f52+9,16.0-25-g07af9f2+1,16.0-3-ge00e371+21,16.0-36-g07840cb1,16.0-4-g18f3627+5,16.0-4-g5f3a788+20,16.0-4-ga3eb747+10,16.0-4-gabf74b7+16,16.0-4-gade8416+9,16.0-4-gb13d127+5,16.0-5-g6a53317+21,16.0-5-gb3f8a4b+74,16.0-5-gef99c9f+12,16.0-6-g9321be7+4,16.0-6-gcbc7b31+22,16.0-6-gf49912c+16,16.0-63-gae20905ba,16.0-7-gd2eeba5+31,16.0-8-g21fd5fe+16,16.0-8-g3a9f023+12,16.0-8-g4734f7a,16.0-9-g85d1a16+16,16.0-9-gf5c1f43,master-g07ce7b41a7,w.2018.48
LSSTDataManagementBasePackage
Gtransfo.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 /*
3  * This file is part of jointcal.
4  *
5  * Developed for the LSST Data Management System.
6  * This product includes software developed by the LSST Project
7  * (https://www.lsst.org).
8  * See the COPYRIGHT file at the top-level directory of this distribution
9  * for details of code ownership.
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <https://www.gnu.org/licenses/>.
23  */
24 
25 #include <iostream>
26 #include <iomanip>
27 #include <iterator> /* for ostream_iterator */
28 #include <limits>
29 #include <cmath>
30 #include <fstream>
31 #include "assert.h"
32 #include <sstream>
33 
34 #include "Eigen/Core"
35 
36 #include "lsst/log/Log.h"
37 #include "lsst/afw/geom/Point.h"
38 #include "lsst/jointcal/Gtransfo.h"
39 #include "lsst/jointcal/Frame.h"
41 #include "lsst/pex/exceptions.h"
42 #include "Eigen/Cholesky"
43 
45 
46 using namespace std;
47 
48 namespace {
49 LOG_LOGGER _log = LOG_GET("jointcal.Gtransfo");
50 }
51 
52 namespace lsst {
53 namespace jointcal {
54 
56  const GtransfoPoly *shift = dynamic_cast<const GtransfoPoly *>(gtransfo);
57  if (shift == nullptr) return false;
58 
59  static const double eps = 1e-5;
60 
61  double dx = shift->coeff(0, 0, 0);
62  double dy = shift->coeff(0, 0, 1);
63 
64  static Point dumb(4000, 4000);
65  if (fabs(dx - int(floor(dx + 0.5))) < eps && fabs(dy - int(floor(dy + 0.5))) < eps &&
66  fabs(dumb.x + dx - shift->apply(dumb).x) < eps && fabs(dumb.y + dy - shift->apply(dumb).y) < eps)
67  return true;
68 
69  return false;
70 }
71 
72 /********* Gtransfo ***********************/
73 
74 Frame Gtransfo::apply(Frame const &inputframe, bool inscribed) const {
75  // 2 opposite corners
76  double xtmin1, xtmax1, ytmin1, ytmax1;
77  apply(inputframe.xMin, inputframe.yMin, xtmin1, ytmin1);
78  apply(inputframe.xMax, inputframe.yMax, xtmax1, ytmax1);
79  Frame fr1(std::min(xtmin1, xtmax1), std::min(ytmin1, ytmax1), std::max(xtmin1, xtmax1),
80  std::max(ytmin1, ytmax1));
81  // 2 other corners
82  double xtmin2, xtmax2, ytmin2, ytmax2;
83  apply(inputframe.xMin, inputframe.yMax, xtmin2, ytmax2);
84  apply(inputframe.xMax, inputframe.yMin, xtmax2, ytmin2);
85  Frame fr2(std::min(xtmin2, xtmax2), std::min(ytmin2, ytmax2), std::max(xtmin2, xtmax2),
86  std::max(ytmin2, ytmax2));
87 
88  if (inscribed) return fr1 * fr2;
89  return fr1 + fr2;
90 }
91 
92 std::unique_ptr<Gtransfo> Gtransfo::composeAndReduce(
93  Gtransfo const &) const { // by default no way to compose
94  return std::unique_ptr<Gtransfo>(nullptr);
95 }
96 
97 double Gtransfo::getJacobian(const double x, const double y) const {
98  double x2, y2;
99  double eps = x * 0.01;
100  if (eps == 0) eps = 0.01;
101  apply(x, y, x2, y2);
102  double dxdx, dydx;
103  apply(x + eps, y, dxdx, dydx);
104  dxdx -= x2;
105  dydx -= y2;
106  double dxdy, dydy;
107  apply(x, y + eps, dxdy, dydy);
108  dxdy -= x2;
109  dydy -= y2;
110  return ((dxdx * dydy - dxdy * dydx) / (eps * eps));
111 }
112 
116 void Gtransfo::computeDerivative(Point const &where, GtransfoLin &derivative, const double step) const {
117  double x = where.x;
118  double y = where.y;
119  double xp0, yp0;
120  apply(x, y, xp0, yp0);
121 
122  double xp, yp;
123  apply(x + step, y, xp, yp);
124  derivative.a11() = (xp - xp0) / step;
125  derivative.a21() = (yp - yp0) / step;
126  apply(x, y + step, xp, yp);
127  derivative.a12() = (xp - xp0) / step;
128  derivative.a22() = (yp - yp0) / step;
129  derivative.dx() = 0;
130  derivative.dy() = 0;
131 }
132 
133 GtransfoLin Gtransfo::linearApproximation(Point const &where, const double step) const {
134  Point outwhere = apply(where);
135  GtransfoLin der;
136  computeDerivative(where, der, step);
137  return GtransfoLinShift(outwhere.x, outwhere.y) * der * GtransfoLinShift(-where.x, -where.y);
138 }
139 
140 void Gtransfo::transformPosAndErrors(FatPoint const &in, FatPoint &out) const {
141  FatPoint res; // in case in and out are the same address...
142  res = apply(in);
143  GtransfoLin der;
144  // could save a call here, since Derivative needs the transform of where that we already have
145  // 0.01 may not be a very good idea in all cases. May be we should provide a way of altering that.
146  computeDerivative(in, der, 0.01);
147  double a11 = der.A11();
148  double a22 = der.A22();
149  double a21 = der.A21();
150  double a12 = der.A12();
151  res.vx = a11 * (a11 * in.vx + 2 * a12 * in.vxy) + a12 * a12 * in.vy;
152  res.vy = a21 * a21 * in.vx + a22 * a22 * in.vy + 2. * a21 * a22 * in.vxy;
153  res.vxy = a21 * a11 * in.vx + a22 * a12 * in.vy + (a21 * a12 + a11 * a22) * in.vxy;
154  out = res;
155 }
156 
157 void Gtransfo::transformErrors(Point const &where, const double *vIn, double *vOut) const {
158  GtransfoLin der;
159  computeDerivative(where, der, 0.01);
160  double a11 = der.A11();
161  double a22 = der.A22();
162  double a21 = der.A21();
163  double a12 = der.A12();
164 
165  /* (a11 a12) (vxx vxy)
166  M = ( ) and V = ( )
167  (a21 a22) (xvy vyy)
168 
169  Vxx = Vin[0], vyy = Vin[1], Vxy = Vin[2];
170  we want to compute M*V*tp(M)
171  A lin alg light package would be perfect...
172  */
173  int xx = 0;
174  int yy = 1;
175  int xy = 2;
176  // M*V :
177 
178  double b11 = a11 * vIn[xx] + a12 * vIn[xy];
179  double b22 = a21 * vIn[xy] + a22 * vIn[yy];
180  double b12 = a11 * vIn[xy] + a12 * vIn[yy];
181  double b21 = a21 * vIn[xx] + a22 * vIn[xy];
182 
183  // (M*V) * tp(M)
184 
185  vOut[xx] = b11 * a11 + b12 * a12;
186  vOut[xy] = b11 * a21 + b12 * a22;
187  vOut[yy] = b21 * a21 + b22 * a22;
188 }
189 
190 std::unique_ptr<Gtransfo> Gtransfo::roughInverse(const Frame &region) const {
191  // "in" and "out" refer to the inverse direction.
192  Point centerOut = region.getCenter();
193  Point centerIn = apply(centerOut);
194  GtransfoLin der;
195  computeDerivative(centerOut, der, std::sqrt(region.getArea()) / 5.);
196  der = der.inverted();
197  der = GtransfoLinShift(centerOut.x, centerOut.y) * der * GtransfoLinShift(-centerIn.x, -centerIn.y);
198  return std::unique_ptr<Gtransfo>(new GtransfoLin(der));
199 }
200 
201 /* implement one in Gtransfo, so that all derived
202  classes do not need to provide one... */
203 
204 /* the routines that follow are used for ea generic parameter
205  transformation serialization, used e.g. for fits. Enables
206  to manipulate transformation parameters as vectors.
207 */
208 
209 // not dummy : what it does is virtual because paramRef is virtual.
210 void Gtransfo::getParams(double *params) const {
211  int npar = getNpar();
212  for (int i = 0; i < npar; ++i) params[i] = paramRef(i);
213 }
214 
215 void Gtransfo::offsetParams(Eigen::VectorXd const &delta) {
216  int npar = getNpar();
217  for (int i = 0; i < npar; ++i) paramRef(i) += delta[i];
218 }
219 
220 double Gtransfo::paramRef(const int) const {
222  std::string("Gtransfo::paramRef should never be called "));
223 }
224 
225 double &Gtransfo::paramRef(const int) {
226  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, "Gtransfo::paramRef should never be called ");
227 }
228 
229 void Gtransfo::paramDerivatives(Point const &, double *, double *) const {
231  "Gtransfo::paramDerivatives() should never be called ");
232 }
233 
235  gtransfo.dump(stream);
236  return stream;
237 }
238 
239 void Gtransfo::write(const std::string &fileName) const {
240  ofstream s(fileName.c_str());
241  write(s);
242  bool ok = !s.fail();
243  s.close();
244  if (!ok)
246  "Gtransfo::write, something went wrong for file " + fileName);
247 }
248 
249 void Gtransfo::write(ostream &stream) const {
251  "Gtransfo::write(ostream), should never be called. MEans that it is missing in some "
252  "derived class ");
253 }
254 
255 /******************* GTransfoInverse ****************/
256 /* inverse transformation, solved by iterations. Before using
257  it (probably via Gtransfo::inverseTransfo), consider
258  seriously StarMatchList::inverseTransfo */
259 class GtransfoInverse : public Gtransfo {
260 private:
262  std::unique_ptr<Gtransfo> _roughInverse;
263  double precision2;
264 
265 public:
266  GtransfoInverse(const Gtransfo *direct, const double precision, const Frame &region);
267 
270  void apply(const double xIn, const double yIn, double &xOut, double &yOut) const;
271 
272  void dump(ostream &stream) const;
273 
274  double fit(StarMatchList const &starMatchList);
275 
276  virtual std::unique_ptr<Gtransfo> clone() const;
277 
279 
281  std::unique_ptr<Gtransfo> roughInverse(const Frame &) const { return _direct->clone(); }
282 
284  std::unique_ptr<Gtransfo> inverseTransfo(double, const Frame &) const { return _direct->clone(); }
285 
286  ~GtransfoInverse();
287 
288 private:
289  void operator=(GtransfoInverse const &);
290 };
291 
292 std::unique_ptr<Gtransfo> Gtransfo::inverseTransfo(const double precision, const Frame &region) const {
293  return std::unique_ptr<Gtransfo>(new GtransfoInverse(this, precision, region));
294 }
295 
296 GtransfoInverse::GtransfoInverse(const Gtransfo *direct, const double precision, const Frame &region) {
297  _direct = direct->clone();
298  _roughInverse = _direct->roughInverse(region);
299  precision2 = precision * precision;
300 }
301 
302 GtransfoInverse::GtransfoInverse(GtransfoInverse const &model) : Gtransfo() {
303  _direct = model._direct->clone();
304  _roughInverse = model._roughInverse->clone();
305  precision2 = model.precision2;
306 }
307 
309 
310 void GtransfoInverse::operator=(GtransfoInverse const &model) {
311  _direct = model._direct->clone();
312  _roughInverse = model._roughInverse->clone();
313  precision2 = model.precision2;
314 }
315 
316 void GtransfoInverse::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
317  Point in(xIn, yIn);
318  Point outGuess = _roughInverse->apply(in);
319  GtransfoLin directDer, reverseDer;
320  int loop = 0;
321  int maxloop = 20;
322  double move2;
323  do {
324  loop++;
325  Point inGuess = _direct->apply(outGuess);
326  _direct->computeDerivative(outGuess, directDer);
327  reverseDer = directDer.inverted();
328  double xShift, yShift;
329  reverseDer.apply(xIn - inGuess.x, yIn - inGuess.y, xShift, yShift);
330  outGuess.x += xShift;
331  outGuess.y += yShift;
332  move2 = xShift * xShift + yShift * yShift;
333  } while ((move2 > precision2) && (loop < maxloop));
334  if (loop == maxloop) LOGLS_WARN(_log, "Problems applying GtransfoInverse at " << in);
335  xOut = outGuess.x;
336  yOut = outGuess.y;
337 }
338 
340  stream << " GtransfoInverse of :" << endl << *_direct << endl;
341 }
342 
344  throw pexExcept::RuntimeError("Cannot fit a GtransfoInverse. Use StarMatchList::inverseTransfo instead.");
345 }
346 
348  return std::unique_ptr<Gtransfo>(new GtransfoInverse(*this));
349 }
350 
351 /************* GtransfoComposition **************/
352 
353 // This class was done to allow composition of Gtransfo's, without specifications of their types.
354 // does not need to be public. Invoked by gtransfoCompose(left,right)
355 
359 private:
360  std::unique_ptr<Gtransfo> _first, _second;
361 
362 public:
365 
367  void apply(const double xIn, const double yIn, double &xOut, double &yOut) const;
368  void dump(ostream &stream = cout) const;
369 
371  double fit(StarMatchList const &starMatchList);
372 
375 };
376 
378  _first = first.clone();
379  _second = second.clone();
380 }
381 
382 void GtransfoComposition::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
383  double xout, yout;
384  _first->apply(xIn, yIn, xout, yout);
385  _second->apply(xout, yout, xOut, yOut);
386 }
387 
389  _first->dump(stream);
390  _second->dump(stream);
391 }
392 
393 double GtransfoComposition::fit(StarMatchList const &starMatchList) {
394  /* fits only one of them. could check that first can actually be fitted... */
395  return _first->fit(starMatchList);
396 }
397 
399  return std::make_unique<GtransfoComposition>(*_second, *_first);
400 }
401 
403 
405  return left.clone();
406 }
407 
409  // Try to use the composeAndReduce method from left. If absent, Gtransfo::composeAndReduce returns NULL.
410  // composeAndReduce is non trivial for polynomials.
411  std::unique_ptr<Gtransfo> composition(left.composeAndReduce(right));
412  // composition == NULL means no reduction: just build a Composition that pipelines "left" and "right".
413  if (composition == nullptr)
414  return std::make_unique<GtransfoComposition>(left, right);
415  else
416  return composition;
417 }
418 
419 // just a speed up, to avoid useless numerical derivation.
420 void GtransfoIdentity::computeDerivative(Point const &, GtransfoLin &derivative, const double) const {
421  derivative = GtransfoLin();
422 }
423 
426  return result; // rely on default Gtransfolin constructor;
427 }
428 
430  return std::make_shared<ast::UnitMap>(2); // a GtransfoIdentity is identically ast::UnitMap(2)
431 }
432 
433 void GtransfoIdentity::write(ostream &stream) const { stream << "GtransfoIdentity 1" << endl; }
434 
436  int format;
437  stream >> format;
438  if (format != 1)
440  " GtransfoIdentity::read : format is not 1 ");
441 }
442 
443 /*************** GtransfoPoly **************************************/
444 
446 
447 GtransfoPoly::GtransfoPoly(const unsigned order) : _order(order) {
448  _nterms = (order + 1) * (order + 2) / 2;
449 
450  // allocate and fill coefficients
451  _coeffs.resize(2 * _nterms, 0.);
452  // the default is supposed to be the identity, (for order>=1).
453  if (_order >= 1) {
454  coeff(1, 0, 0) = 1;
455  coeff(0, 1, 1) = 1;
456  }
457 }
458 
459 //#ifdef TO_BE_FIXED
460 GtransfoPoly::GtransfoPoly(const Gtransfo *gtransfo, const Frame &frame, unsigned order, unsigned nPoint) {
461  StarMatchList sm;
462 
463  double step = std::sqrt(fabs(frame.getArea()) / double(nPoint));
464  for (double x = frame.xMin + step / 2; x <= frame.xMax; x += step)
465  for (double y = frame.yMin + step / 2; y <= frame.yMax; y += step) {
466  auto pix = std::make_shared<BaseStar>(x, y, 0, 0);
467  double xtr, ytr;
468  gtransfo->apply(x, y, xtr, ytr);
469  auto tp = std::make_shared<BaseStar>(xtr, ytr, 0, 0);
470  /* These are fake stars so no need to transform fake errors.
471  all errors (and weights) will be equal : */
472  sm.push_back(StarMatch(*pix, *tp, pix, tp));
473  }
474  GtransfoPoly ret(order);
475  ret.fit(sm);
476  *this = ret;
477 }
478 //#endif
479 
481  jointcal::Frame const &domain, unsigned const order, unsigned const nSteps) {
482  jointcal::StarMatchList starMatchList;
483  double xStart = domain.xMin;
484  double yStart = domain.yMin;
485  double xStep = domain.getWidth() / (nSteps + 1);
486  double yStep = domain.getHeight() / (nSteps + 1);
487  for (unsigned i = 0; i < nSteps; ++i) {
488  for (unsigned j = 0; j < nSteps; ++j) {
489  // TODO: once DM-4044 is done, we can remove the redundancy in `Point`/`Point2D` here
490  jointcal::Point in(xStart + i * xStep, yStart + j * yStep);
491  afw::geom::Point2D inAfw(in.x, in.y);
492  afw::geom::Point2D outAfw = transform->applyForward(inAfw);
493  jointcal::Point out(outAfw.getX(), outAfw.getY());
494  starMatchList.emplace_back(in, out, nullptr, nullptr);
495  }
496  }
497  GtransfoPoly poly(order);
498  poly.fit(starMatchList);
499  *this = poly;
500 }
501 
502 void GtransfoPoly::computeMonomials(double xIn, double yIn, double *monomial) const {
503  /* The ordering of monomials is implemented here.
504  You may not change it without updating the "mapping" routines
505  coeff(unsigned, unsigned, unsigned).
506  I (P.A.) did not find a clever way to loop over monomials.
507  Improvements welcome.
508  This routine is used also by the fit to fill monomials.
509  We could certainly be more elegant.
510  */
511 
512  double xx = 1;
513  for (unsigned ix = 0; ix <= _order; ++ix) {
514  double yy = 1;
515  unsigned k = ix * (ix + 1) / 2;
516  for (unsigned iy = 0; iy <= _order - ix; ++iy) {
517  monomial[k] = xx * yy;
518  yy *= yIn;
519  k += ix + iy + 2;
520  }
521  xx *= xIn;
522  }
523 }
524 
525 void GtransfoPoly::setOrder(const unsigned order) {
526  _order = order;
527  unsigned old_nterms = _nterms;
528  _nterms = (_order + 1) * (_order + 2) / 2;
529 
530  // temporarily save coefficients
531  vector<double> old_coeffs = _coeffs;
532  // reallocate enough size
533  _coeffs.resize(2 * _nterms);
534  // reassign to zero (this is necessary because ycoeffs
535  // are after xcoeffs and so their meaning changes
536  for (unsigned k = 0; k < _nterms; ++k) _coeffs[k] = 0;
537  // put back what we had before
538  unsigned kmax = min(old_nterms, _nterms);
539  for (unsigned k = 0; k < kmax; ++k) {
540  _coeffs[k] = old_coeffs[k]; // x terms
541  _coeffs[k + _nterms] = old_coeffs[k + old_nterms]; // y terms
542  }
543 }
544 
545 /* this is reasonably fast, when optimized */
546 void GtransfoPoly::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
547  /*
548  This routine computes the monomials only once for both
549  polynomials. This is why GtransfoPoly does not use an auxilary
550  class (such as PolyXY) to handle each polynomial.
551 
552  The code works even if &xIn == &xOut (or &yIn == &yOut)
553  It uses Variable Length Allocation (VLA) rather than a vector<double>
554  because allocating the later costs about 50 ns. All VLA uses are tagged.
555  */
556  double monomials[_nterms]; // this is VLA, which is (perhaps) not casher C++
557  computeMonomials(xIn, yIn, monomials);
558 
559  xOut = 0;
560  yOut = 0;
561  const double *c = &_coeffs[0];
562  const double *pm = &monomials[0];
563  // the ordering of the coefficients and the monomials are identical.
564  for (int k = _nterms; k--;) xOut += (*(pm++)) * (*(c++));
565  pm = &monomials[0];
566  for (int k = _nterms; k--;) yOut += (*(pm++)) * (*(c++));
567 }
568 
569 void GtransfoPoly::computeDerivative(Point const &where, GtransfoLin &derivative, const double step)
570  const { /* routine checked against numerical derivatives from Gtransfo::Derivative */
571  if (_order == 1) {
572  derivative = GtransfoLin(*this);
573  derivative.dx() = derivative.dy() = 0;
574  return;
575  }
576 
577  double dermx[2 * _nterms]; // VLA
578  double *dermy = dermx + _nterms;
579  double xin = where.x;
580  double yin = where.y;
581 
582  double xx = 1;
583  double xxm1 = 1; // xx^(ix-1)
584  for (unsigned ix = 0; ix <= _order; ++ix) {
585  unsigned k = (ix) * (ix + 1) / 2;
586  // iy = 0
587  dermx[k] = ix * xxm1;
588  dermy[k] = 0;
589  k += ix + 2;
590  double yym1 = 1; // yy^(iy-1)
591  for (unsigned iy = 1; iy <= _order - ix; ++iy) {
592  dermx[k] = ix * xxm1 * yym1 * yin;
593  dermy[k] = iy * xx * yym1;
594  yym1 *= yin;
595  k += ix + iy + 2;
596  }
597  xx *= xin;
598  if (ix >= 1) xxm1 *= xin;
599  }
600 
601  derivative.dx() = 0;
602  derivative.dy() = 0;
603 
604  const double *mx = &dermx[0];
605  const double *my = &dermy[0];
606  const double *c = &_coeffs[0];
607  // dx'
608  double a11 = 0, a12 = 0;
609  for (int k = _nterms; k--;) {
610  a11 += (*(mx++)) * (*c);
611  a12 += (*(my++)) * (*(c++));
612  }
613  derivative.a11() = a11;
614  derivative.a12() = a12;
615  // dy'
616  double a21 = 0, a22 = 0;
617  mx = &dermx[0];
618  my = &dermy[0];
619  for (int k = _nterms; k--;) {
620  a21 += (*(mx++)) * (*c);
621  a22 += (*(my++)) * (*(c++));
622  }
623  derivative.a21() = a21;
624  derivative.a22() = a22;
625 }
626 
628  /*
629  The results from this routine were compared to what comes out
630  from apply and transformErrors. The Derivative routine was
631  checked against numerical derivatives from
632  Gtransfo::Derivative. (P.A dec 2009).
633 
634  This routine could be made much simpler by calling apply and
635  Derivative (i.e. you just suppress it, and the fallback is the
636  generic version in Gtransfo). BTW, I checked that both routines
637  provide the same result. This version is however faster
638  (monomials get recycled).
639  */
640  double monomials[_nterms]; // VLA
641 
642  FatPoint res; // to store the result, because nothing forbids &in == &out.
643 
644  double dermx[2 * _nterms]; // monomials for derivative w.r.t. x (VLA)
645  double *dermy = dermx + _nterms; // same for y
646  double xin = in.x;
647  double yin = in.y;
648 
649  double xx = 1;
650  double xxm1 = 1; // xx^(ix-1)
651  for (unsigned ix = 0; ix <= _order; ++ix) {
652  unsigned k = (ix) * (ix + 1) / 2;
653  // iy = 0
654  dermx[k] = ix * xxm1;
655  dermy[k] = 0;
656  monomials[k] = xx;
657  k += ix + 2;
658  double yy = yin;
659  double yym1 = 1; // yy^(iy-1)
660  for (unsigned iy = 1; iy <= _order - ix; ++iy) {
661  monomials[k] = xx * yy;
662  dermx[k] = ix * xxm1 * yy;
663  dermy[k] = iy * xx * yym1;
664  yym1 *= yin;
665  yy *= yin;
666  k += ix + iy + 2;
667  }
668  xx *= xin;
669  if (ix >= 1) xxm1 *= xin;
670  }
671 
672  // output position
673  double xout = 0, yout = 0;
674  const double *c = &_coeffs[0];
675  const double *pm = &monomials[0];
676  for (int k = _nterms; k--;) xout += (*(pm++)) * (*(c++));
677  pm = &monomials[0];
678  for (int k = _nterms; k--;) yout += (*(pm++)) * (*(c++));
679  res.x = xout;
680  res.y = yout;
681 
682  // derivatives
683  c = &_coeffs[0];
684  const double *mx = &dermx[0];
685  const double *my = &dermy[0];
686  double a11 = 0, a12 = 0;
687  for (int k = _nterms; k--;) {
688  a11 += (*(mx++)) * (*c);
689  a12 += (*(my++)) * (*(c++));
690  }
691 
692  double a21 = 0, a22 = 0;
693  mx = &dermx[0];
694  my = &dermy[0];
695  for (int k = _nterms; k--;) {
696  a21 += (*(mx++)) * (*c);
697  a22 += (*(my++)) * (*(c++));
698  }
699 
700  // output co-variance
701  res.vx = a11 * (a11 * in.vx + 2 * a12 * in.vxy) + a12 * a12 * in.vy;
702  res.vy = a21 * a21 * in.vx + a22 * a22 * in.vy + 2. * a21 * a22 * in.vxy;
703  res.vxy = a21 * a11 * in.vx + a22 * a12 * in.vy + (a21 * a12 + a11 * a22) * in.vxy;
704  out = res;
705 }
706 
707 /* The coefficient ordering is defined both here *AND* in the
708  GtransfoPoly::apply, GtransfoPoly::Derivative, ... routines
709  Change all or none ! */
710 
711 double GtransfoPoly::coeff(const unsigned degX, const unsigned degY, const unsigned whichCoord) const {
712  assert((degX + degY <= _order) && whichCoord < 2);
713  /* this assertion above is enough to ensure that the index used just
714  below is within bounds since the reserved length is
715  2*_nterms=(order+1)*(order+2) */
716  return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
717 }
718 
719 double &GtransfoPoly::coeff(const unsigned degX, const unsigned degY, const unsigned whichCoord) {
720  assert((degX + degY <= _order) && whichCoord < 2);
721  return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
722 }
723 
724 double GtransfoPoly::coeffOrZero(const unsigned degX, const unsigned degY, const unsigned whichCoord) const {
725  // assert((degX+degY<=order) && whichCoord<2);
726  assert(whichCoord < 2);
727  if (degX + degY <= _order)
728  return _coeffs[(degX + degY) * (degX + degY + 1) / 2 + degY + whichCoord * _nterms];
729  return 0;
730 }
731 
732 /* parameter serialization for "virtual" fits */
733 double GtransfoPoly::paramRef(const int i) const {
734  assert(unsigned(i) < 2 * _nterms);
735  return _coeffs[i];
736 }
737 
738 double &GtransfoPoly::paramRef(const int i) {
739  assert(unsigned(i) < 2 * _nterms);
740  return _coeffs[i];
741 }
742 
743 void GtransfoPoly::paramDerivatives(Point const &where, double *dx, double *dy)
744  const { /* first half : dxout/dpar, second half : dyout/dpar */
745  computeMonomials(where.x, where.y, dx);
746  for (unsigned k = 0; k < _nterms; ++k) {
747  dy[_nterms + k] = dx[k];
748  dx[_nterms + k] = dy[k] = 0;
749  }
750 }
751 
752 /* utility for the dump(ostream&) routine */
753 static string monomialString(const unsigned powX, const unsigned powY) {
754  stringstream ss;
755  if (powX + powY) ss << "*";
756  if (powX > 0) ss << "x";
757  if (powX > 1) ss << "^" << powX;
758  if (powY > 0) ss << "y";
759  if (powY > 1) ss << "^" << powY;
760  return ss.str();
761 }
762 
764  auto oldPrecision = stream.precision();
765  stream.precision(12);
766  for (unsigned ic = 0; ic < 2; ++ic) {
767  if (ic == 0)
768  stream << "newx = ";
769  else
770  stream << "newy = ";
771  for (unsigned p = 0; p <= _order; ++p)
772  for (unsigned py = 0; py <= p; ++py) {
773  if (p + py != 0) stream << " + ";
774  stream << coeff(p - py, py, ic) << monomialString(p - py, py);
775  }
776  stream << endl;
777  }
778  if (_order > 0) stream << " Linear determinant = " << determinant() << endl;
779  stream.precision(oldPrecision);
780 }
781 
783  if (_order >= 1) return coeff(1, 0, 0) * coeff(0, 1, 1) - coeff(0, 1, 0) * coeff(1, 0, 1);
784  return 0;
785 }
786 
789  Point center = frame.getCenter();
790  return GtransfoLinScale(2. / frame.getWidth(), 2. / frame.getHeight()) *
791  GtransfoLinShift(-center.x, -center.y);
792 }
793 
794 /*utility for the GtransfoPoly::fit() routine */
795 static GtransfoLin shiftAndNormalize(StarMatchList const &starMatchList) {
796  double xav = 0;
797  double x2 = 0;
798  double yav = 0;
799  double y2 = 0;
800  double count = 0;
801  for (auto it = starMatchList.begin(); it != starMatchList.end(); ++it) {
802  const StarMatch &a_match = *it;
803  Point const &point1 = a_match.point1;
804  xav += point1.x;
805  yav += point1.y;
806  x2 += std::pow(point1.x, 2);
807  y2 += std::pow(point1.y, 2);
808  count++;
809  }
810  if (count == 0) return GtransfoLin();
811  xav /= count;
812  yav /= count;
813  // 3.5 stands for sqrt(12).
814  double xspan = 3.5 * std::sqrt(x2 / count - std::pow(xav, 2));
815  double yspan = 3.5 * std::sqrt(y2 / count - std::pow(yav, 2));
816  return GtransfoLinScale(2. / xspan, 2. / yspan) * GtransfoLinShift(-xav, -yav);
817 }
818 
819 static double sq(double x) { return x * x; }
820 
821 double GtransfoPoly::computeFit(StarMatchList const &starMatchList, Gtransfo const &shiftToCenter,
822  const bool useErrors) {
823  Eigen::MatrixXd A(2 * _nterms, 2 * _nterms);
824  A.setZero();
825  Eigen::VectorXd B(2 * _nterms);
826  B.setZero();
827  double sumr2 = 0;
828  double monomials[_nterms];
829  for (auto it = starMatchList.begin(); it != starMatchList.end(); ++it) {
830  const StarMatch &a_match = *it;
831  Point tmp = shiftToCenter.apply(a_match.point1);
832  FatPoint point1(tmp, a_match.point1.vx, a_match.point1.vy, a_match.point1.vxy);
833  FatPoint const &point2 = a_match.point2;
834  double wxx, wyy, wxy;
835  FatPoint tr1;
836  computeMonomials(point1.x, point1.y, monomials);
837  if (useErrors) {
838  transformPosAndErrors(point1, tr1); // we might consider recycling the monomials
839  double vxx = (tr1.vx + point2.vx);
840  double vyy = (tr1.vy + point2.vy);
841  double vxy = (tr1.vxy + point2.vxy);
842  double det = vxx * vyy - vxy * vxy;
843  wxx = vyy / det;
844  wyy = vxx / det;
845  wxy = -vxy / det;
846  } else {
847  wxx = wyy = 1;
848  wxy = 0;
849  apply(point1.x, point1.y, tr1.x, tr1.y);
850  }
851  double resx = point2.x - tr1.x;
852  double resy = point2.y - tr1.y;
853  sumr2 += wxx * sq(resx) + wyy * sq(resy) + 2 * wxy * resx * resy;
854 
855  double bxcoeff = wxx * resx + wxy * resy;
856  double bycoeff = wyy * resy + wxy * resx;
857  for (unsigned j = 0; j < _nterms; ++j) {
858  for (unsigned i = j; i < _nterms; ++i) {
859  A(i, j) += wxx * monomials[i] * monomials[j];
860  A(i + _nterms, j + _nterms) += wyy * monomials[i] * monomials[j];
861  A(j, i + _nterms) = A(i, j + _nterms) += wxy * monomials[i] * monomials[j];
862  }
863  B(j) += bxcoeff * monomials[j];
864  B(j + _nterms) += bycoeff * monomials[j];
865  }
866  } // end loop on points
867  Eigen::LDLT<Eigen::MatrixXd, Eigen::Lower> factor(A);
868  // should probably throw
869  if (factor.info() != Eigen::Success) {
870  LOGL_ERROR(_log, "GtransfoPoly::fit could not factorize");
871  return -1;
872  }
873 
874  Eigen::VectorXd sol = factor.solve(B);
875  for (unsigned k = 0; k < 2 * _nterms; ++k) _coeffs[k] += sol(k);
876  if (starMatchList.size() == _nterms) return 0;
877  return (sumr2 - B.dot(sol));
878 }
879 
880 double GtransfoPoly::fit(StarMatchList const &starMatchList) {
881  if (starMatchList.size() < _nterms) {
882  LOGLS_FATAL(_log, "GtransfoPoly::fit trying to fit a polynomial transfo of order "
883  << _order << " with only " << starMatchList.size() << " matches.");
884  return -1;
885  }
886 
887  GtransfoPoly conditionner = shiftAndNormalize(starMatchList);
888 
889  computeFit(starMatchList, conditionner, false); // get a rough solution
890  computeFit(starMatchList, conditionner, true); // weight with it
891  double chi2 = computeFit(starMatchList, conditionner, true); // once more
892 
893  (*this) = (*this) * conditionner;
894  if (starMatchList.size() == _nterms) return 0;
895  return chi2;
896 }
897 
899  if (getOrder() == 1 && right.getOrder() == 1)
900  return std::make_unique<GtransfoLin>((*this) * (right)); // does the composition
901  else
902  return std::make_unique<GtransfoPoly>((*this) * (right)); // does the composition
903 }
904 
905 /* PolyXY the class used to perform polynomial algebra (and in
906  particular composition) at the coefficient level. This class
907  handles a single polynomial, while a GtransfoPoly is a couple of
908  polynomials. This class does not have any routine to evaluate
909  polynomials. Efficiency is not a concern since these routines are
910  seldom used. There is no need to expose this tool class to
911  Gtransfo users.
912 */
913 
914 class PolyXY {
915  unsigned order;
916  unsigned nterms;
917  vector<long double> coeffs;
918 
919 public:
920  PolyXY(const int order) : order(order), nterms((order + 1) * (order + 2) / 2) {
921  coeffs.reserve(nterms);
922  coeffs.insert(coeffs.begin(), nterms, 0L); // fill & initialize to 0.
923  }
924 
925  unsigned getOrder() const { return order; }
926 
927  PolyXY(GtransfoPoly const &gtransfoPoly, const unsigned whichCoord)
928  : order(gtransfoPoly.getOrder()), nterms((order + 1) * (order + 2) / 2), coeffs(nterms, 0L) {
929  for (unsigned px = 0; px <= order; ++px)
930  for (unsigned py = 0; py <= order - px; ++py)
931  coeff(px, py) = gtransfoPoly.coeff(px, py, whichCoord);
932  }
933 
934  long double coeff(const unsigned powX, const unsigned powY) const {
935  assert(powX + powY <= order);
936  return coeffs.at((powX + powY) * (powX + powY + 1) / 2 + powY);
937  }
938 
939  long double &coeff(const unsigned powX, const unsigned powY) {
940  assert(powX + powY <= order);
941  return coeffs.at((powX + powY) * (powX + powY + 1) / 2 + powY);
942  }
943 };
944 
945 /* ===================== PolyXY Algebra routines ================== */
946 
947 static void operator+=(PolyXY &left, const PolyXY &right) {
948  unsigned rdeg = right.getOrder();
949  assert(left.getOrder() >= rdeg);
950  for (unsigned i = 0; i <= rdeg; ++i)
951  for (unsigned j = 0; j <= rdeg - i; ++j) left.coeff(i, j) += right.coeff(i, j);
952 }
953 
954 /* multiplication by a scalar */
955 static PolyXY operator*(const long double &a, const PolyXY &polyXY) {
956  PolyXY result(polyXY);
957  // no direct access to coefficients: do it the soft way
958  unsigned order = polyXY.getOrder();
959  for (unsigned i = 0; i <= order; ++i)
960  for (unsigned j = 0; j <= order - i; ++j) result.coeff(i, j) *= a;
961  return result;
962 }
963 
965 static PolyXY product(const PolyXY &p1, const PolyXY &p2) {
966  unsigned deg1 = p1.getOrder();
967  unsigned deg2 = p2.getOrder();
968  PolyXY result(deg1 + deg2);
969  for (unsigned i1 = 0; i1 <= deg1; ++i1)
970  for (unsigned j1 = 0; j1 <= deg1 - i1; ++j1)
971  for (unsigned i2 = 0; i2 <= deg2; ++i2)
972  for (unsigned j2 = 0; j2 <= deg2 - i2; ++j2)
973  result.coeff(i1 + i2, j1 + j2) += p1.coeff(i1, j1) * p2.coeff(i2, j2);
974  return result;
975 }
976 
977 /* powers[k](x,y) = polyXY(x,y)**k, 0 <= k <= maxP */
978 static void computePowers(const PolyXY &polyXY, const unsigned maxP, vector<PolyXY> &powers) {
979  powers.reserve(maxP + 1);
980  powers.push_back(PolyXY(0));
981  powers[0].coeff(0, 0) = 1L;
982  for (unsigned k = 1; k <= maxP; ++k) powers.push_back(product(powers[k - 1], polyXY));
983 }
984 
986 static PolyXY composition(const PolyXY &polyXY, const PolyXY &polyX, const PolyXY &polyY) {
987  unsigned pdeg = polyXY.getOrder();
988  PolyXY result(pdeg * max(polyX.getOrder(), polyY.getOrder()));
989  vector<PolyXY> pXPowers;
990  vector<PolyXY> pYPowers;
991  computePowers(polyX, pdeg, pXPowers);
992  computePowers(polyY, pdeg, pYPowers);
993  for (unsigned px = 0; px <= pdeg; ++px)
994  for (unsigned py = 0; py <= pdeg - px; ++py)
995  result += polyXY.coeff(px, py) * product(pXPowers.at(px), pYPowers.at(py));
996  return result;
997 }
998 
999 /* ===================== end of PolyXY Algebra routines ============= */
1000 
1001 /* reducing polynomial composition is the reason for PolyXY stuff : */
1002 
1004  // split each transfo into 2d polynomials
1005  PolyXY plx(*this, 0);
1006  PolyXY ply(*this, 1);
1007  PolyXY prx(right, 0);
1008  PolyXY pry(right, 1);
1009 
1010  // compute the compositions
1011  PolyXY rx(composition(plx, prx, pry));
1012  PolyXY ry(composition(ply, prx, pry));
1013 
1014  // copy the results the hard way.
1015  GtransfoPoly result(_order * right._order);
1016  for (unsigned px = 0; px <= result._order; ++px)
1017  for (unsigned py = 0; py <= result._order - px; ++py) {
1018  result.coeff(px, py, 0) = rx.coeff(px, py);
1019  result.coeff(px, py, 1) = ry.coeff(px, py);
1020  }
1021  return result;
1022 }
1023 
1025  if (_order >= right._order) {
1026  GtransfoPoly res(*this);
1027  for (unsigned i = 0; i <= right._order; ++i)
1028  for (unsigned j = 0; j <= right._order - i; ++j) {
1029  res.coeff(i, j, 0) += right.coeff(i, j, 0);
1030  res.coeff(i, j, 1) += right.coeff(i, j, 1);
1031  }
1032  return res;
1033  } else
1034  return (right + (*this));
1035 }
1036 
1038  GtransfoPoly res(std::max(_order, right._order));
1039  for (unsigned i = 0; i <= res._order; ++i)
1040  for (unsigned j = 0; j <= res._order - i; ++j) {
1041  res.coeff(i, j, 0) = coeffOrZero(i, j, 0) - right.coeffOrZero(i, j, 0);
1042  res.coeff(i, j, 1) = coeffOrZero(i, j, 1) - right.coeffOrZero(i, j, 1);
1043  }
1044  return res;
1045 }
1046 
1048  auto inverse = inversePolyTransfo(*this, domain, 1e-7, _order + 2, 100);
1049  return std::make_shared<ast::PolyMap>(toAstPolyMapCoefficients(), inverse->toAstPolyMapCoefficients());
1050 }
1051 
1053  s << " GtransfoPoly 1" << endl;
1054  s << "order " << _order << endl;
1055  int oldprec = s.precision();
1056  s << setprecision(12);
1057  for (unsigned k = 0; k < 2 * _nterms; ++k) s << _coeffs[k] << ' ';
1058  s << endl;
1059  s << setprecision(oldprec);
1060 }
1061 
1063  int format;
1064  s >> format;
1065  if (format != 1)
1066  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, " GtransfoPoly::read : format is not 1 ");
1067 
1068  string order;
1069  s >> order >> _order;
1070  if (order != "order")
1072  " GtransfoPoly::read : expecting \"order\" and found " + order);
1073  setOrder(_order);
1074  for (unsigned k = 0; k < 2 * _nterms; ++k) s >> _coeffs[k];
1075 }
1076 
1077 ndarray::Array<double, 2, 2> GtransfoPoly::toAstPolyMapCoefficients() const {
1078  int nCoeffs = _coeffs.size();
1079  ndarray::Array<double, 2, 2> result = ndarray::allocate(ndarray::makeVector(nCoeffs, 4));
1080 
1081  ndarray::Size k = 0;
1082  for (unsigned iCoord = 0; iCoord < 2; ++iCoord) {
1083  for (unsigned p = 0; p <= _order; ++p) {
1084  for (unsigned py = 0; py <= p; ++py, ++k) {
1085  result[k][0] = coeff(p - py, py, iCoord);
1086  result[k][1] = iCoord + 1;
1087  result[k][2] = p - py;
1088  result[k][3] = py;
1089  }
1090  }
1091  }
1092 
1093  return result;
1094 }
1095 
1097  double const precision, int const maxOrder,
1098  unsigned const nSteps) {
1099  StarMatchList sm;
1100  double xStart = domain.xMin;
1101  double yStart = domain.yMin;
1102  double xStep = domain.getWidth() / (nSteps - 1);
1103  double yStep = domain.getHeight() / (nSteps - 1);
1104  for (unsigned i = 0; i < nSteps; ++i) {
1105  for (unsigned j = 0; j < nSteps; ++j) {
1106  Point in(xStart + i * xStep, yStart + j * yStep);
1107  Point out(forward.apply(in));
1108  sm.push_back(StarMatch(out, in, nullptr, nullptr));
1109  }
1110  }
1111  unsigned npairs = sm.size();
1112  int order;
1115  double chi2 = 0;
1116  double oldChi2 = std::numeric_limits<double>::infinity();
1117  for (order = 1; order <= maxOrder; ++order) {
1118  poly.reset(new GtransfoPoly(order));
1119  auto success = poly->fit(sm);
1120  if (success == -1) {
1121  std::stringstream errMsg;
1122  errMsg << "Cannot fit a polynomial of order " << order << " with " << nSteps << "^2 points";
1123  throw pexExcept::RuntimeError(errMsg.str());
1124  }
1125  // compute the chi2 ignoring errors:
1126  chi2 = 0;
1127  for (auto const &i : sm) chi2 += i.point2.computeDist2(poly->apply((i.point1)));
1128  LOGLS_TRACE(_log, "inversePoly order " << order << ": " << chi2 << " / " << npairs << " = "
1129  << chi2 / npairs << " < " << precision * precision);
1130 
1131  if (chi2 / npairs < precision * precision) break;
1132 
1133  // If this triggers, we know we did not reach the required precision.
1134  if (chi2 > oldChi2) {
1135  LOGLS_WARN(_log, "inversePolyTransfo: chi2 increases (" << chi2 << " > " << oldChi2
1136  << "); ending fit with order: " << order);
1137  LOGLS_WARN(_log, "inversePolyTransfo: requested precision not reached: "
1138  << chi2 << " / " << npairs << " = " << chi2 / npairs << " < "
1139  << precision * precision);
1140  poly = std::move(oldPoly);
1141  order--;
1142  break;
1143  } else {
1144  oldChi2 = chi2;
1145  // Clone it so we don't lose it in the next iteration.
1146  oldPoly = dynamic_pointer_cast<GtransfoPoly>(std::shared_ptr<Gtransfo>(poly->clone()));
1147  }
1148  }
1149  if (order > maxOrder)
1150  LOGLS_WARN(_log, "inversePolyTransfo: Reached max order without reaching requested precision: "
1151  << chi2 << " / " << npairs << " = " << chi2 / npairs << " < "
1152  << precision * precision);
1153  return poly;
1154 }
1155 
1156 /**************** GtransfoLin ***************************************/
1157 /* GtransfoLin is a specialized constructor of GtransfoPoly
1158  May be it could just disappear ??
1159 */
1160 
1161 GtransfoLin::GtransfoLin(const double Dx, const double Dy, const double A11, const double A12,
1162  const double A21, const double A22)
1163  : GtransfoPoly(1) {
1164  dx() = Dx;
1165  a11() = A11;
1166  a12() = A12;
1167  dy() = Dy;
1168  a21() = A21;
1169  a22() = A22;
1170 }
1171 
1173  if (gtransfoPoly.getOrder() != 1)
1175  "Trying to build a GtransfoLin from a higher order transfo. Aborting. ");
1176  (GtransfoPoly &)(*this) = gtransfoPoly;
1177 }
1178 
1180  // There is a general routine in GtransfoPoly that would do the job:
1181  // return GtransfoLin(GtransfoPoly::operator*(right));
1182  // however, we are using this composition of linear stuff heavily in
1183  // Gtransfo::linearApproximation, itself used in inverseTransfo::apply.
1184  // So, for sake of efficiency, and since it is easy, we take a shortcut:
1186  apply(right.Dx(), right.Dy(), result.dx(), result.dy());
1187  result.a11() = A11() * right.A11() + A12() * right.A21();
1188  result.a12() = A11() * right.A12() + A12() * right.A22();
1189  result.a21() = A21() * right.A11() + A22() * right.A21();
1190  result.a22() = A21() * right.A12() + A22() * right.A22();
1191  return result;
1192 }
1193 
1194 void GtransfoLin::computeDerivative(Point const &, GtransfoLin &derivative, const double) const {
1195  derivative = *this;
1196  derivative.coeff(0, 0, 0) = 0;
1197  derivative.coeff(0, 0, 1) = 0;
1198 }
1199 
1200 GtransfoLin GtransfoLin::linearApproximation(Point const &, const double) const { return *this; }
1201 
1203  //
1204  // (T1,M1) * (T2,M2) = 1 i.e (0,1) implies
1205  // T1 = -M1 * T2
1206  // M1 = M2^(-1)
1207  //
1208 
1209  double a11 = A11();
1210  double a12 = A12();
1211  double a21 = A21();
1212  double a22 = A22();
1213  double d = (a11 * a22 - a12 * a21);
1214  if (d == 0) {
1215  LOGL_FATAL(_log,
1216  "GtransfoLin::invert singular transformation: transfo contents will be dumped to stderr.");
1217  dump(cerr);
1218  }
1219 
1220  GtransfoLin result(0, 0, a22 / d, -a12 / d, -a21 / d, a11 / d);
1221  double rdx, rdy;
1222  result.apply(Dx(), Dy(), rdx, rdy);
1223  result.dx() = -rdx;
1224  result.dy() = -rdy;
1225  return result;
1226 }
1227 
1230 }
1231 
1233  throw pexExcept::NotFoundError("GTransfoLinRot::fit not implemented! aborting");
1234 }
1235 
1236 double GtransfoLinShift::fit(StarMatchList const &starMatchList) {
1237  int npairs = starMatchList.size();
1238  if (npairs < 3) {
1239  LOGLS_FATAL(_log, "GtransfoLinShift::fit trying to fit a linear transfo with only " << npairs
1240  << " matches.");
1241  return -1;
1242  }
1243 
1244  double sumr2 = 0; /* used to compute chi2 without relooping */
1245  /* loop on pairs and fill */
1246  Eigen::VectorXd B(2);
1247  B.setZero();
1248  Eigen::MatrixXd A(2, 2);
1249  A.setZero();
1250 
1251  for (auto const &it : starMatchList) {
1252  FatPoint const &point1 = it.point1;
1253  FatPoint const &point2 = it.point2;
1254  double deltax = point2.x - point1.x;
1255  double deltay = point2.y - point1.y;
1256  double vxx = point1.vx + point2.vx;
1257  double vyy = point1.vy + point2.vy;
1258  double vxy = point1.vxy + point2.vxy;
1259  double det = vxx * vyy - vxy * vxy;
1260  double wxx = vyy / det;
1261  double wyy = vxx / det;
1262  double wxy = -vxy / det;
1263  B(0) += deltax * wxx + wxy * deltay;
1264  B(1) += deltay * wyy + wxy * deltax;
1265  A(0, 0) += wxx;
1266  A(1, 1) += wyy;
1267  A(0, 1) += wxy;
1268  sumr2 += deltax * deltax * wxx + deltay * deltay * wyy + 2. * wxy * deltax * deltay;
1269  }
1270  double det = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
1271  if (det <= 0) return -1;
1272  double tmp = A(0, 0);
1273  A(0, 0) = A(1, 1) / det;
1274  A(1, 1) = tmp / det;
1275  A(0, 1) = A(1, 0) = -A(0, 1) / det;
1276  Eigen::VectorXd sol = A * B;
1277  (*this) = GtransfoLinShift(sol(0), sol(1));
1278  return (sumr2 - sol.dot(B)); // chi2 compact form
1279 }
1280 
1281 GtransfoLinRot::GtransfoLinRot(const double angleRad, const Point *center, const double scaleFactor) {
1282  double c = scaleFactor * std::cos(angleRad);
1283  double s = scaleFactor * std::sin(angleRad);
1284  a11() = a22() = c;
1285  a21() = s;
1286  a12() = -s;
1287 
1288  // we want that the center does not move : gtransfo+M*C = C ==> gtransfo = C - M*C
1289  Point a_point(0., 0.);
1290  if (center) a_point = *center;
1291 
1292  dx() = dy() = 0;
1293  GtransfoPoly::apply(a_point.x, a_point.y, dx(), dy()); // compute M*C
1294  dx() = a_point.x - Dx();
1295  dy() = a_point.y - dy();
1296 }
1297 
1298 static double deg2rad(double degree) { return degree * M_PI / 180.; }
1299 
1300 static double rad2deg(double rad) { return rad * 180. / M_PI; }
1301 
1302 /************* WCS transfo ******************/
1303 /************** LinPixelToTan *******************/
1304 
1305 /* Implementation note : it seemed wise to incorporate
1306  the radians to degreess convertion into the linPixelToTan
1307  part (right in the constructor), and to do the
1308  opposite operation in the LinPart routine.
1309  When I was coding the fit, I realized that it was a
1310  bad idea. then I realized that the fitting routine
1311  itself was probably useless. Finaly, for a persistor,
1312  it seems bizarre that the stored data is different
1313  from what convention (angles in degrees for WCS's)
1314  would expect.
1315  So, no more "automatic" degrees to radians and
1316  radians to degrees conversion. They are explicitely
1317  done in apply (for TanPixelToRaDec and TanRaDecToPixel).
1318  This is a minor concern though....
1319 */
1320 BaseTanWcs::BaseTanWcs(GtransfoLin const &pixToTan, Point const &tangentPoint,
1321  const GtransfoPoly *corrections) {
1322  // the angles returned by linPixelToTan should be in degrees.
1323  linPixelToTan = pixToTan;
1324  ra0 = deg2rad(tangentPoint.x);
1325  dec0 = deg2rad(tangentPoint.y);
1326  cos0 = std::cos(dec0);
1327  sin0 = std::sin(dec0);
1328  corr = nullptr;
1329  if (corrections) corr.reset(new GtransfoPoly(*corrections));
1330 }
1331 
1332 /* with some sort of smart pointer ro handle "corr", we could remove the
1333  copy constructor, the operator = and the destructor */
1334 
1335 // ": Gtransfo" suppresses a warning
1337  corr = nullptr;
1338  *this = original;
1339 }
1340 
1341 void BaseTanWcs::operator=(const BaseTanWcs &original) {
1342  linPixelToTan = original.linPixelToTan;
1343  ra0 = original.ra0;
1344  dec0 = original.dec0;
1345  cos0 = std::cos(dec0);
1346  sin0 = std::sin(dec0);
1347  corr = nullptr;
1348  if (original.corr) corr.reset(new GtransfoPoly(*original.corr));
1349 }
1350 
1351 void BaseTanWcs::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
1352  double l, m; // radians in the tangent plane
1353  pixToTangentPlane(xIn, yIn, l, m); // l, m in degrees.
1354  l = deg2rad(l);
1355  m = deg2rad(m); // now in radians
1356  // Code inspired from worldpos.c in wcssubs (ancestor of the wcslib)
1357  /* At variance with wcslib, it collapses the projection to a plane
1358  and expression of sidereal cooordinates into a single set of
1359  operations. */
1360  double dect = cos0 - m * sin0;
1361  if (dect == 0) {
1362  LOGL_WARN(_log, "No sidereal coordinates at pole!");
1363  xOut = 0;
1364  yOut = 0;
1365  return;
1366  }
1367  double rat = ra0 + atan2(l, dect);
1368  dect = atan(std::cos(rat - ra0) * (m * cos0 + sin0) / dect);
1369  if (rat - ra0 > M_PI) rat -= (2. * M_PI);
1370  if (rat - ra0 < -M_PI) rat += (2. * M_PI);
1371  if (rat < 0.0) rat += (2. * M_PI);
1372  // convert to degree
1373  xOut = rad2deg(rat);
1374  yOut = rad2deg(dect);
1375 }
1376 
1377 Point BaseTanWcs::getTangentPoint() const { return Point(rad2deg(ra0), rad2deg(dec0)); }
1378 
1380 
1382 
1384  /* CRPIX's are defined by:
1385  ( CD1_1 CD1_2 ) (x - crpix1)
1386  transformed = ( ) * ( )
1387  ( CD2_1 CD2_2 ) (y - crpix2)
1388 
1389  so that CrPix is the point which transforms to (0,0)
1390  */
1391  const GtransfoLin inverse = linPixelToTan.inverted();
1392  return Point(inverse.Dx(), inverse.Dy());
1393 }
1394 
1396 
1398 
1399 void GtransfoSkyWcs::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
1400  auto const outCoord = _skyWcs->pixelToSky(afw::geom::Point2D(xIn, yIn));
1401  xOut = outCoord[0].asDegrees();
1402  yOut = outCoord[1].asDegrees();
1403 }
1404 
1405 void GtransfoSkyWcs::dump(std::ostream &stream) const { stream << "GtransfoSkyWcs(" << *_skyWcs << ")"; }
1406 
1407 double GtransfoSkyWcs::fit(const StarMatchList &starMatchList) {
1408  throw LSST_EXCEPT(pex::exceptions::LogicError, "Not implemented");
1409 }
1410 
1413 }
1414 
1415 /*************************** TanPixelToRaDec ***************/
1416 
1417 TanPixelToRaDec::TanPixelToRaDec(GtransfoLin const &pixToTan, Point const &tangentPoint,
1418  const GtransfoPoly *corrections)
1419  : BaseTanWcs(pixToTan, tangentPoint, corrections) {}
1420 
1421 // ": Gtransfo" suppresses a warning
1423 
1425  if (right.getOrder() == 1) {
1426  return std::make_unique<TanPixelToRaDec>((*this) * (right));
1427  } else {
1428  return std::unique_ptr<Gtransfo>(nullptr);
1429  }
1430 }
1431 
1433  TanPixelToRaDec result(*this);
1434  result.linPixelToTan = result.linPixelToTan * right;
1435  return result;
1436 }
1437 
1439  if (corr != nullptr) {
1440  LOGL_WARN(_log, "You are inverting a TanPixelToRaDec with corrections.");
1441  LOGL_WARN(_log, "The inverse you get ignores the corrections!");
1442  }
1444 }
1445 
1448 }
1449 
1451  if (!corr)
1453  else
1454  return std::unique_ptr<Gtransfo>(new GtransfoInverse(this, precision, region));
1455 }
1456 
1458  if (corr)
1459  return (*corr) * linPixelToTan;
1460  else
1461  return linPixelToTan;
1462 }
1463 
1464 void TanPixelToRaDec::pixToTangentPlane(double xPixel, double yPixel, double &xTangentPlane,
1465  double &yTangentPlane) const {
1466  // xTangentPlane, yTangentPlane in degrees.
1467  linPixelToTan.apply(xPixel, yPixel, xTangentPlane, yTangentPlane);
1468  if (corr) {
1469  double xtmp = xTangentPlane;
1470  double ytmp = yTangentPlane;
1471  corr->apply(xtmp, ytmp, xTangentPlane, yTangentPlane); // still in degrees.
1472  }
1473 }
1474 
1477 }
1478 
1480  stream << " TanPixelToRaDec, lin part :" << endl << linPixelToTan;
1481  Point tp = getTangentPoint();
1482  stream << " tangent point " << tp.x << ' ' << tp.y << endl;
1483  Point crpix = getCrPix();
1484  stream << " crpix " << crpix.x << ' ' << crpix.y << endl;
1485  if (corr) stream << "PV correction: " << endl << *corr;
1486 }
1487 
1489  /* OK we could implement this routine, but it is
1490  probably useless since to do the match, we have to
1491  project from sky to tangent plane. When a match is
1492  found, it is easier to carry out the fit in the
1493  tangent plane, rather than going back to the celestial
1494  sphere (and reproject to fit...). Anyway if this
1495  message shows up, we'll think about it.
1496  */
1498  "TanPixelToRaDec::fit is NOT implemented (although it is doable)) ");
1499  return -1;
1500 }
1501 
1502 /*************************** TanSipPixelToRaDec ***************/
1503 
1504 TanSipPixelToRaDec::TanSipPixelToRaDec(GtransfoLin const &pixToTan, Point const &tangentPoint,
1505  const GtransfoPoly *corrections)
1506  : BaseTanWcs(pixToTan, tangentPoint, corrections) {}
1507 
1508 // ": Gtransfo" suppresses a warning
1510 
1511 /* Would require some checks before cooking up something more efficient
1512  than just a linear approximation */
1513 #if 0
1515 {
1516  if (&region) {}
1518 }
1519 #endif
1520 
1522  const { /* We have not implemented (yet) the reverse corrections available in SIP */
1523  return std::unique_ptr<Gtransfo>(new GtransfoInverse(this, precision, region));
1524 }
1525 
1527  if (corr)
1528  return GtransfoPoly(linPixelToTan) * (*corr);
1529  else
1530  return linPixelToTan;
1531 }
1532 
1533 void TanSipPixelToRaDec::pixToTangentPlane(double xPixel, double yPixel, double &xTangentPlane,
1534  double &yTangentPlane) const {
1535  // xTangentPlane, yTangentPlane returned in degrees
1536  if (corr) {
1537  double xtmp, ytmp;
1538  corr->apply(xPixel, yPixel, xtmp, ytmp);
1539  linPixelToTan.apply(xtmp, ytmp, xTangentPlane, yTangentPlane);
1540  } else
1541  linPixelToTan.apply(xPixel, yPixel, xTangentPlane, yTangentPlane);
1542 }
1543 
1546 }
1547 
1549  stream << " TanSipPixelToRaDec, lin part :" << endl << linPixelToTan;
1550  Point tp = getTangentPoint();
1551  stream << " tangent point " << tp.x << ' ' << tp.y << endl;
1552  Point crpix = getCrPix();
1553  stream << " crpix " << crpix.x << ' ' << crpix.y << endl;
1554  if (corr) stream << "PV correction: " << endl << *corr;
1555 }
1556 
1558  /* OK we could implement this routine, but it is
1559  probably useless since to do the match, we have to
1560  project from sky to tangent plane. When a match is
1561  found, it is easier to carry out the fit in the
1562  tangent plane, rather than going back to the celestial
1563  sphere (and reproject to fit...). Anyway if this
1564  message shows up, we'll think about it.
1565  */
1567  "TanSipPixelToRaDec::fit is NOT implemented (although it is doable)) ");
1568  return -1;
1569 }
1570 
1571 /*************** reverse transfo of TanPixelToRaDec: TanRaDecToPixel ********/
1572 
1573 TanRaDecToPixel::TanRaDecToPixel(GtransfoLin const &tan2Pix, Point const &tangentPoint)
1574  : linTan2Pix(tan2Pix) {
1575  setTangentPoint(tangentPoint);
1576 }
1577 
1578 void TanRaDecToPixel::setTangentPoint(Point const &tangentPoint) {
1579  /* the radian to degrees conversion after projection
1580  is handled in apply */
1581  ra0 = deg2rad(tangentPoint.x);
1582  dec0 = deg2rad(tangentPoint.y);
1583  cos0 = std::cos(dec0);
1584  sin0 = std::sin(dec0);
1585 }
1586 
1588  ra0 = dec0 = 0;
1589  cos0 = 1;
1590  sin0 = 0;
1591 }
1592 
1593 Point TanRaDecToPixel::getTangentPoint() const { return Point(rad2deg(ra0), rad2deg(dec0)); }
1594 
1595 GtransfoLin TanRaDecToPixel::getLinPart() const { return linTan2Pix; }
1596 
1597 // Use analytic derivatives, computed at the same time as the transform itself
1599  /* this routine is very similar to apply, but also propagates errors.
1600  The deg2rad and rad2deg are ignored for errors because they act as
1601  2 global scalings that cancel each other.
1602  Derivatives were computed using maple:
1603 
1604  l1 := sin(a - a0)*cos(d);
1605  m1 := sin(d)*sin(d0)+cos(d)*cos(d0)*cos(a-a0);
1606  l2 := sin(d)*cos(d0)-cos(d)*sin(d0)*cos(a-a0);
1607  simplify(diff(l1/m1,a));
1608  simplify(diff(l1/m1,d));
1609  simplify(diff(l2/m1,a));
1610  simplify(diff(l2/m1,d));
1611 
1612  Checked against Gtransfo::transformPosAndErrors (dec 09)
1613  */
1614  double ra = deg2rad(in.x);
1615  double dec = deg2rad(in.y);
1616  if (ra - ra0 > M_PI) ra -= (2. * M_PI);
1617  if (ra - ra0 < -M_PI) ra += (2. * M_PI);
1618  // Code inspired from worldpos.c in wcssubs (ancestor of the wcslib)
1619  // The same code is copied in ::apply()
1620 
1621  double coss = std::cos(dec);
1622  double sins = std::sin(dec);
1623  double sinda = std::sin(ra - ra0);
1624  double cosda = std::cos(ra - ra0);
1625  double l = sinda * coss;
1626  double m = sins * sin0 + coss * cos0 * cosda;
1627  l = l / m;
1628  m = (sins * cos0 - coss * sin0 * cosda) / m;
1629 
1630  // derivatives
1631  double deno =
1632  sq(sin0) - sq(coss) + sq(coss * cos0) * (1 + sq(cosda)) + 2 * sins * sin0 * coss * cos0 * cosda;
1633  double a11 = coss * (cosda * sins * sin0 + coss * cos0) / deno;
1634  double a12 = -sinda * sin0 / deno;
1635  double a21 = coss * sinda * sins / deno;
1636  double a22 = cosda / deno;
1637 
1638  FatPoint tmp;
1639  tmp.vx = a11 * (a11 * in.vx + 2 * a12 * in.vxy) + a12 * a12 * in.vy;
1640  tmp.vy = a21 * a21 * in.vx + a22 * a22 * in.vy + 2. * a21 * a22 * in.vxy;
1641  tmp.vxy = a21 * a11 * in.vx + a22 * a12 * in.vy + (a21 * a12 + a11 * a22) * in.vxy;
1642 
1643  // l and m are now coordinates in the tangent plane, in radians.
1644  tmp.x = rad2deg(l);
1645  tmp.y = rad2deg(m);
1646 
1647  linTan2Pix.transformPosAndErrors(tmp, out);
1648 }
1649 
1650 void TanRaDecToPixel::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
1651  double ra = deg2rad(xIn);
1652  double dec = deg2rad(yIn);
1653  if (ra - ra0 > M_PI) ra -= (2. * M_PI);
1654  if (ra - ra0 < -M_PI) ra += (2. * M_PI);
1655  // Code inspired from worldpos.c in wcssubs (ancestor of the wcslib)
1656  // The same code is copied in ::transformPosAndErrors()
1657  double coss = std::cos(dec);
1658  double sins = std::sin(dec);
1659  double l = std::sin(ra - ra0) * coss;
1660  double m = sins * sin0 + coss * cos0 * std::cos(ra - ra0);
1661  l = l / m;
1662  m = (sins * cos0 - coss * sin0 * std::cos(ra - ra0)) / m;
1663  // l and m are now coordinates in the tangent plane, in radians.
1664  l = rad2deg(l);
1665  m = rad2deg(m);
1666  linTan2Pix.apply(l, m, xOut, yOut);
1667 }
1668 
1671 }
1672 
1674  Point tp = getTangentPoint();
1675  stream << " tan2pix " << linTan2Pix << " tangent point " << tp.x << ' ' << tp.y << endl;
1676 }
1677 
1680 }
1681 
1684 }
1685 
1687  return std::unique_ptr<Gtransfo>(new TanRaDecToPixel(*this));
1688 }
1689 
1692  "TanRaDecToPixel::fit is NOT implemented (although it is doable)) ");
1693  return -1;
1694 }
1695 
1696 /************* a "run-time" transfo, that does not require to
1697 modify this file */
1698 
1699 UserTransfo::UserTransfo(GtransfoFun &userFun, const void *userData)
1700  : _userFun(userFun), _userData(userData) {}
1701 
1702 void UserTransfo::apply(const double xIn, const double yIn, double &xOut, double &yOut) const {
1703  _userFun(xIn, yIn, xOut, yOut, _userData);
1704 }
1705 
1707  stream << "UserTransfo with user function @ " << _userFun << "and userData@ " << _userData << endl;
1708 }
1709 
1712  "UserTransfo::fit is NOT implemented (and will never be)) ");
1713  return -1;
1714 }
1715 
1717  return std::unique_ptr<Gtransfo>(new UserTransfo(*this));
1718 }
1719 
1720 /*************************************************************/
1721 
1723  ifstream s(fileName.c_str());
1724  if (!s)
1725  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, " gtransfoRead : cannot open " + fileName);
1726  try {
1728  s.close();
1729  return res;
1732  std::string(e.what()) + " in file " + fileName);
1733  }
1734 }
1735 
1737  std::string type;
1738  s >> type;
1739  if (s.fail())
1741  "gtransfoRead : could not find a Gtransfotype");
1742  if (type == "GtransfoIdentity") {
1744  res->read(s);
1745  return std::move(res);
1746  } else if (type == "GtransfoPoly") {
1748  res->read(s);
1749  return std::move(res);
1750  } else
1752  " gtransfoRead : No reader for Gtransfo type " + type);
1753 }
1754 } // namespace jointcal
1755 } // namespace lsst
#define LOGLS_WARN(logger, message)
Log a warn-level message using an iostream-based interface.
Definition: Log.h:657
std::unique_ptr< Gtransfo > composeAndReduce(GtransfoLin const &right) const
Return a reduced composition of newTransfo = this(right()), or nullptr if it cannot be reduced...
Definition: Gtransfo.cc:1424
virtual std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:347
void dump(std::ostream &stream=std::cout) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:1706
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:1544
implements the linear transformations (6 real coefficients).
Definition: Gtransfo.h:414
the transformation that handles pix to sideral transfos (Gnomonic, possibly with polynomial distortio...
Definition: Gtransfo.h:601
T atan(T... args)
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const override
Definition: Gtransfo.cc:1399
GtransfoLin getLinPart() const
The Linear part (corresponding to CD&#39;s and CRPIX&#39;s)
Definition: Gtransfo.cc:1595
std::shared_ptr< ast::Mapping > toAstMap(jointcal::Frame const &domain) const override
Create an equivalent AST mapping for this transformation, including an analytic inverse if possible...
Definition: Gtransfo.cc:1047
#define LOG_LOGGER
Definition: Log.h:712
double paramRef(const int i) const override
Definition: Gtransfo.cc:733
GtransfoLin inverted() const
returns the inverse: T1 = T2.inverted();
Definition: Gtransfo.cc:1202
A hanger for star associations.
Definition: StarMatch.h:54
A point in a plane.
Definition: Point.h:36
void() GtransfoFun(const double, const double, double &, double &, const void *)
signature of the user-provided routine that actually does the coordinate transfo for UserTransfo...
Definition: Gtransfo.h:726
double getArea() const
Definition: Frame.cc:118
void setCorrections(std::unique_ptr< GtransfoPoly > corrections)
Assign the correction polynomial (what it means is left to derived classes)
Definition: Gtransfo.cc:1381
Low-level polynomials (including special polynomials) in C++.
Definition: Basis1d.h:26
double fit(StarMatchList const &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
Definition: Gtransfo.cc:343
long double & coeff(const unsigned powX, const unsigned powY)
Definition: Gtransfo.cc:939
double fit(StarMatchList const &starMatchList)
guess what
Definition: Gtransfo.cc:1236
PolyXY(GtransfoPoly const &gtransfoPoly, const unsigned whichCoord)
Definition: Gtransfo.cc:927
void setOrder(const unsigned order)
Sets the polynomial order (the highest sum of exponents of the largest monomial). ...
Definition: Gtransfo.cc:525
double fit(StarMatchList const &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
Definition: Gtransfo.cc:1690
GtransfoLin operator*(GtransfoLin const &right) const
enables to combine linear tranformations: T1=T2*T3 is legal.
Definition: Gtransfo.cc:1179
void write(std::ostream &s) const override
Definition: Gtransfo.cc:433
GtransfoPoly(const unsigned order=1)
Default transfo : identity for all orders (>=1 ).
Definition: Gtransfo.cc:447
table::PointKey< double > crpix
Definition: OldWcs.cc:131
T fail(T... args)
std::unique_ptr< Gtransfo > roughInverse(const Frame &) const
Overload the "generic routine".
Definition: Gtransfo.cc:281
FatPoint point2
2 points
Definition: StarMatch.h:60
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
implements an iterative (Gauss-Newton) solver.
Definition: Gtransfo.cc:316
double fit(StarMatchList const &starMatchList)
Not implemented yet, because we do it otherwise.
Definition: Gtransfo.cc:1488
virtual void pixToTangentPlane(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const =0
Transform from pixels to tangent plane (degrees)
T endl(T... args)
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
Definition: Gtransfo.cc:1702
py::object result
Definition: schema.cc:284
T left(T... args)
void computeDerivative(Point const &where, GtransfoLin &derivative, const double step=0.01) const override
Computes the local Derivative of a transfo, w.r.t.
Definition: Gtransfo.cc:420
int y
Definition: SpanSet.cc:49
std::unique_ptr< Gtransfo > clone() const override
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:1411
table::Key< int > a
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
return second(first(xIn,yIn))
Definition: Gtransfo.cc:382
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
Transform pixels to ICRS RA, Dec in degrees.
Definition: Gtransfo.cc:1351
STL namespace.
T precision(T... args)
void computeDerivative(Point const &where, GtransfoLin &derivative, const double step=0.01) const
specialised analytic routine
Definition: Gtransfo.cc:1194
double xMin
coordinate of boundary.
Definition: Frame.h:41
T end(T... args)
#define LOGL_ERROR(logger, message...)
Log a error-level message using a varargs/printf style interface.
Definition: Log.h:561
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:1686
T floor(T... args)
GtransfoPoly operator*(GtransfoPoly const &right) const
Composition (internal stuff in quadruple precision)
Definition: Gtransfo.cc:1003
GtransfoLin linPixelToTan
Definition: Gtransfo.h:591
double fit(StarMatchList const &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
Definition: Gtransfo.cc:393
Polynomial transformation class.
Definition: Gtransfo.h:276
std::shared_ptr< GtransfoPoly > inversePolyTransfo(Gtransfo const &forward, Frame const &domain, double const precision, int const maxOrder=9, unsigned const nSteps=50)
Approximate the inverse by a polynomial, to some precision.
Definition: Gtransfo.cc:1096
GtransfoLin normalizeCoordinatesTransfo(const Frame &frame)
Returns the transformation that maps the input frame along both axes to [-1,1].
Definition: Gtransfo.cc:788
double dec
Definition: Match.cc:41
GtransfoInverse(const Gtransfo *direct, const double precision, const Frame &region)
Definition: Gtransfo.cc:296
STL class.
int min
A Point with uncertainties.
Definition: FatPoint.h:34
#define M_PI
Definition: ListMatch.cc:31
std::ostream & operator<<(std::ostream &os, CameraSysPrefix const &detSysPrefix)
Definition: CameraSys.cc:47
T resize(T... args)
T atan2(T... args)
STL class.
LSST DM logging module built on log4cxx.
double x
coordinate
Definition: Point.h:41
T min(T... args)
pairs of points
T sin(T... args)
T at(T... args)
GtransfoLin()
the default constructor constructs the do-nothing transformation.
Definition: Gtransfo.h:419
bool isIntegerShift(const Gtransfo *gtransfo)
Shorthand test to tell if a transfo is a simple integer shift.
Definition: Gtransfo.cc:55
double getHeight() const
size along y axis
Definition: Frame.h:57
TanPixelToRaDec inverted() const
exact typed inverse:
Definition: Gtransfo.cc:1669
Reports attempts to access elements using an invalid key.
Definition: Runtime.h:151
T push_back(T... args)
virtual char const * what(void) const noexcept
Return a character string summarizing this exception.
Definition: Exception.cc:99
void setTangentPoint(Point const &tangentPoint)
Resets the projection (or tangent) point.
Definition: Gtransfo.cc:1578
STL class.
double coeffOrZero(const unsigned powX, const unsigned powY, const unsigned whichCoord) const
read access, zero if beyond order
Definition: Gtransfo.cc:724
std::unique_ptr< Gtransfo > roughInverse(const Frame &region) const
Overload the "generic routine" (available for all Gtransfo types.
Definition: Gtransfo.cc:1446
#define LOGL_FATAL(logger, message...)
Log a fatal-level message using a varargs/printf style interface.
Definition: Log.h:577
virtual void pixToTangentPlane(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const
transforms from pixel space to tangent plane (degrees)
Definition: Gtransfo.cc:1533
void read(std::istream &s)
Definition: Gtransfo.cc:435
rectangle with sides parallel to axes.
Definition: Frame.h:38
GtransfoComposition(Gtransfo const &second, Gtransfo const &first)
will pipe transfos
Definition: Gtransfo.cc:377
void dump(std::ostream &stream) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:1479
A base class for image defects.
Definition: cameraGeom.dox:3
#define LOGLS_TRACE(logger, message)
Log a trace-level message using an iostream-based interface.
Definition: Log.h:597
table::Key< int > type
Definition: Detector.cc:164
Point getCrPix() const
Get the pixel origin of the WCS (CRPIX in FITS WCS terminology, but zero-based)
Definition: Gtransfo.cc:1383
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame &region) const
Inverse transfo: returns a TanPixelToRaDec.
Definition: Gtransfo.cc:1682
GtransfoLin getLinPart() const
The Linear part (corresponding to CD&#39;s and CRPIX&#39;s)
Definition: Gtransfo.cc:1379
void dump(std::ostream &stream) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:1548
int const step
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame &region) const
returns an inverse transfo. Numerical if not overloaded.
Definition: Gtransfo.cc:1228
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame &region) const
Inverse transfo: returns a TanRaDecToPixel if there are no corrections, or the iterative solver if th...
Definition: Gtransfo.cc:1450
void dump(ostream &stream=cout) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:388
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:129
GtransfoPoly getPixelToTangentPlane() const
the transformation from pixels to tangent plane (degrees)
Definition: Gtransfo.cc:1457
void read(std::istream &s)
Definition: Gtransfo.cc:1062
std::shared_ptr< afw::geom::SkyWcs > getSkyWcs() const
Definition: Gtransfo.h:543
def getParams(record, galType='sersic')
Definition: compareModel.py:17
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:1475
T str(T... args)
table::Key< table::Array< int > > degree
Definition: PsfexPsf.cc:360
This one is the Tangent Plane (called gnomonic) projection (from celestial sphere to tangent plane) ...
Definition: Gtransfo.h:679
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:398
int max
virtual std::unique_ptr< Gtransfo > composeAndReduce(Gtransfo const &right) const
Return a reduced composition of newTransfo = this(right()), or nullptr if it cannot be reduced...
Definition: Gtransfo.cc:92
double fit(StarMatchList const &starMatchList) override
guess what
Definition: Gtransfo.cc:880
T reset(T... args)
T cos(T... args)
T dynamic_pointer_cast(T... args)
std::shared_ptr< ast::Mapping > toAstMap(jointcal::Frame const &domain) const override
Create an equivalent AST mapping for this transformation, including an analytic inverse if possible...
Definition: Gtransfo.cc:429
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const
Definition: Gtransfo.cc:1650
double fit(StarMatchList const &starMatchList)
guess what
Definition: Gtransfo.cc:1232
#define LOGL_WARN(logger, message...)
Log a warn-level message using a varargs/printf style interface.
Definition: Log.h:545
void dump(std::ostream &stream=std::cout) const override
print out of coefficients in a readable form.
Definition: Gtransfo.cc:763
std::unique_ptr< Gtransfo > inverseTransfo(const double precision, const Frame &region) const
Inverse transfo: returns a TanRaDecToPixel if there are no corrections, or the iterative solver if th...
Definition: Gtransfo.cc:1521
BaseTanWcs(GtransfoLin const &pixToTan, Point const &tangentPoint, const GtransfoPoly *corrections=nullptr)
Definition: Gtransfo.cc:1320
Reports errors in the logical structure of the program.
Definition: Runtime.h:46
T infinity(T... args)
T fabs(T... args)
T max(T... args)
solver_t * s
T move(T... args)
virtual GtransfoLin linearApproximation(Point const &where, const double step=0.01) const override
linear approximation.
Definition: Gtransfo.cc:424
double fit(StarMatchList const &starMatchList)
Not implemented yet, because we do it otherwise.
Definition: Gtransfo.cc:1557
double fit(StarMatchList const &starMatchList)
fits a transfo to a std::list of Point pairs (p1,p2, the Point fields in StarMatch).
Definition: Gtransfo.cc:1710
double x
double getWidth() const
size along x axis
Definition: Frame.h:54
T count(T... args)
unsigned getOrder() const
Returns the polynomial order.
Definition: Gtransfo.h:302
void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
transform with analytical derivatives
Definition: Gtransfo.cc:1598
T insert(T... args)
Point getCenter() const
Center of the frame.
Definition: Frame.h:60
A do-nothing transformation. It anyway has dummy routines to mimick a Gtransfo.
Definition: Gtransfo.h:219
T size(T... args)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
just here to provide a specialized constructor, and fit.
Definition: Gtransfo.h:478
STL class.
void computeDerivative(Point const &where, GtransfoLin &derivative, const double step=0.01) const override
specialised analytic routine
Definition: Gtransfo.cc:569
void dump(std::ostream &stream) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:1673
std::unique_ptr< Gtransfo > clone() const
returns a copy (allocated by new) of the transformation.
Definition: Gtransfo.cc:1716
T begin(T... args)
friend class GtransfoPoly
Definition: Gtransfo.h:469
T pow(T... args)
just here to provide specialized constructors. GtransfoLin fit routine.
Definition: Gtransfo.h:505
T c_str(T... args)
a virtual (interface) class for geometric transformations.
Definition: Gtransfo.h:65
virtual void pixToTangentPlane(double xPixel, double yPixel, double &xTangentPlane, double &yTangentPlane) const
transforms from pixel space to tangent plane (degrees)
Definition: Gtransfo.cc:1464
Point getTangentPoint() const
Get the sky origin (CRVAL in FITS WCS terminology) in degrees.
Definition: Gtransfo.cc:1377
std::unique_ptr< GtransfoPoly > corr
Definition: Gtransfo.h:593
Reports invalid arguments.
Definition: Runtime.h:66
double coeff(const unsigned powX, const unsigned powY, const unsigned whichCoord) const
access to coefficients (read only)
Definition: Gtransfo.cc:711
int m
Definition: SpanSet.cc:49
Private class to handle Gtransfo compositions (i.e.
Definition: Gtransfo.cc:358
GtransfoPoly operator-(GtransfoPoly const &right) const
Subtraction.
Definition: Gtransfo.cc:1037
GtransfoPoly getPixelToTangentPlane() const
the transformation from pixels to tangent plane (degrees)
Definition: Gtransfo.cc:1526
T sqrt(T... args)
#define LOGLS_FATAL(logger, message)
Log a fatal-level message using an iostream-based interface.
Definition: Log.h:697
PolyXY(const int order)
Definition: Gtransfo.cc:920
TanRaDecToPixel inverted() const
approximate inverse : it ignores corrections;
Definition: Gtransfo.cc:1438
GtransfoSkyWcs(std::shared_ptr< afw::geom::SkyWcs > skyWcs)
Definition: Gtransfo.cc:1397
long double coeff(const unsigned powX, const unsigned powY) const
Definition: Gtransfo.cc:934
#define LOG_GET(logger)
Returns a Log object associated with logger.
Definition: Log.h:83
void paramDerivatives(Point const &where, double *dx, double *dy) const override
Derivative w.r.t parameters.
Definition: Gtransfo.cc:743
void write(std::ostream &s) const override
Definition: Gtransfo.cc:1052
std::unique_ptr< Gtransfo > gtransfoRead(const std::string &fileName)
The virtual constructor from a file.
Definition: Gtransfo.cc:1722
T setprecision(T... args)
virtual std::unique_ptr< Gtransfo > clone() const =0
returns a copy (allocated by new) of the transformation.
virtual void dump(std::ostream &stream=std::cout) const =0
dumps the transfo coefficients to stream.
void apply(const double xIn, const double yIn, double &xOut, double &yOut) const override
Definition: Gtransfo.cc:546
STL class.
UserTransfo(GtransfoFun &fun, const void *userData)
the transfo routine and extra data that it may need.
Definition: Gtransfo.cc:1699
unsigned getOrder() const
Definition: Gtransfo.cc:925
double fit(const StarMatchList &starMatchList) override
Not implemented; throws pex::exceptions::LogicError.
Definition: Gtransfo.cc:1407
TanPixelToRaDec operator*(GtransfoLin const &right) const
composition with GtransfoLin
Definition: Gtransfo.cc:1432
STL class.
GtransfoLin linearApproximation(Point const &where, const double step=0.01) const
linear (local) approximation.
Definition: Gtransfo.cc:1200
virtual void transformPosAndErrors(const FatPoint &in, FatPoint &out) const override
a mix of apply and Derivative
Definition: Gtransfo.cc:627
virtual void apply(const double xIn, const double yIn, double &xOut, double &yOut) const =0
void dump(ostream &stream) const
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:339
void operator=(const BaseTanWcs &original)
Definition: Gtransfo.cc:1341
T reserve(T... args)
std::unique_ptr< Gtransfo > inverseTransfo(double, const Frame &) const
Inverse transfo: returns the direct one!
Definition: Gtransfo.cc:284
std::unique_ptr< Gtransfo > composeAndReduce(GtransfoPoly const &right) const
Return a reduced composition of newTransfo = this(right()), or nullptr if it cannot be reduced...
Definition: Gtransfo.cc:898
double determinant() const
Definition: Gtransfo.cc:782
Reports errors that are due to events beyond the control of the program.
Definition: Runtime.h:104
std::unique_ptr< Gtransfo > roughInverse(const Frame &region) const
Overload the "generic routine" (available for all Gtransfo types.
Definition: Gtransfo.cc:1678
void dump(std::ostream &stream=std::cout) const override
dumps the transfo coefficients to stream.
Definition: Gtransfo.cc:1405
Point getTangentPoint() const
tangent point coordinates (degrees)
Definition: Gtransfo.cc:1593
T emplace_back(T... args)
GtransfoPoly operator+(GtransfoPoly const &right) const
Addition.
Definition: Gtransfo.cc:1024
std::unique_ptr< Gtransfo > gtransfoCompose(Gtransfo const &left, Gtransfo const &right)
Returns a pointer to a composition of gtransfos, representing left(right()).
Definition: Gtransfo.cc:408