lsst.jointcal  master-gc7bdbdc8f0
ConstrainedPhotometryModel.cc
Go to the documentation of this file.
1 #include <map>
2 #include <limits>
3 
4 #include "lsst/log/Log.h"
5 
6 #include "astshim.h"
7 #include "astshim/ChebyMap.h"
11 #include "lsst/jointcal/CcdImage.h"
14 
15 namespace {
16 LOG_LOGGER _log = LOG_GET("jointcal.ConstrainedPhotometryModel");
17 }
18 
19 namespace lsst {
20 namespace jointcal {
21 
23  afw::geom::Box2D const &focalPlaneBBox,
24  int visitDegree) {
25  // keep track of which chip we want to constrain (the one closest to the middle of the focal plane)
26  double minRadius2 = std::numeric_limits<double>::infinity();
27  CcdIdType constrainedChip = -1;
28 
29  // First initialize all visit and ccd transfos, before we make the ccdImage mappings.
30  for (auto const &ccdImage : ccdImageList) {
31  auto visit = ccdImage->getVisit();
32  auto chip = ccdImage->getCcdId();
33  auto visitPair = _visitMap.find(visit);
34  auto chipPair = _chipMap.find(chip);
35 
36  // If the chip is not in the map, add it, otherwise continue.
37  if (chipPair == _chipMap.end()) {
38  auto center = ccdImage->getDetector()->getCenter(afw::cameraGeom::FOCAL_PLANE);
39  double radius2 = std::pow(center.getPoint().getX(), 2) + std::pow(center.getPoint().getY(), 2);
40  if (radius2 < minRadius2) {
41  minRadius2 = radius2;
42  constrainedChip = chip;
43  }
44  auto photoCalib = ccdImage->getPhotoCalib();
45  // Use the single-frame processing calibration from the PhotoCalib as the default.
46  auto chipTransfo =
47  std::make_shared<PhotometryTransfoSpatiallyInvariant>(photoCalib->getCalibrationMean());
48  _chipMap[chip] =
50  }
51  // If the visit is not in the map, add it, otherwise continue.
52  if (visitPair == _visitMap.end()) {
53  auto visitTransfo = std::make_shared<PhotometryTransfoChebyshev>(visitDegree, focalPlaneBBox);
54  _visitMap[visit] =
56  }
57  }
58 
59  // Fix one chip mapping, to remove the degeneracy from the system.
60  _chipMap.at(constrainedChip)->setFixed(true);
61 
62  // Now create the ccdImage mappings, which are combinations of the chip/visit mappings above.
63  _myMap.reserve(ccdImageList.size()); // we know how big it will be, so pre-allocate space.
64  for (auto const &ccdImage : ccdImageList) {
65  auto visit = ccdImage->getVisit();
66  auto chip = ccdImage->getCcdId();
67  _myMap.emplace(ccdImage->getHashKey(),
69  new ChipVisitPhotometryMapping(_chipMap[chip], _visitMap[visit])));
70  }
71  LOGLS_INFO(_log, "Got " << _chipMap.size() << " chip mappings and " << _visitMap.size()
72  << " visit mappings; holding chip " << constrainedChip << " fixed.");
73  LOGLS_DEBUG(_log, "CcdImage map has " << _myMap.size() << " mappings, with " << _myMap.bucket_count()
74  << " buckets and a load factor of " << _myMap.load_factor());
75 }
76 
77 unsigned ConstrainedPhotometryModel::assignIndices(std::string const &whatToFit, unsigned firstIndex) {
78  // TODO DM-8046: currently ignoring whatToFit: eventually implement configurability.
79  unsigned index = firstIndex;
80  for (auto &i : _chipMap) {
81  auto mapping = i.second.get();
82  // Don't assign indices for fixed parameters.
83  if (mapping->isFixed()) continue;
84  mapping->setIndex(index);
85  index += mapping->getNpar();
86  }
87  for (auto &i : _visitMap) {
88  auto mapping = i.second.get();
89  mapping->setIndex(index);
90  index += mapping->getNpar();
91  }
92  return index;
93 }
94 
95 void ConstrainedPhotometryModel::offsetParams(Eigen::VectorXd const &delta) {
96  for (auto &i : _chipMap) {
97  auto mapping = i.second.get();
98  // Don't offset indices for fixed parameters.
99  if (mapping->isFixed()) continue;
100  mapping->offsetParams(delta.segment(mapping->getIndex(), mapping->getNpar()));
101  }
102  for (auto &i : _visitMap) {
103  auto mapping = i.second.get();
104  mapping->offsetParams(delta.segment(mapping->getIndex(), mapping->getNpar()));
105  }
106 }
107 
109  double instFlux) const {
110  auto mapping = findMapping(ccdImage);
111  return mapping->transform(measuredStar, instFlux);
112 }
113 
115  std::vector<unsigned> &indices) const {
116  auto mapping = findMapping(ccdImage);
117  mapping->getMappingIndices(indices);
118 }
119 
121  CcdImage const &ccdImage,
122  Eigen::VectorXd &derivatives) const {
123  auto mapping = findMapping(ccdImage);
124  mapping->computeParameterDerivatives(measuredStar, measuredStar.getInstFlux(), derivatives);
125 }
126 
127 namespace {
128 // Convert photoTransfo's way of storing Chebyshev coefficients into the format wanted by ChebyMap.
129 ndarray::Array<double, 2, 2> toChebyMapCoeffs(std::shared_ptr<PhotometryTransfoChebyshev> transfo) {
130  auto coeffs = transfo->getCoefficients();
131  // 4 x nPar: ChebyMap wants rows that look like (a_ij, 1, i, j) for out += a_ij*T_i(x)*T_j(y)
132  ndarray::Array<double, 2, 2> chebyCoeffs = allocate(ndarray::makeVector(transfo->getNpar(), 4));
133  Eigen::VectorXd::Index k = 0;
134  auto degree = transfo->getDegree();
135  for (ndarray::Size j = 0; j <= degree; ++j) {
136  ndarray::Size const iMax = degree - j; // to save re-computing `i+j <= degree` every inner step.
137  for (ndarray::Size i = 0; i <= iMax; ++i, ++k) {
138  chebyCoeffs[k][0] = coeffs[j][i];
139  chebyCoeffs[k][1] = 1;
140  chebyCoeffs[k][2] = i;
141  chebyCoeffs[k][3] = j;
142  }
143  }
144  return chebyCoeffs;
145 }
146 } // namespace
147 
149  CcdImage const &ccdImage) const {
150  auto oldPhotoCalib = ccdImage.getPhotoCalib();
151  auto detector = ccdImage.getDetector();
152  auto ccdBBox = detector->getBBox();
153  ChipVisitPhotometryMapping *mapping = dynamic_cast<ChipVisitPhotometryMapping *>(findMapping(ccdImage));
154  // There should be no way in which we can get to this point and not have a ChipVisitMapping,
155  // so blow up if we don't.
156  assert(mapping != nullptr);
157  auto pixToFocal = detector->getTransform(afw::cameraGeom::PIXELS, afw::cameraGeom::FOCAL_PLANE);
158  // We know it's a Chebyshev transfo because we created it as such, so blow up if it's not.
159  auto visitTransfo =
161  assert(visitTransfo != nullptr);
162  auto focalBBox = visitTransfo->getBBox();
163 
164  // Unravel our chebyshev coefficients to build an astshim::ChebyMap.
165  auto coeff_f = toChebyMapCoeffs(
166  std::dynamic_pointer_cast<PhotometryTransfoChebyshev>(mapping->getVisitMapping()->getTransfo()));
167  // Bounds are the bbox
168  std::vector<double> lowerBound = {focalBBox.getMinX(), focalBBox.getMinY()};
169  std::vector<double> upperBound = {focalBBox.getMaxX(), focalBBox.getMaxY()};
170  ast::ChebyMap chebyMap(coeff_f, 1, lowerBound, upperBound);
171 
172  // The chip part is easy: zoom map with the single value as the "zoom" factor.
173  ast::ZoomMap zoomMap(1, mapping->getChipMapping()->getParameters()[0]);
174 
175  // Now stitch them all together.
176  auto transform =
177  afw::geom::TransformPoint2ToGeneric(pixToFocal->getFrameSet()->then(chebyMap).then(zoomMap));
178  // NOTE: TransformBoundedField does not yet implement mean(), so we have to compute it here.
179  double mean = mapping->getChipMapping()->getParameters()[0] * visitTransfo->mean();
180  auto boundedField = std::make_shared<afw::math::TransformBoundedField>(ccdBBox, transform);
181  return std::make_shared<afw::image::PhotoCalib>(mean, oldPhotoCalib->getCalibrationErr(), boundedField,
182  false);
183 }
184 
186  for (auto &i : _chipMap) {
187  i.second->dump(stream);
188  stream << std::endl;
189  }
190  stream << std::endl;
191  for (auto &i : _visitMap) {
192  i.second->dump(stream);
193  stream << std::endl;
194  }
195 }
196 
197 PhotometryMappingBase *ConstrainedPhotometryModel::findMapping(CcdImage const &ccdImage) const {
198  auto i = _myMap.find(ccdImage.getHashKey());
199  if (i == _myMap.end())
201  "ConstrainedPhotometryModel cannot find CcdImage " + ccdImage.getName());
202  return i->second.get();
203 }
204 
205 } // namespace jointcal
206 } // namespace lsst
Transform< Point2Endpoint, GenericEndpoint > TransformPoint2ToGeneric
Relates transfo(s) to their position in the fitting matrix and allows interaction with the transfo(s)...
std::string getName() const
Return the _name that identifies this ccdImage.
Definition: CcdImage.h:49
CameraSysPrefix const PIXELS
std::shared_ptr< PhotometryMapping > getChipMapping() const
T endl(T... args)
T bucket_count(T... args)
T end(T... args)
T load_factor(T... args)
STL class.
unsigned assignIndices(std::string const &whatToFit, unsigned firstIndex) override
Assign indices to parameters involved in mappings, starting at firstIndex.
T at(T... args)
std::shared_ptr< PhotometryMapping > getVisitMapping() const
std::shared_ptr< afw::image::PhotoCalib > getPhotoCalib() const
Return the exposure&#39;s photometric calibration.
Definition: CcdImage.h:114
#define LOGLS_DEBUG(logger, message)
Class for a simple mapping implementing a generic Gtransfo.
objects measured on actual images.
Definition: MeasuredStar.h:18
T dynamic_pointer_cast(T... args)
T infinity(T... args)
void computeParameterDerivatives(MeasuredStar const &measuredStar, CcdImage const &ccdImage, Eigen::VectorXd &derivatives) const override
Compute the parametric derivatives of this model.
T move(T... args)
#define LOGLS_INFO(logger, message)
T find(T... args)
T size(T... args)
void dump(std::ostream &stream=std::cout) const override
Dump the contents of the transfos, for debugging.
#define LSST_EXCEPT(type,...)
void offsetParams(Eigen::VectorXd const &delta) override
Offset the parameters by the provided amounts.
T pow(T... args)
double transform(CcdImage const &ccdImage, MeasuredStar const &star, double instFlux) const override
Return the on-sky transformed flux for measuredStar on ccdImage.
T emplace(T... args)
nth-degree 2d Chebyshev photometry transfo.
std::shared_ptr< afw::image::PhotoCalib > toPhotoCalib(CcdImage const &ccdImage) const override
Return the mapping of ccdImage represented as a PhotoCalib.
CcdImageKey getHashKey() const
Definition: CcdImage.h:105
std::shared_ptr< afw::cameraGeom::Detector > getDetector() const
Definition: CcdImage.h:103
Handler of an actual image from a single CCD.
Definition: CcdImage.h:34
#define LOG_GET(logger)
ConstrainedPhotometryModel(CcdImageList const &ccdImageList, afw::geom::Box2D const &focalPlaneBBox, int visitDegree=7)
Construct a constrained photometry model.
A mapping containing a single photometryTransfo.
STL class.
CameraSys const FOCAL_PLANE
void getMappingIndices(CcdImage const &ccdImage, std::vector< unsigned > &indices) const override
Get how this set of parameters (of length Npar()) map into the "grand" fit.
A two-level photometric transform: one for the ccd and one for the visit.
T reserve(T... args)