LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
PhotoCalib.cc
Go to the documentation of this file.
1 /*
2  * LSST Data Management System
3  * Copyright 2017 LSST Corporation.
4  *
5  * This product includes software developed by the
6  * LSST Project (http://www.lsst.org/).
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the LSST License Statement and
19  * the GNU General Public License along with this program. If not,
20  * see <http://www.lsstcorp.org/LegalNotices/>.
21  */
22 
23 #include <cmath>
24 
25 #include "lsst/geom/Point.h"
28 #include "lsst/afw/table/Source.h"
32 #include "lsst/pex/exceptions.h"
33 #include "ndarray.h"
34 #include "lsst/afw/table/io/Persistable.cc"
35 #include "lsst/utils/Magnitude.h"
36 
37 namespace lsst {
38 namespace afw {
39 
42 
43 namespace image {
44 
45 // ------------------- helpers -------------------
46 
47 namespace {
48 
49 int const SERIALIZATION_VERSION = 1;
50 
51 double toNanojansky(double instFlux, double scale) { return instFlux * scale; }
52 
53 double toMagnitude(double instFlux, double scale) { return utils::nanojanskyToABMagnitude(instFlux * scale); }
54 
55 double toInstFluxFromMagnitude(double magnitude, double scale) {
56  // Note: flux[nJy] / scale = instFlux[counts]
57  return utils::ABMagnitudeToNanojansky(magnitude) / scale;
58 }
59 
60 double toNanojanskyErr(double instFlux, double instFluxErr, double scale, double scaleErr,
61  double nanojansky) {
62  return std::abs(nanojansky) * hypot(instFluxErr / instFlux, scaleErr / scale);
63 }
64 
78 void toNanojanskyVariance(ndarray::Array<float const, 2, 1> const &instFlux,
79  ndarray::Array<float const, 2, 1> const &instFluxVar, float scaleErr,
80  ndarray::Array<float const, 2, 1> const &flux, ndarray::Array<float, 2, 1> out) {
81  auto eigenFlux = ndarray::asEigen<Eigen::ArrayXpr>(flux);
82  auto eigenInstFluxVar = ndarray::asEigen<Eigen::ArrayXpr>(instFluxVar);
83  auto eigenInstFlux = ndarray::asEigen<Eigen::ArrayXpr>(instFlux);
84  auto eigenOut = ndarray::asEigen<Eigen::ArrayXpr>(out);
85  eigenOut = eigenFlux.square() *
86  (eigenInstFluxVar / eigenInstFlux.square() + (scaleErr / eigenFlux * eigenInstFlux).square());
87 }
88 
89 double toMagnitudeErr(double instFlux, double instFluxErr, double scale, double scaleErr) {
90  return 2.5 / log(10.0) * hypot(instFluxErr / instFlux, scaleErr / scale);
91 }
92 
93 } // anonymous namespace
94 
95 // ------------------- Conversions to nanojansky -------------------
96 
97 double PhotoCalib::instFluxToNanojansky(double instFlux, lsst::geom::Point<double, 2> const &point) const {
98  return toNanojansky(instFlux, evaluate(point));
99 }
100 
101 double PhotoCalib::instFluxToNanojansky(double instFlux) const {
102  return toNanojansky(instFlux, _calibrationMean);
103 }
104 
105 Measurement PhotoCalib::instFluxToNanojansky(double instFlux, double instFluxErr,
106  lsst::geom::Point<double, 2> const &point) const {
107  double calibration, error, nanojansky;
108  calibration = evaluate(point);
109  nanojansky = toNanojansky(instFlux, calibration);
110  error = toNanojanskyErr(instFlux, instFluxErr, calibration, _calibrationErr, nanojansky);
111  return Measurement(nanojansky, error);
112 }
113 
114 Measurement PhotoCalib::instFluxToNanojansky(double instFlux, double instFluxErr) const {
115  double nanojansky = toNanojansky(instFlux, _calibrationMean);
116  double error = toNanojanskyErr(instFlux, instFluxErr, _calibrationMean, _calibrationErr, nanojansky);
117  return Measurement(nanojansky, error);
118 }
119 
120 Measurement PhotoCalib::instFluxToNanojansky(afw::table::SourceRecord const &sourceRecord,
121  std::string const &instFluxField) const {
122  auto position = sourceRecord.getCentroid();
123  auto instFluxKey = sourceRecord.getSchema().find<double>(instFluxField + "_instFlux").key;
124  auto instFluxErrKey = sourceRecord.getSchema().find<double>(instFluxField + "_instFluxErr").key;
125  return instFluxToNanojansky(sourceRecord.get(instFluxKey), sourceRecord.get(instFluxErrKey), position);
126 }
127 ndarray::Array<double, 2, 2> PhotoCalib::instFluxToNanojansky(afw::table::SourceCatalog const &sourceCatalog,
128  std::string const &instFluxField) const {
129  ndarray::Array<double, 2, 2> result =
130  ndarray::allocate(ndarray::makeVector(int(sourceCatalog.size()), 2));
131  instFluxToNanojanskyArray(sourceCatalog, instFluxField, result);
132  return result;
133 }
134 
135 void PhotoCalib::instFluxToNanojansky(afw::table::SourceCatalog &sourceCatalog,
136  std::string const &instFluxField, std::string const &outField) const {
137  auto instFluxKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFlux").key;
138  auto instFluxErrKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFluxErr").key;
139  auto nanojanskyKey = sourceCatalog.getSchema().find<double>(outField + "_flux").key;
140  auto nanojanskyErrKey = sourceCatalog.getSchema().find<double>(outField + "_fluxErr").key;
141  for (auto &record : sourceCatalog) {
142  auto result = instFluxToNanojansky(record.get(instFluxKey), record.get(instFluxErrKey),
143  record.getCentroid());
144  record.set(nanojanskyKey, result.value);
145  record.set(nanojanskyErrKey, result.error);
146  }
147 }
148 
149 // ------------------- Conversions to Magnitudes -------------------
150 
151 double PhotoCalib::instFluxToMagnitude(double instFlux, lsst::geom::Point<double, 2> const &point) const {
152  return toMagnitude(instFlux, evaluate(point));
153 }
154 
155 double PhotoCalib::instFluxToMagnitude(double instFlux) const {
156  return toMagnitude(instFlux, _calibrationMean);
157 }
158 
159 Measurement PhotoCalib::instFluxToMagnitude(double instFlux, double instFluxErr,
160  lsst::geom::Point<double, 2> const &point) const {
161  double calibration, error, magnitude;
162  calibration = evaluate(point);
163  magnitude = toMagnitude(instFlux, calibration);
164  error = toMagnitudeErr(instFlux, instFluxErr, calibration, _calibrationErr);
165  return Measurement(magnitude, error);
166 }
167 
168 Measurement PhotoCalib::instFluxToMagnitude(double instFlux, double instFluxErr) const {
169  double magnitude = toMagnitude(instFlux, _calibrationMean);
170  double error = toMagnitudeErr(instFlux, instFluxErr, _calibrationMean, _calibrationErr);
171  return Measurement(magnitude, error);
172 }
173 
174 Measurement PhotoCalib::instFluxToMagnitude(afw::table::SourceRecord const &sourceRecord,
175  std::string const &instFluxField) const {
176  auto position = sourceRecord.getCentroid();
177  auto instFluxKey = sourceRecord.getSchema().find<double>(instFluxField + "_instFlux").key;
178  auto instFluxErrKey = sourceRecord.getSchema().find<double>(instFluxField + "_instFluxErr").key;
179  return instFluxToMagnitude(sourceRecord.get(instFluxKey), sourceRecord.get(instFluxErrKey), position);
180 }
181 
182 ndarray::Array<double, 2, 2> PhotoCalib::instFluxToMagnitude(afw::table::SourceCatalog const &sourceCatalog,
183  std::string const &instFluxField) const {
184  ndarray::Array<double, 2, 2> result =
185  ndarray::allocate(ndarray::makeVector(int(sourceCatalog.size()), 2));
186  instFluxToMagnitudeArray(sourceCatalog, instFluxField, result);
187  return result;
188 }
189 
190 void PhotoCalib::instFluxToMagnitude(afw::table::SourceCatalog &sourceCatalog,
191  std::string const &instFluxField, std::string const &outField) const {
192  auto instFluxKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFlux").key;
193  auto instFluxErrKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFluxErr").key;
194  auto magKey = sourceCatalog.getSchema().find<double>(outField + "_mag").key;
195  auto magErrKey = sourceCatalog.getSchema().find<double>(outField + "_magErr").key;
196  for (auto &record : sourceCatalog) {
197  auto result = instFluxToMagnitude(record.get(instFluxKey), record.get(instFluxErrKey),
198  record.getCentroid());
199  record.set(magKey, result.value);
200  record.set(magErrKey, result.error);
201  }
202 }
203 
204 // ------------------- other utility methods -------------------
205 
206 double PhotoCalib::magnitudeToInstFlux(double magnitude) const {
207  return toInstFluxFromMagnitude(magnitude, _calibrationMean);
208 }
209 
210 double PhotoCalib::magnitudeToInstFlux(double magnitude, lsst::geom::Point<double, 2> const &point) const {
211  return toInstFluxFromMagnitude(magnitude, evaluate(point));
212 }
213 
214 std::shared_ptr<math::BoundedField> PhotoCalib::computeScaledCalibration() const {
215  return *(_calibration) / _calibrationMean;
216 }
217 
219  throw LSST_EXCEPT(pex::exceptions::LogicError, "Not Implemented: See DM-10154.");
220 }
221 
222 bool PhotoCalib::operator==(PhotoCalib const &rhs) const {
223  return (_calibrationMean == rhs._calibrationMean && _calibrationErr == rhs._calibrationErr &&
224  (*_calibration) == *(rhs._calibration));
225 }
226 
227 double PhotoCalib::computeCalibrationMean(std::shared_ptr<afw::math::BoundedField> calibration) const {
228  return calibration->mean();
229 }
230 
232  if (photoCalib._isConstant)
233  os << "spatially constant with ";
234  else
235  os << *(photoCalib._calibration) << " with ";
236  return os << "mean: " << photoCalib._calibrationMean << " error: " << photoCalib._calibrationErr;
237 }
238 
239 MaskedImage<float> PhotoCalib::calibrateImage(MaskedImage<float> const &maskedImage,
240  bool includeScaleUncertainty) const {
241  // Deep copy construct, as we're mutiplying in-place.
242  auto result = MaskedImage<float>(maskedImage, true);
243 
244  if (_isConstant) {
245  *(result.getImage()) *= _calibrationMean;
246  } else {
247  _calibration->multiplyImage(*(result.getImage()), true); // only in the overlap region
248  }
249  if (includeScaleUncertainty) {
250  toNanojanskyVariance(maskedImage.getImage()->getArray(), maskedImage.getVariance()->getArray(),
251  _calibrationErr, result.getImage()->getArray(),
252  result.getVariance()->getArray());
253  } else {
254  toNanojanskyVariance(maskedImage.getImage()->getArray(), maskedImage.getVariance()->getArray(), 0,
255  result.getImage()->getArray(), result.getVariance()->getArray());
256  }
257 
258  return result;
259 }
260 
261 // ------------------- persistence -------------------
262 
263 namespace {
264 
265 class PhotoCalibSchema {
266 public:
267  table::Schema schema;
268  table::Key<double> calibrationMean;
269  table::Key<double> calibrationErr;
270  table::Key<table::Flag> isConstant;
271  table::Key<int> field;
272  table::Key<int> version;
273 
274  // No copying
275  PhotoCalibSchema(PhotoCalibSchema const &) = delete;
276  PhotoCalibSchema &operator=(PhotoCalibSchema const &) = delete;
277  // No moving
278  PhotoCalibSchema(PhotoCalibSchema &&) = delete;
279  PhotoCalibSchema &operator=(PhotoCalibSchema &&) = delete;
280 
281  static PhotoCalibSchema const &get() {
282  static PhotoCalibSchema const instance;
283  return instance;
284  }
285 
286 private:
287  PhotoCalibSchema()
288  : schema(),
289  calibrationMean(schema.addField<double>(
290  "calibrationMean", "mean calibration on this PhotoCalib's domain", "count")),
292  schema.addField<double>("calibrationErr", "1-sigma error on calibrationMean", "count")),
293  isConstant(schema.addField<table::Flag>("isConstant", "Is this spatially-constant?")),
294  field(schema.addField<int>("field", "archive ID of the BoundedField object")),
295  version(schema.addField<int>("version", "version of this PhotoCalib")) {
296  schema.getCitizen().markPersistent();
297  }
298 };
299 
300 class PhotoCalibFactory : public table::io::PersistableFactory {
301 public:
302  PTR(table::io::Persistable)
303  read(InputArchive const &archive, CatalogVector const &catalogs) const override {
304  table::BaseRecord const &record = catalogs.front().front();
305  PhotoCalibSchema const &keys = PhotoCalibSchema::get();
306  int version = getVersion(record);
307  if (version < 1) {
308  throw(pex::exceptions::RuntimeError("Unsupported version (version 0 was defined in maggies): " +
309  std::to_string(version)));
310  }
311  return std::make_shared<PhotoCalib>(record.get(keys.calibrationMean), record.get(keys.calibrationErr),
312  archive.get<afw::math::BoundedField>(record.get(keys.field)),
313  record.get(keys.isConstant));
314  }
315 
316  PhotoCalibFactory(std::string const &name) : afw::table::io::PersistableFactory(name) {}
317 
318 protected:
319  int getVersion(table::BaseRecord const &record) const {
320  int version = -1;
321  try {
322  std::string versionName = "version";
323  auto versionKey = record.getSchema().find<int>(versionName);
324  version = record.get(versionKey.key);
325  } catch (const pex::exceptions::NotFoundError &) {
326  // un-versioned files are version 0
327  version = 0;
328  }
329  return version;
330  }
331 };
332 
333 std::string getPhotoCalibPersistenceName() { return "PhotoCalib"; }
334 
335 PhotoCalibFactory registration(getPhotoCalibPersistenceName());
336 
337 } // anonymous namespace
338 
339 std::string PhotoCalib::getPersistenceName() const { return getPhotoCalibPersistenceName(); }
340 
341 void PhotoCalib::write(OutputArchiveHandle &handle) const {
342  PhotoCalibSchema const &keys = PhotoCalibSchema::get();
343  table::BaseCatalog catalog = handle.makeCatalog(keys.schema);
344  auto record = catalog.addNew();
345  record->set(keys.calibrationMean, _calibrationMean);
346  record->set(keys.calibrationErr, _calibrationErr);
347  record->set(keys.isConstant, _isConstant);
348  record->set(keys.field, handle.put(_calibration));
349  record->set(keys.version, SERIALIZATION_VERSION);
350  handle.saveCatalog(catalog);
351 }
352 
353 // ------------------- private/protected helpers -------------------
354 
355 double PhotoCalib::evaluate(lsst::geom::Point<double, 2> const &point) const {
356  if (_isConstant)
357  return _calibrationMean;
358  else
359  return _calibration->evaluate(point);
360 }
361 
362 void PhotoCalib::instFluxToNanojanskyArray(afw::table::SourceCatalog const &sourceCatalog,
363  std::string const &instFluxField,
364  ndarray::Array<double, 2, 2> result) const {
365  double instFlux, instFluxErr, nanojansky, calibration;
366  auto instFluxKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFlux").key;
367  auto instFluxErrKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFluxErr").key;
368  auto iter = result.begin();
369  for (auto const &rec : sourceCatalog) {
370  instFlux = rec.get(instFluxKey);
371  instFluxErr = rec.get(instFluxErrKey);
372  calibration = evaluate(rec.getCentroid());
373  nanojansky = toNanojansky(instFlux, calibration);
374  (*iter)[0] = nanojansky;
375  (*iter)[1] = toNanojanskyErr(instFlux, instFluxErr, calibration, _calibrationErr, nanojansky);
376  iter++;
377  }
378 }
379 
380 void PhotoCalib::instFluxToMagnitudeArray(afw::table::SourceCatalog const &sourceCatalog,
381  std::string const &instFluxField,
382  ndarray::Array<double, 2, 2> result) const {
383  double instFlux, instFluxErr, calibration;
384  auto instFluxKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFlux").key;
385  auto instFluxErrKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFluxErr").key;
386  auto iter = result.begin();
387  for (auto const &rec : sourceCatalog) {
388  instFlux = rec.get(instFluxKey);
389  instFluxErr = rec.get(instFluxErrKey);
390  calibration = evaluate(rec.getCentroid());
391  (*iter)[0] = toMagnitude(instFlux, calibration);
392  (*iter)[1] = toMagnitudeErr(instFlux, instFluxErr, calibration, _calibrationErr);
393  iter++;
394  }
395 }
396 
398  double calibration = 0.0, calibrationErr = 0.0;
399 
400  auto key = "FLUXMAG0";
401  if (metadata.exists(key)) {
402  double fluxMag0 = metadata.getAsDouble(key);
403  if (strip) metadata.remove(key);
404 
405  calibration = utils::referenceFlux / fluxMag0;
406  key = "FLUXMAG0ERR";
407  if (metadata.exists(key)) {
408  double fluxMag0Err = metadata.getAsDouble(key);
409  calibrationErr = utils::referenceFlux * fluxMag0Err / std::pow(fluxMag0, 2);
410  if (strip) metadata.remove(key);
411  }
412  } else {
413  return nullptr;
414  }
415 
416  return std::make_shared<PhotoCalib>(calibration, calibrationErr);
417 }
418 
419 } // namespace image
420 } // namespace afw
421 } // namespace lsst
double getAsDouble(std::string const &name) const
Get the last value for any arithmetic property name (possibly hierarchical).
VariancePtr getVariance() const
Return a (shared_ptr to) the MaskedImage&#39;s variance.
Definition: MaskedImage.h:1091
Angle abs(Angle const &a)
Definition: Angle.h:106
static std::shared_ptr< T > dynamicCast(std::shared_ptr< Persistable > const &ptr)
Dynamically cast a shared_ptr.
Definition: Persistable.cc:18
int put(Persistable const *obj, bool permissive=false)
Save an object to the archive and return a unique ID that can be used to retrieve it from an InputArc...
An object passed to Persistable::write to allow it to persist itself.
The photometric calibration of an exposure.
Definition: PhotoCalib.h:111
def scale(algorithm, min, max=None, frame=None)
Definition: ds9.py:109
T to_string(T... args)
py::object result
Definition: schema.cc:284
table::Key< int > field
Definition: PhotoCalib.cc:271
A value and its error.
Definition: PhotoCalib.h:50
virtual void remove(std::string const &name)
Remove all values for a property name (possibly hierarchical).
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Definition: BaseRecord.h:125
std::shared_ptr< PhotoCalib > makePhotoCalib(daf::base::PropertySet &metadata, bool strip=false)
Construct a PhotoCalib from FITS FLUXMAG0/FLUXMAG0ERR keywords.
Definition: PhotoCalib.cc:397
ImagePtr getImage() const
Return a (shared_ptr to) the MaskedImage&#39;s image.
Definition: MaskedImage.h:1058
STL class.
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
Definition: Schema.cc:656
A base class for image defects.
#define PTR(...)
Definition: base.h:41
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:74
Utilities for converting between flux and magnitude in C++.
Schema getSchema() const
Return the Schema that holds this record&#39;s fields and keys.
Definition: BaseRecord.h:54
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
table::Key< double > calibrationMean
Definition: PhotoCalib.cc:268
Reports errors in the logical structure of the program.
Definition: Runtime.h:46
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
std::ostream & operator<<(std::ostream &os, PhotoCalib const &photoCalib)
Definition: PhotoCalib.cc:231
T hypot(T... args)
T find(T... args)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
double nanojanskyToABMagnitude(double flux)
Convert a flux in nanojansky to AB magnitude.
Definition: Magnitude.cc:30
table::Schema schema
Definition: PhotoCalib.cc:267
Key< U > key
Definition: Schema.cc:281
T pow(T... args)
Class for storing generic metadata.
Definition: PropertySet.h:68
ItemVariant const * other
Definition: Schema.cc:56
Record class that contains measurements made on a single exposure.
Definition: Source.h:82
table::Key< table::Flag > isConstant
Definition: PhotoCalib.cc:270
table::Key< double > fluxMag0
Definition: Calib.cc:378
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
table::Key< int > version
Definition: PhotoCalib.cc:272
const double referenceFlux
The Oke & Gunn (1983) AB magnitude reference flux, in nJy (often approximated as 3631.0).
Definition: Magnitude.h:46
table::Key< double > fluxMag0Err
Definition: Calib.cc:379
STL class.
double ABMagnitudeToNanojansky(double magnitude)
Convert an AB magnitude to a flux in nanojansky.
Definition: Magnitude.cc:32
Implementation of the Photometric Calibration class.
bool strip
Definition: fits.cc:831
table::Key< double > calibrationErr
Definition: PhotoCalib.cc:269
std::ostream * os
Definition: Schema.cc:746
CentroidSlotDefinition::MeasValue getCentroid() const
Get the value of the Centroid slot measurement.
Definition: Source.h:644
std::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
Definition: Catalog.h:472