6 #include "Eigen/Sparse" 20 LOG_LOGGER _log =
LOG_GET(
"jointcal.AstrometryFit");
29 _astrometryModel(astrometryModel),
30 _refractionCoefficient(0),
33 _nParRefrac(_associations->getNFilters()),
43 _referenceColor += i->color;
48 _referenceColor /= double(count);
49 if (_sigCol > 0) _sigCol =
sqrt(_sigCol / count -
std::pow(_referenceColor, 2));
51 LOGLS_INFO(_log,
"Reference Color: " << _referenceColor <<
" sig " << _sigCol);
59 Point AstrometryFit::transformFittedStar(FittedStar
const &fittedStar, Gtransfo
const *sky2TP,
60 Point
const &refractionVector,
double refractionCoeff,
62 Point fittedStarInTP = sky2TP->apply(fittedStar);
63 if (fittedStar.mightMove) {
64 fittedStarInTP.x += fittedStar.pmx * mjd;
65 fittedStarInTP.y += fittedStar.pmy * mjd;
70 double color = fittedStar.color - _referenceColor;
71 fittedStarInTP.x += refractionVector.x * color * refractionCoeff;
72 fittedStarInTP.y += refractionVector.y * color * refractionCoeff;
73 return fittedStarInTP;
79 static void tweakAstromMeasurementErrors(FatPoint &P, MeasuredStar
const &Ms,
double error) {
80 static bool called =
false;
81 static double increment = 0;
92 void AstrometryFit::leastSquareDerivativesMeasurement(CcdImage
const &
ccdImage, TripletList &tripletList,
93 Eigen::VectorXd &fullGrad,
94 MeasuredStarList
const *msList)
const {
104 if (msList) assert(&(msList->front()->getCcdImage()) == &ccdImage);
107 const Mapping *mapping = _astrometryModel->getMapping(ccdImage);
109 unsigned npar_mapping = (_fittingDistortions) ? mapping->getNpar() : 0;
110 unsigned npar_pos = (_fittingPos) ? 2 : 0;
111 unsigned npar_refrac = (_fittingRefrac) ? 1 : 0;
112 unsigned npar_pm = (_fittingPM) ?
NPAR_PM : 0;
113 unsigned npar_tot = npar_mapping + npar_pos + npar_refrac + npar_pm;
116 if (npar_tot == 0)
return;
118 if (_fittingDistortions) mapping->getMappingIndices(indices);
121 double mjd = ccdImage.getMjd() - _JDRef;
123 Point refractionVector = ccdImage.getRefractionVector();
125 const Gtransfo *sky2TP = _astrometryModel->getSky2TP(ccdImage);
131 Eigen::Matrix2d transW(2, 2);
132 Eigen::Matrix2d
alpha(2, 2);
133 Eigen::VectorXd grad(npar_tot);
135 unsigned kTriplets = tripletList.getNextFreeIndex();
136 const MeasuredStarList &catalog = (msList) ? *msList : ccdImage.getCatalogForFit();
138 for (
auto &i : catalog) {
139 const MeasuredStar &ms = *i;
140 if (!ms.isValid())
continue;
143 tweakAstromMeasurementErrors(inPos, ms, _posError);
147 if (_fittingDistortions)
148 mapping->computeTransformAndDerivatives(inPos, outPos, H);
150 mapping->transformPosAndErrors(inPos, outPos);
152 unsigned ipar = npar_mapping;
153 double det = outPos.vx * outPos.vy -
std::pow(outPos.vxy, 2);
154 if (det <= 0 || outPos.vx <= 0 || outPos.vy <= 0) {
155 LOGLS_WARN(_log,
"Inconsistent measurement errors: drop measurement at " 156 << Point(ms) <<
" in image " << ccdImage.getName());
159 transW(0, 0) = outPos.vy / det;
160 transW(1, 1) = outPos.vx / det;
161 transW(0, 1) = transW(1, 0) = -outPos.vxy / det;
164 alpha(0, 0) = sqrt(transW(0, 0));
166 alpha(1, 0) = transW(0, 1) / alpha(0, 0);
169 alpha(1, 1) = 1. / sqrt(det * transW(0, 0));
172 auto fs = ms.getFittedStar();
174 Point fittedStarInTP =
175 transformFittedStar(*fs, sky2TP, refractionVector, _refractionCoefficient, mjd);
180 sky2TP->computeDerivative(*fs, dypdy, 1e-3);
183 H(npar_mapping, 0) = -dypdy.A11();
184 H(npar_mapping + 1, 0) = -dypdy.A12();
185 H(npar_mapping, 1) = -dypdy.A21();
186 H(npar_mapping + 1, 1) = -dypdy.A22();
187 indices[npar_mapping] = fs->getIndexInMatrix();
188 indices.
at(npar_mapping + 1) = fs->getIndexInMatrix() + 1;
193 if (_fittingPM && fs->mightMove) {
195 H(ipar + 1, 1) = -mjd;
196 indices[ipar] = fs->getIndexInMatrix() + 2;
197 indices[ipar + 1] = fs->getIndexInMatrix() + 3;
200 if (_fittingRefrac) {
203 double color = fs->color - _referenceColor;
205 H(ipar, 0) = -refractionVector.x * color;
206 H(ipar, 1) = -refractionVector.y * color;
207 indices[ipar] = _refracPosInMatrix;
212 Eigen::Vector2d res(fittedStarInTP.x - outPos.x, fittedStarInTP.y - outPos.y);
220 for (
unsigned ipar = 0; ipar < npar_tot; ++ipar) {
221 for (
unsigned ic = 0; ic < 2; ++ic) {
222 double val = halpha(ipar, ic);
223 if (val == 0)
continue;
224 tripletList.addTriplet(indices[ipar], kTriplets + ic, val);
226 fullGrad(indices[ipar]) += grad(ipar);
230 tripletList.setNextFreeIndex(kTriplets);
233 void AstrometryFit::leastSquareDerivativesReference(FittedStarList
const &fittedStarList,
234 TripletList &tripletList,
235 Eigen::VectorXd &fullGrad)
const {
244 if (!_fittingPos)
return;
248 Eigen::Matrix2d W(2, 2);
249 Eigen::Matrix2d
alpha(2, 2);
250 Eigen::Matrix2d H(2, 2), halpha(2, 2), HW(2, 2);
252 Eigen::Vector2d res, grad;
254 unsigned kTriplets = tripletList.getNextFreeIndex();
261 TanRaDec2Pix proj(GtransfoLin(), Point(0., 0.));
262 for (
auto const &i : fittedStarList) {
263 const FittedStar &fs = *i;
264 const RefStar *rs = fs.getRefStar();
265 if (rs ==
nullptr)
continue;
266 proj.setTangentPoint(fs);
269 proj.transformPosAndErrors(*rs, rsProj);
271 proj.computeDerivative(fs, der, 1e-4);
273 H(0, 0) = -der.A11();
274 H(1, 0) = -der.A12();
275 H(0, 1) = -der.A21();
276 H(1, 1) = -der.A22();
278 double det = rsProj.vx * rsProj.vy -
std::pow(rsProj.vxy, 2);
279 if (rsProj.vx <= 0 || rsProj.vy <= 0 || det <= 0) {
280 LOGLS_WARN(_log,
"RefStar error matrix not positive definite for: " << *rs);
283 W(0, 0) = rsProj.vy / det;
284 W(0, 1) = W(1, 0) = -rsProj.vxy / det;
285 W(1, 1) = rsProj.vx / det;
288 alpha(0, 0) = sqrt(W(0, 0));
290 alpha(1, 0) = W(0, 1) / alpha(0, 0);
291 alpha(1, 1) = 1. / sqrt(det * W(0, 0));
293 indices[0] = fs.getIndexInMatrix();
294 indices[1] = fs.getIndexInMatrix() + 1;
295 unsigned npar_tot = 2;
311 for (
unsigned ipar = 0; ipar < npar_tot; ++ipar) {
312 for (
unsigned ic = 0; ic < 2; ++ic) {
313 double val = halpha(ipar, ic);
314 if (val == 0)
continue;
315 tripletList.addTriplet(indices[ipar], kTriplets + ic, val);
317 fullGrad(indices[ipar]) += grad(ipar);
321 tripletList.setNextFreeIndex(kTriplets);
324 void AstrometryFit::accumulateStatImage(CcdImage
const &ccdImage, Chi2Accumulator &accum)
const {
331 const Mapping *mapping = _astrometryModel->getMapping(ccdImage);
333 double mjd = ccdImage.getMjd() - _JDRef;
335 Point refractionVector = ccdImage.getRefractionVector();
337 const Gtransfo *sky2TP = _astrometryModel->getSky2TP(ccdImage);
339 Eigen::Matrix2Xd transW(2, 2);
341 auto &catalog = ccdImage.getCatalogForFit();
342 for (
auto const &ms : catalog) {
343 if (!ms->isValid())
continue;
345 FatPoint inPos = *ms;
346 tweakAstromMeasurementErrors(inPos, *ms, _posError);
350 mapping->transformPosAndErrors(inPos, outPos);
351 double det = outPos.vx * outPos.vy -
std::pow(outPos.vxy, 2);
352 if (det <= 0 || outPos.vx <= 0 || outPos.vy <= 0) {
353 LOGLS_WARN(_log,
" Inconsistent measurement errors :drop measurement at " 354 << Point(*ms) <<
" in image " << ccdImage.getName());
357 transW(0, 0) = outPos.vy / det;
358 transW(1, 1) = outPos.vx / det;
359 transW(0, 1) = transW(1, 0) = -outPos.vxy / det;
361 auto fs = ms->getFittedStar();
362 Point fittedStarInTP =
363 transformFittedStar(*fs, sky2TP, refractionVector, _refractionCoefficient, mjd);
365 Eigen::Vector2d res(fittedStarInTP.x - outPos.x, fittedStarInTP.y - outPos.y);
366 double chi2Val = res.transpose() * transW * res;
368 accum.addEntry(chi2Val, 2, ms);
372 void AstrometryFit::accumulateStatImageList(
CcdImageList const &ccdImageList, Chi2Accumulator &accum)
const {
373 for (
auto const &ccdImage : ccdImageList) {
374 accumulateStatImage(*ccdImage, accum);
378 void AstrometryFit::accumulateStatRefStars(Chi2Accumulator &accum)
const {
386 FittedStarList &fittedStarList =
_associations->fittedStarList;
387 TanRaDec2Pix proj(GtransfoLin(), Point(0., 0.));
388 for (
auto const &fs : fittedStarList) {
389 const RefStar *rs = fs->getRefStar();
390 if (rs ==
nullptr)
continue;
391 proj.setTangentPoint(*fs);
394 proj.transformPosAndErrors(*rs, rsProj);
396 double rx = rsProj.x;
397 double ry = rsProj.y;
398 double det = rsProj.vx * rsProj.vy -
std::pow(rsProj.vxy, 2);
399 double wxx = rsProj.vy / det;
400 double wyy = rsProj.vx / det;
401 double wxy = -rsProj.vxy / det;
402 accum.addEntry(wxx *
std::pow(rx, 2) + 2 * wxy * rx * ry + wyy *
std::pow(ry, 2), 2, fs);
409 void AstrometryFit::getIndicesOfMeasuredStar(MeasuredStar
const &measuredStar,
411 if (_fittingDistortions) {
412 const Mapping *mapping = _astrometryModel->getMapping(measuredStar.getCcdImage());
413 mapping->getMappingIndices(indices);
415 auto fs = measuredStar.getFittedStar();
416 unsigned fsIndex = fs->getIndexInMatrix();
431 LOGLS_INFO(_log,
"assignIndices: Now fitting " << whatToFit);
432 _fittingDistortions = (
_whatToFit.
find(
"Distortions") != std::string::npos);
433 _fittingPos = (
_whatToFit.
find(
"Positions") != std::string::npos);
434 _fittingRefrac = (
_whatToFit.
find(
"Refrac") != std::string::npos);
435 if (_sigCol == 0 && _fittingRefrac) {
437 "Cannot fit refraction coefficients without a color lever arm. Ignoring refraction.");
438 _fittingRefrac =
false;
443 _nParDistortions = 0;
444 if (_fittingDistortions) _nParDistortions = _astrometryModel->assignIndices(0,
_whatToFit);
445 unsigned ipar = _nParDistortions;
449 for (
auto &fittedStar : fittedStarList) {
454 fittedStar->setIndexInMatrix(ipar);
456 if ((_fittingPM)&fittedStar->mightMove) ipar +=
NPAR_PM;
459 _nParPositions = ipar - _nParDistortions;
460 if (_fittingRefrac) {
461 _refracPosInMatrix = ipar;
470 "AstrometryFit::offsetParams : the provided vector length is not compatible with " 471 "the current whatToFit setting");
472 if (_fittingDistortions) _astrometryModel->offsetParams(delta);
476 for (
auto const &i : fittedStarList) {
482 fs.
x += delta(index);
483 fs.
y += delta(index + 1);
485 fs.
pmx += delta(index + 2);
486 fs.
pmy += delta(index + 3);
490 if (_fittingRefrac) {
491 _refractionCoefficient += delta(_refracPosInMatrix);
497 static void write_sparse_matrix_in_fits(
SpMat const &mat,
std::string const &fitsName) {
498 if (mat.rows() * mat.cols() > 2e8) {
500 "write_sparse_matrix_in_fits: yout matrix is too large. " << fitsName <<
" not generated");
503 Mat
m(mat.rows(), mat.cols());
504 for (
int k = 0; k < mat.outerSize(); ++k)
505 for (SpMat::InnerIterator it(mat, k); it; ++it) {
506 m(it.row(), it.col()) = it.value();
508 m.writeFits(fitsName);
511 static void write_vect_in_fits(Eigen::VectorXd
const &vectorXd,
std::string const &fitsName) {
512 Vect v(vectorXd.size());
513 for (
int k = 0; k < vectorXd.size(); ++k) v(k) = V(k);
514 Mat(v).writeFits(fitsName);
521 const char *what2fit[] = {
"Positions",
524 "Positions Distortions",
526 "Distortions Refrac",
527 "Positions Distortions Refrac"};
529 const char *what2fit[] = {
"Positions",
"Distortions",
"Positions Distortions"};
531 for (
unsigned k = 0; k <
sizeof(what2fit) /
sizeof(what2fit[0]); ++k) {
538 jacobian.setFromTriplets(tripletList.
begin(), tripletList.
end());
539 SpMat hessian = jacobian * jacobian.transpose();
543 write_sparse_matrix_in_fits(hessian, name);
545 write_vect_in_fits(grad, name);
555 size_t slash = tupleName.
rfind(
'/');
556 if (dot == std::string::npos || (slash != std::string::npos && dot < slash)) dot = tupleName.
size();
558 meas_tuple.
insert(dot,
"-meas");
561 ref_tuple.
insert(dot,
"-ref");
569 <<
"#rx: residual in degrees in TP" <<
std::endl 571 <<
"#xtp: transformed coordinate in TP " <<
std::endl 574 <<
"#jd: Julian date of the measurement" <<
std::endl 575 <<
"#rvx: transformed measurement uncertainty " <<
std::endl 579 <<
"#fsindex: some unique index of the object" <<
std::endl 580 <<
"#ra: pos of fitted star" <<
std::endl 581 <<
"#dec: pos of fitted star" <<
std::endl 582 <<
"#chi2: contribution to Chi2 (2D dofs)" <<
std::endl 583 <<
"#nm: number of measurements of this FittedStar" <<
std::endl 588 for (
auto const &i : L) {
591 const Mapping *mapping = _astrometryModel->getMapping(im);
593 double mjd = im.
getMjd() - _JDRef;
594 for (
auto const &is : cat) {
599 tweakAstromMeasurementErrors(inPos, ms, _posError);
601 const Gtransfo *sky2TP = _astrometryModel->getSky2TP(im);
604 Point fittedStarInTP =
605 transformFittedStar(*fs, sky2TP, refractionVector, _refractionCoefficient, mjd);
606 Point res = tpPos - fittedStarInTP;
608 double wxx = tpPos.
vy / det;
609 double wyy = tpPos.
vx / det;
610 double wxy = -tpPos.
vxy / det;
612 double chi2 = wxx * res.
x * res.
x + wyy * res.
y * res.
y + 2 * wxy * res.
x * res.
y;
614 tuple << ms.
x <<
' ' << ms.
y <<
' ' << res.
x <<
' ' << res.
y <<
' ' << tpPos.
x <<
' ' << tpPos.
y 615 <<
' ' << fs->getMag() <<
' ' << mjd <<
' ' << tpPos.
vx <<
' ' << tpPos.
vy <<
' ' 616 << tpPos.
vxy <<
' ' << fs->color <<
' ' << fs->getIndexInMatrix() <<
' ' << fs->x <<
' ' 617 << fs->y <<
' ' << chi2 <<
' ' << fs->getMeasurementCount() <<
' ' << im.
getCcdId() <<
' ' 627 <<
"#rx: residual in degrees in TP" <<
std::endl 630 <<
"#rvx: transformed measurement uncertainty " <<
std::endl 634 <<
"#fsindex: some unique index of the object" <<
std::endl 635 <<
"#chi2: contribution to Chi2 (2D dofs)" <<
std::endl 636 <<
"#nm: number of measurements of this FittedStar" <<
std::endl 641 for (
auto const &i : fittedStarList) {
644 if (rs ==
nullptr)
continue;
649 double rx = rsProj.
x;
650 double ry = rsProj.
y;
652 double wxx = rsProj.
vy / det;
653 double wyy = rsProj.
vx / det;
654 double wxy = -rsProj.
vxy / det;
655 double chi2 = wxx *
std::pow(rx, 2) + 2 * wxy * rx * ry + wyy *
std::pow(ry, 2);
657 tuple << fs.
x <<
' ' << fs.
y <<
' ' << rx <<
' ' << ry <<
' ' << fs.
getMag() <<
' ' << rsProj.
vx #define LOGLS_WARN(logger, message)
Objects used as position anchors, typically USNO stars.
VisitIdType getVisit() const
returns visit ID
implements the linear transformations (6 real coefficients).
virtual class needed in the abstraction of the distortion model
int getMeasurementCount() const
double getMjd() const
Julian Date.
void makeRefResTuple(std::string const &tupleName) const
Produces a tuple containing residuals of reference terms.
CcdIdType getCcdId() const
returns ccd ID
void assignIndices(std::string const &whatToFit) override
Set parameters to fit and assign indices in the big matrix.
AstrometryFit(std::shared_ptr< Associations > associations, std::shared_ptr< AstrometryModel > astrometryModel, double posError)
this is the only constructor
A Point with uncertainties.
A list of MeasuredStar. They are usually filled in Associations::AddImage.
def dot(symb, c, r, frame=None, size=2, ctype=None, origin=afwImage.PARENT, args, kwargs)
#define LOGLS_DEBUG(logger, message)
Class for a simple mapping implementing a generic Gtransfo.
Eigen::SparseMatrix< double > SpMat
A list of FittedStar s. Such a list is typically constructed by Associations.
objects measured on actual images.
void makeMeasResTuple(std::string const &tupleName) const
Produces a tuple containing residuals of measurement terms.
void offsetParams(Eigen::VectorXd const &delta) override
Offset the parameters by the requested quantities.
std::shared_ptr< Associations > _associations
Eigen::Matrix< double, Eigen::Dynamic, 2 > MatrixX2d
#define LOGLS_INFO(logger, message)
void setTangentPoint(const Point &tangentPoint)
Resets the projection (or tangent) point.
#define LSST_EXCEPT(type,...)
void saveResultTuples(std::string const &tupleName) const override
Save the full chi2 term per star that was used in the minimization, for debugging.
std::shared_ptr< const FittedStar > getFittedStar() const
This one is the Tangent Plane (called gnomonic) projection (from celestial sphere to tangent plane) ...
void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
transform with analytical derivatives
Point getRefractionVector() const
MeasuredStarList const & getCatalogForFit() const
Gets the catalog to be used for fitting, which may have been cleaned-up.
a virtual (interface) class for geometric transformations.
const RefStar * getRefStar() const
Get the astrometric reference star associated with this star.
void leastSquareDerivatives(TripletList &tripletList, Eigen::VectorXd &grad) const
Evaluates the chI^2 derivatives (Jacobian and gradient) for the current whatToFit setting...
int getIndexInMatrix() const
std::list< std::shared_ptr< CcdImage > > CcdImageList
double getMag() const
derived using available zero points in input images. In the absence ofZP, ZP= 0.
Handler of an actual image from a single CCD.
T setprecision(T... args)
bool isValid() const
Fits may use that to discard outliers.
void checkStuff()
DEBUGGING routine.
unsigned getNextFreeIndex() const
The objects which have been measured several times.
virtual void transformPosAndErrors(FatPoint const &where, FatPoint &outPoint) const =0
The same as above but without the parameter derivatives (used to evaluate chi^2)