lsst.jointcal  16.0-7-g6e35192
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, int visitOrder)
24  : _fittingChips(false), _fittingVisits(false) {
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.getX(), 2) + std::pow(center.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<FluxTransfoSpatiallyInvariant>(photoCalib->getCalibrationMean());
48  _chipMap[chip] = std::make_unique<PhotometryMapping>(std::move(chipTransfo));
49  }
50  // If the visit is not in the map, add it, otherwise continue.
51  if (visitPair == _visitMap.end()) {
52  auto visitTransfo = std::make_shared<PhotometryTransfoChebyshev>(visitOrder, focalPlaneBBox);
53  _visitMap[visit] = std::make_unique<PhotometryMapping>(std::move(visitTransfo));
54  }
55  }
56 
57  // Fix one chip mapping, to remove the degeneracy from the system.
58  _chipMap.at(constrainedChip)->setFixed(true);
59 
60  // Now create the ccdImage mappings, which are combinations of the chip/visit mappings above.
61  _chipVisitMap.reserve(
62  ccdImageList.size()); // we know how chipVisitbig it will be, so pre-allocate space.
63  for (auto const &ccdImage : ccdImageList) {
64  auto visit = ccdImage->getVisit();
65  auto chip = ccdImage->getCcdId();
66  _chipVisitMap.emplace(ccdImage->getHashKey(),
67  std::make_unique<ChipVisitPhotometryMapping>(_chipMap[chip], _visitMap[visit]));
68  }
69  LOGLS_INFO(_log, "Got " << _chipMap.size() << " chip mappings and " << _visitMap.size()
70  << " visit mappings; holding chip " << constrainedChip << " fixed ("
71  << getTotalParameters() << " total parameters).");
72  LOGLS_DEBUG(_log, "CcdImage map has " << _chipVisitMap.size() << " mappings, with "
73  << _chipVisitMap.bucket_count() << " buckets and a load factor of "
74  << _chipVisitMap.load_factor());
75 }
76 
77 unsigned ConstrainedPhotometryModel::assignIndices(std::string const &whatToFit, unsigned firstIndex) {
78  unsigned index = firstIndex;
79  if (whatToFit.find("Model") == std::string::npos) {
80  LOGLS_WARN(_log, "assignIndices was called and Model is *not* in whatToFit");
81  return index;
82  }
83 
84  // If we got here, "Model" is definitely in whatToFit.
85  _fittingChips = (whatToFit.find("ModelChip") != std::string::npos);
86  _fittingVisits = (whatToFit.find("ModelVisit") != std::string::npos);
87  // If nothing more than "Model" is specified, it means fit everything.
88  if ((!_fittingChips) && (!_fittingVisits)) {
89  _fittingChips = _fittingVisits = true;
90  }
91 
92  if (_fittingChips) {
93  for (auto &idMapping : _chipMap) {
94  auto mapping = idMapping.second.get();
95  // Don't assign indices for fixed parameters.
96  if (mapping->isFixed()) continue;
97  mapping->setIndex(index);
98  index += mapping->getNpar();
99  }
100  }
101  if (_fittingVisits) {
102  for (auto &idMapping : _visitMap) {
103  auto mapping = idMapping.second.get();
104  mapping->setIndex(index);
105  index += mapping->getNpar();
106  }
107  }
108  for (auto &idMapping : _chipVisitMap) {
109  idMapping.second->setWhatToFit(_fittingChips, _fittingVisits);
110  }
111  return index;
112 }
113 
114 void ConstrainedPhotometryModel::offsetParams(Eigen::VectorXd const &delta) {
115  if (_fittingChips) {
116  for (auto &idMapping : _chipMap) {
117  auto mapping = idMapping.second.get();
118  // Don't offset indices for fixed parameters.
119  if (mapping->isFixed()) continue;
120  mapping->offsetParams(delta.segment(mapping->getIndex(), mapping->getNpar()));
121  }
122  }
123  if (_fittingVisits) {
124  for (auto &idMapping : _visitMap) {
125  auto mapping = idMapping.second.get();
126  mapping->offsetParams(delta.segment(mapping->getIndex(), mapping->getNpar()));
127  }
128  }
129 }
130 
132  MeasuredStar const &measuredStar) const {
133  return transform(ccdImage, measuredStar) - measuredStar.getFittedStar()->getFlux();
134 }
135 
137  MeasuredStar const &measuredStar) const {
138  auto mapping = findMapping(ccdImage);
139  return mapping->transform(measuredStar, measuredStar.getInstFlux());
140 }
141 
143  MeasuredStar const &measuredStar) const {
144  auto mapping = findMapping(ccdImage);
145  return mapping->transformError(measuredStar, measuredStar.getInstFlux(), measuredStar.getInstFluxErr());
146 }
147 
149  for (auto &idMapping : _chipMap) {
150  idMapping.second.get()->freezeErrorTransform();
151  }
152  for (auto &idMapping : _visitMap) {
153  idMapping.second.get()->freezeErrorTransform();
154  }
155 }
156 
158  std::vector<unsigned> &indices) const {
159  auto mapping = findMapping(ccdImage);
160  mapping->getMappingIndices(indices);
161 }
162 
164  int total = 0;
165  for (auto &idMapping : _chipMap) {
166  total += idMapping.second->getNpar();
167  }
168  for (auto &idMapping : _visitMap) {
169  total += idMapping.second->getNpar();
170  }
171  return total;
172 }
173 
175  CcdImage const &ccdImage,
176  Eigen::VectorXd &derivatives) const {
177  auto mapping = findMapping(ccdImage);
178  mapping->computeParameterDerivatives(measuredStar, measuredStar.getInstFlux(), derivatives);
179 }
180 
181 namespace {
182 // Convert photoTransfo's way of storing Chebyshev coefficients into the format wanted by ChebyMap.
183 ndarray::Array<double, 2, 2> toChebyMapCoeffs(std::shared_ptr<PhotometryTransfoChebyshev> transfo) {
184  auto coeffs = transfo->getCoefficients();
185  // 4 x nPar: ChebyMap wants rows that look like (a_ij, 1, i, j) for out += a_ij*T_i(x)*T_j(y)
186  ndarray::Array<double, 2, 2> chebyCoeffs = allocate(ndarray::makeVector(transfo->getNpar(), 4));
187  Eigen::VectorXd::Index k = 0;
188  auto order = transfo->getOrder();
189  for (ndarray::Size j = 0; j <= order; ++j) {
190  ndarray::Size const iMax = order - j; // to save re-computing `i+j <= order` every inner step.
191  for (ndarray::Size i = 0; i <= iMax; ++i, ++k) {
192  chebyCoeffs[k][0] = coeffs[j][i];
193  chebyCoeffs[k][1] = 1;
194  chebyCoeffs[k][2] = i;
195  chebyCoeffs[k][3] = j;
196  }
197  }
198  return chebyCoeffs;
199 }
200 } // namespace
201 
203  CcdImage const &ccdImage) const {
204  auto oldPhotoCalib = ccdImage.getPhotoCalib();
205  auto detector = ccdImage.getDetector();
206  auto ccdBBox = detector->getBBox();
207  ChipVisitPhotometryMapping *mapping = dynamic_cast<ChipVisitPhotometryMapping *>(findMapping(ccdImage));
208  // There should be no way in which we can get to this point and not have a ChipVisitMapping,
209  // so blow up if we don't.
210  assert(mapping != nullptr);
211  auto pixToFocal = detector->getTransform(afw::cameraGeom::PIXELS, afw::cameraGeom::FOCAL_PLANE);
212  // We know it's a Chebyshev transfo because we created it as such, so blow up if it's not.
213  auto visitTransfo =
215  assert(visitTransfo != nullptr);
216  auto focalBBox = visitTransfo->getBBox();
217 
218  // Unravel our chebyshev coefficients to build an astshim::ChebyMap.
219  auto coeff_f = toChebyMapCoeffs(
220  std::dynamic_pointer_cast<PhotometryTransfoChebyshev>(mapping->getVisitMapping()->getTransfo()));
221  // Bounds are the bbox
222  std::vector<double> lowerBound = {focalBBox.getMinX(), focalBBox.getMinY()};
223  std::vector<double> upperBound = {focalBBox.getMaxX(), focalBBox.getMaxY()};
224 
225  afw::geom::TransformPoint2ToGeneric chebyTransform(ast::ChebyMap(coeff_f, 1, lowerBound, upperBound));
226 
227  // The chip part is easy: zoom map with the single value as the "zoom" factor.
229  ast::ZoomMap(1, mapping->getChipMapping()->getParameters()[0]));
230 
231  // Now stitch them all together.
232  auto transform = pixToFocal->then(chebyTransform)->then(zoomTransform);
233  // NOTE: TransformBoundedField does not yet implement mean(), so we have to compute it here.
234  double mean = mapping->getChipMapping()->getParameters()[0] * visitTransfo->mean();
235  auto boundedField = std::make_shared<afw::math::TransformBoundedField>(ccdBBox, *transform);
236  return std::make_shared<afw::image::PhotoCalib>(mean, oldPhotoCalib->getCalibrationErr(), boundedField,
237  false);
238 }
239 
241  for (auto &idMapping : _chipMap) {
242  idMapping.second->dump(stream);
243  stream << std::endl;
244  }
245  stream << std::endl;
246  for (auto &idMapping : _visitMap) {
247  idMapping.second->dump(stream);
248  stream << std::endl;
249  }
250 }
251 
252 PhotometryMappingBase *ConstrainedPhotometryModel::findMapping(CcdImage const &ccdImage) const {
253  auto idMapping = _chipVisitMap.find(ccdImage.getHashKey());
254  if (idMapping == _chipVisitMap.end())
256  "ConstrainedPhotometryModel cannot find CcdImage " + ccdImage.getName());
257  return idMapping->second.get();
258 }
259 
260 } // namespace jointcal
261 } // namespace lsst
#define LOGLS_WARN(logger, message)
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:56
CameraSysPrefix const PIXELS
std::shared_ptr< PhotometryMapping > getChipMapping() const
T endl(T... args)
T bucket_count(T... args)
T end(T... args)
ConstrainedPhotometryModel(CcdImageList const &ccdImageList, afw::geom::Box2D const &focalPlaneBBox, int visitOrder=7)
Construct a constrained photometry model.
T load_factor(T... args)
STL class.
unsigned assignIndices(std::string const &whatToFit, unsigned firstIndex) override
Assign indices in the full matrix to the parameters being fit in the mappings, starting at firstIndex...
T at(T... args)
std::shared_ptr< PhotometryMapping > getVisitMapping() const
int getTotalParameters() const override
Return the total number of parameters in this model.
std::shared_ptr< afw::image::PhotoCalib > getPhotoCalib() const
Return the exposure&#39;s photometric calibration.
Definition: CcdImage.h:137
#define LOGLS_DEBUG(logger, message)
Class for a simple mapping implementing a generic Gtransfo.
double computeResidual(CcdImage const &ccdImage, MeasuredStar const &measuredStar) const override
double getInstFluxErr() const
Definition: MeasuredStar.h:70
double transformError(CcdImage const &ccdImage, MeasuredStar const &measuredStar) const override
Return the on-sky transformed flux uncertainty for measuredStar on ccdImage.
objects measured on actual images.
Definition: MeasuredStar.h:19
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 &measuredStar) const override
Return the on-sky transformed flux for measuredStar on ccdImage.
T emplace(T... args)
nth-order 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:128
std::shared_ptr< afw::cameraGeom::Detector > getDetector() const
Definition: CcdImage.h:126
Handler of an actual image from a single CCD.
Definition: CcdImage.h:41
#define LOG_GET(logger)
std::shared_ptr< FittedStar > getFittedStar() const
Definition: MeasuredStar.h:85
STL class.
CameraSys const FOCAL_PLANE
void freezeErrorTransform() override
Once this routine has been called, the error transform is not modified by offsetParams().
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)