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 double chi2 = wxx *
std::pow(rx, 2) + 2 * wxy * rx * ry + wyy *
std::pow(ry, 2);
403 accum.addEntry(chi2, 2, fs);
410 void AstrometryFit::getIndicesOfMeasuredStar(MeasuredStar
const &measuredStar,
412 if (_fittingDistortions) {
413 const Mapping *mapping = _astrometryModel->getMapping(measuredStar.getCcdImage());
414 mapping->getMappingIndices(indices);
416 auto fs = measuredStar.getFittedStar();
417 unsigned fsIndex = fs->getIndexInMatrix();
432 LOGLS_INFO(_log,
"assignIndices: Now fitting " << whatToFit);
433 _fittingDistortions = (
_whatToFit.
find(
"Distortions") != std::string::npos);
434 _fittingPos = (
_whatToFit.
find(
"Positions") != std::string::npos);
435 _fittingRefrac = (
_whatToFit.
find(
"Refrac") != std::string::npos);
436 if (_sigCol == 0 && _fittingRefrac) {
438 "Cannot fit refraction coefficients without a color lever arm. Ignoring refraction.");
439 _fittingRefrac =
false;
444 _nParDistortions = 0;
445 if (_fittingDistortions) _nParDistortions = _astrometryModel->assignIndices(0,
_whatToFit);
446 unsigned ipar = _nParDistortions;
450 for (
auto &fittedStar : fittedStarList) {
455 fittedStar->setIndexInMatrix(ipar);
457 if ((_fittingPM)&fittedStar->mightMove) ipar +=
NPAR_PM;
460 _nParPositions = ipar - _nParDistortions;
461 if (_fittingRefrac) {
462 _refracPosInMatrix = ipar;
471 "AstrometryFit::offsetParams : the provided vector length is not compatible with " 472 "the current whatToFit setting");
473 if (_fittingDistortions) _astrometryModel->offsetParams(delta);
477 for (
auto const &i : fittedStarList) {
483 fs.
x += delta(index);
484 fs.
y += delta(index + 1);
486 fs.
pmx += delta(index + 2);
487 fs.
pmy += delta(index + 3);
491 if (_fittingRefrac) {
492 _refractionCoefficient += delta(_refracPosInMatrix);
498 static void write_sparse_matrix_in_fits(
SpMat const &mat,
std::string const &fitsName) {
499 if (mat.rows() * mat.cols() > 2e8) {
501 "write_sparse_matrix_in_fits: yout matrix is too large. " << fitsName <<
" not generated");
504 Mat
m(mat.rows(), mat.cols());
505 for (
int k = 0; k < mat.outerSize(); ++k)
506 for (SpMat::InnerIterator it(mat, k); it; ++it) {
507 m(it.row(), it.col()) = it.value();
509 m.writeFits(fitsName);
512 static void write_vect_in_fits(Eigen::VectorXd
const &vectorXd,
std::string const &fitsName) {
513 Vect v(vectorXd.size());
514 for (
int k = 0; k < vectorXd.size(); ++k) v(k) = V(k);
515 Mat(v).writeFits(fitsName);
522 const char *what2fit[] = {
"Positions",
525 "Positions Distortions",
527 "Distortions Refrac",
528 "Positions Distortions Refrac"};
530 const char *what2fit[] = {
"Positions",
"Distortions",
"Positions Distortions"};
532 for (
unsigned k = 0; k <
sizeof(what2fit) /
sizeof(what2fit[0]); ++k) {
539 jacobian.setFromTriplets(tripletList.
begin(), tripletList.
end());
540 SpMat hessian = jacobian * jacobian.transpose();
544 write_sparse_matrix_in_fits(hessian, name);
546 write_vect_in_fits(grad, name);
555 ofile <<
"#xccd" << separator <<
"yccd " << separator <<
"rx" << separator <<
"ry" << separator <<
"xtp" 556 << separator <<
"ytp" << separator <<
"mag" << separator <<
"mjd" << separator <<
"rvx" << separator
557 <<
"rvy" << separator <<
"rvxy" << separator <<
"color" << separator <<
"fsindex" << separator
558 <<
"ra" << separator <<
"dec" << separator <<
"chi2" << separator <<
"nm" << separator <<
"chip" 560 ofile <<
"#coordinates in CCD" << separator << separator <<
"residual in degrees in TP" << separator
561 << separator <<
"transformed coordinate in TP" << separator << separator <<
"rough mag" << separator
562 <<
"Modified Julian date of the measurement" << separator <<
"transformed measurement uncertainty" 563 << separator << separator << separator <<
"currently unused" << separator
564 <<
"unique index of the fittedStar" << separator <<
"on sky position of fittedStar" << separator
565 << separator <<
"contribution to Chi2 (2D dofs)" << separator
566 <<
"number of measurements of this fittedStar" << separator <<
"chip id" << separator <<
"visit id" 569 for (
auto const &ccdImage : ccdImageList) {
571 const Mapping *mapping = _astrometryModel->getMapping(*ccdImage);
572 const Point &refractionVector = ccdImage->getRefractionVector();
573 double mjd = ccdImage->getMjd() - _JDRef;
574 for (
auto const &ms : cat) {
575 if (!ms->isValid())
continue;
578 tweakAstromMeasurementErrors(inPos, *ms, _posError);
580 const Gtransfo *sky2TP = _astrometryModel->getSky2TP(*ccdImage);
581 auto fs = ms->getFittedStar();
583 Point fittedStarInTP =
584 transformFittedStar(*fs, sky2TP, refractionVector, _refractionCoefficient, mjd);
585 Point res = tpPos - fittedStarInTP;
587 double wxx = tpPos.
vy / det;
588 double wyy = tpPos.
vx / det;
589 double wxy = -tpPos.
vxy / det;
590 double chi2 = wxx * res.
x * res.
x + wyy * res.
y * res.
y + 2 * wxy * res.
x * res.
y;
592 ofile << ms->x << separator << ms->y << separator << res.
x << separator << res.
y << separator
593 << tpPos.
x << separator << tpPos.
y << separator << fs->getMag() << separator << mjd
594 << separator << tpPos.
vx << separator << tpPos.
vy << separator << tpPos.
vxy << separator
595 << fs->color << separator << fs->getIndexInMatrix() << separator << fs->x << separator
596 << fs->y << separator << chi2 << separator << fs->getMeasurementCount() << separator
597 << ccdImage->getCcdId() << separator << ccdImage->getVisit() <<
std::endl;
605 ofile <<
"#ra" << separator <<
"dec " << separator <<
"rx" << separator <<
"ry" << separator <<
"mag" 606 << separator <<
"rvx" << separator <<
"rvy" << separator <<
"rvxy" << separator <<
"color" 607 << separator <<
"fsindex" << separator <<
"chi2" << separator <<
"nm" <<
std::endl;
608 ofile <<
"#coordinates of fittedStar" << separator << separator <<
"residual in degrees in TP" 609 << separator << separator <<
"magnitude" << separator
610 <<
"refStar transformed measurement uncertainty" << separator << separator << separator
611 <<
"currently unused" << separator <<
"unique index of the fittedStar" << separator
612 <<
"refStar contribution to Chi2 (2D dofs)" << separator
613 <<
"number of measurements of this FittedStar" <<
std::endl;
617 for (
auto const &i : fittedStarList) {
620 if (rs ==
nullptr)
continue;
625 double rx = rsProj.
x;
626 double ry = rsProj.
y;
628 double wxx = rsProj.
vy / det;
629 double wyy = rsProj.
vx / det;
630 double wxy = -rsProj.
vxy / det;
631 double chi2 = wxx *
std::pow(rx, 2) + 2 * wxy * rx * ry + wyy *
std::pow(ry, 2);
633 ofile << fs.
x << separator << fs.
y << separator << rx << separator << ry << separator << fs.
getMag()
634 << separator << rsProj.
vx << separator << rsProj.
vy << separator << rsProj.
vxy << separator
Objects used as position anchors, typically USNO stars.
implements the linear transformations (6 real coefficients).
void saveChi2RefContributions(std::string const &baseName) const override
Save a CSV file containing residuals of reference terms.
virtual class needed in the abstraction of the distortion model
int getMeasurementCount() const
void saveChi2MeasContributions(std::string const &baseName) const override
Save a CSV file containing residuals of measurement terms.
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.
#define LOGLS_INFO(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.
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
void setTangentPoint(const Point &tangentPoint)
Resets the projection (or tangent) point.
#define LSST_EXCEPT(type,...)
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
a virtual (interface) class for geometric transformations.
const RefStar * getRefStar() const
Get the astrometric reference star associated with this star.
#define LOGLS_DEBUG(logger, message)
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.
T setprecision(T... args)
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)
#define LOGLS_WARN(logger, message)