LSST Applications g063fba187b+eddd1b24d7,g0f08755f38+4a855ab515,g1653933729+a8ce1bb630,g168dd56ebc+a8ce1bb630,g1a2382251a+062a45aee3,g1dcb35cd9c+45d3fa5522,g20f6ffc8e0+4a855ab515,g217e2c1bcf+f55e51b560,g28da252d5a+7d8e536cc7,g2bbee38e9b+2d92fc7d83,g2bc492864f+2d92fc7d83,g3156d2b45e+6e55a43351,g32e5bea42b+625186cc6b,g347aa1857d+2d92fc7d83,g35bb328faa+a8ce1bb630,g3a166c0a6a+2d92fc7d83,g3e281a1b8c+c5dd892a6c,g3e8969e208+a8ce1bb630,g414038480c+5927e1bc1e,g41af890bb2+1af189bab1,g7af13505b9+7b6a50a2f8,g80478fca09+6174b7f182,g82479be7b0+5b71efbaf0,g858d7b2824+4a855ab515,g9125e01d80+a8ce1bb630,ga5288a1d22+61618a97c4,gb58c049af0+d64f4d3760,gc28159a63d+2d92fc7d83,gc5452a3dca+f4add4ffd5,gcab2d0539d+d9f5af7f69,gcf0d15dbbd+6c7e0a19ec,gda6a2b7d83+6c7e0a19ec,gdaeeff99f8+1711a396fd,ge79ae78c31+2d92fc7d83,gef2f8181fd+55fff6f525,gf0baf85859+c1f95f4921,gfa517265be+4a855ab515,gfa999e8aa5+17cd334064,w.2024.51
LSST Data Management Base Package
Loading...
Searching...
No Matches
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#include <iostream>
25#include <iomanip>
26
27#include "lsst/geom/Point.h"
33#include "lsst/pex/exceptions.h"
34#include "ndarray.h"
37
38namespace lsst {
39namespace afw {
40
43
44namespace image {
45
46// ------------------- helpers -------------------
47
48std::ostream &operator<<(std::ostream &os, Measurement const &measurement) {
51 s << "value=" << measurement.value << ", error=" << measurement.error;
52 return os << s.str();
53}
54
55namespace {
56
57int const SERIALIZATION_VERSION = 1;
58
59double toNanojansky(double instFlux, double scale) { return instFlux * scale; }
60
61double toMagnitude(double instFlux, double scale) {
62 return cpputils::nanojanskyToABMagnitude(instFlux * scale);
63}
64
65double toInstFluxFromMagnitude(double magnitude, double scale) {
66 // Note: flux[nJy] / scale = instFlux[counts]
67 return cpputils::ABMagnitudeToNanojansky(magnitude) / scale;
68}
69
70double toNanojanskyErr(double instFlux, double instFluxErr, double scale, double scaleErr,
71 double nanojansky) {
72 return std::abs(nanojansky) * hypot(instFluxErr / instFlux, scaleErr / scale);
73}
74
88void toNanojanskyVariance(ndarray::Array<float const, 2, 1> const &instFlux,
89 ndarray::Array<float const, 2, 1> const &instFluxVar, float scaleErr,
90 ndarray::Array<float const, 2, 1> const &flux, ndarray::Array<float, 2, 1> out) {
91 auto eigenFlux = ndarray::asEigen<Eigen::ArrayXpr>(flux);
92 auto eigenInstFluxVar = ndarray::asEigen<Eigen::ArrayXpr>(instFluxVar);
93 auto eigenInstFlux = ndarray::asEigen<Eigen::ArrayXpr>(instFlux);
94 auto eigenOut = ndarray::asEigen<Eigen::ArrayXpr>(out);
95 eigenOut = eigenFlux.square() * (eigenInstFluxVar / eigenInstFlux.square() +
96 (scaleErr / (eigenFlux / eigenInstFlux)).square());
97}
98
112void fromNanojanskyVariance(ndarray::Array<float const, 2, 1> const &flux,
113 ndarray::Array<float const, 2, 1> const &fluxVar, float scaleErr,
114 ndarray::Array<float const, 2, 1> const &instFlux,
115 ndarray::Array<float, 2, 1> out) {
116 auto eigenFlux = ndarray::asEigen<Eigen::ArrayXpr>(flux);
117 auto eigenFluxVar = ndarray::asEigen<Eigen::ArrayXpr>(fluxVar);
118 auto eigenInstFlux = ndarray::asEigen<Eigen::ArrayXpr>(instFlux);
119 auto eigenOut = ndarray::asEigen<Eigen::ArrayXpr>(out);
120 eigenOut = eigenInstFlux.square() *
121 (eigenFluxVar / eigenFlux.square() - (scaleErr / (eigenFlux / eigenInstFlux)).square());
122}
123
124double toMagnitudeErr(double instFlux, double instFluxErr, double scale, double scaleErr) {
125 return 2.5 / std::log(10.0) * hypot(instFluxErr / instFlux, scaleErr / scale);
126}
127
128} // anonymous namespace
129
130// ------------------- Conversions to nanojansky -------------------
131
132double PhotoCalib::instFluxToNanojansky(double instFlux, lsst::geom::Point<double, 2> const &point) const {
133 return toNanojansky(instFlux, evaluate(point));
134}
135
136double PhotoCalib::instFluxToNanojansky(double instFlux) const {
137 return toNanojansky(instFlux, _calibrationMean);
138}
139
140Measurement PhotoCalib::instFluxToNanojansky(double instFlux, double instFluxErr,
141 lsst::geom::Point<double, 2> const &point) const {
142 double calibration, error, nanojansky;
143 calibration = evaluate(point);
144 nanojansky = toNanojansky(instFlux, calibration);
145 error = toNanojanskyErr(instFlux, instFluxErr, calibration, _calibrationErr, nanojansky);
146 return Measurement(nanojansky, error);
147}
148
149Measurement PhotoCalib::instFluxToNanojansky(double instFlux, double instFluxErr) const {
150 double nanojansky = toNanojansky(instFlux, _calibrationMean);
151 double error = toNanojanskyErr(instFlux, instFluxErr, _calibrationMean, _calibrationErr, nanojansky);
152 return Measurement(nanojansky, error);
153}
154
155Measurement PhotoCalib::instFluxToNanojansky(afw::table::SourceRecord const &sourceRecord,
156 std::string const &instFluxField) const {
157 auto position = sourceRecord.getCentroid();
158 auto instFluxKey = sourceRecord.getSchema().find<double>(instFluxField + "_instFlux").key;
159 auto instFluxErrKey = sourceRecord.getSchema().find<double>(instFluxField + "_instFluxErr").key;
160 return instFluxToNanojansky(sourceRecord.get(instFluxKey), sourceRecord.get(instFluxErrKey), position);
161}
162ndarray::Array<double, 2, 2> PhotoCalib::instFluxToNanojansky(afw::table::SourceCatalog const &sourceCatalog,
163 std::string const &instFluxField) const {
164 ndarray::Array<double, 2, 2> result =
165 ndarray::allocate(ndarray::makeVector(int(sourceCatalog.size()), 2));
166 instFluxToNanojanskyArray(sourceCatalog, instFluxField, result);
167 return result;
168}
169
170void PhotoCalib::instFluxToNanojansky(afw::table::SourceCatalog &sourceCatalog,
171 std::string const &instFluxField, std::string const &outField) const {
172 auto instFluxKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFlux").key;
173 auto instFluxErrKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFluxErr").key;
174 auto nanojanskyKey = sourceCatalog.getSchema().find<double>(outField + "_flux").key;
175 auto nanojanskyErrKey = sourceCatalog.getSchema().find<double>(outField + "_fluxErr").key;
176 for (auto &record : sourceCatalog) {
177 auto result = instFluxToNanojansky(record.get(instFluxKey), record.get(instFluxErrKey),
178 record.getCentroid());
179 record.set(nanojanskyKey, result.value);
180 record.set(nanojanskyErrKey, result.error);
181 }
182}
183
184// ------------------- Conversions to Magnitudes -------------------
185
186double PhotoCalib::instFluxToMagnitude(double instFlux, lsst::geom::Point<double, 2> const &point) const {
187 return toMagnitude(instFlux, evaluate(point));
188}
189
190double PhotoCalib::instFluxToMagnitude(double instFlux) const {
191 return toMagnitude(instFlux, _calibrationMean);
192}
193
194Measurement PhotoCalib::instFluxToMagnitude(double instFlux, double instFluxErr,
195 lsst::geom::Point<double, 2> const &point) const {
196 double calibration, error, magnitude;
197 calibration = evaluate(point);
198 magnitude = toMagnitude(instFlux, calibration);
199 error = toMagnitudeErr(instFlux, instFluxErr, calibration, _calibrationErr);
200 return Measurement(magnitude, error);
201}
202
203Measurement PhotoCalib::instFluxToMagnitude(double instFlux, double instFluxErr) const {
204 double magnitude = toMagnitude(instFlux, _calibrationMean);
205 double error = toMagnitudeErr(instFlux, instFluxErr, _calibrationMean, _calibrationErr);
206 return Measurement(magnitude, error);
207}
208
209Measurement PhotoCalib::instFluxToMagnitude(afw::table::SourceRecord const &sourceRecord,
210 std::string const &instFluxField) const {
211 auto position = sourceRecord.getCentroid();
212 auto instFluxKey = sourceRecord.getSchema().find<double>(instFluxField + "_instFlux").key;
213 auto instFluxErrKey = sourceRecord.getSchema().find<double>(instFluxField + "_instFluxErr").key;
214 return instFluxToMagnitude(sourceRecord.get(instFluxKey), sourceRecord.get(instFluxErrKey), position);
215}
216
217ndarray::Array<double, 2, 2> PhotoCalib::instFluxToMagnitude(afw::table::SourceCatalog const &sourceCatalog,
218 std::string const &instFluxField) const {
219 ndarray::Array<double, 2, 2> result =
220 ndarray::allocate(ndarray::makeVector(int(sourceCatalog.size()), 2));
221 instFluxToMagnitudeArray(sourceCatalog, instFluxField, result);
222 return result;
223}
224
225void PhotoCalib::instFluxToMagnitude(afw::table::SourceCatalog &sourceCatalog,
226 std::string const &instFluxField, std::string const &outField) const {
227 auto instFluxKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFlux").key;
228 auto instFluxErrKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFluxErr").key;
229 auto magKey = sourceCatalog.getSchema().find<double>(outField + "_mag").key;
230 auto magErrKey = sourceCatalog.getSchema().find<double>(outField + "_magErr").key;
231 for (auto &record : sourceCatalog) {
232 auto result = instFluxToMagnitude(record.get(instFluxKey), record.get(instFluxErrKey),
233 record.getCentroid());
234 record.set(magKey, result.value);
235 record.set(magErrKey, result.error);
236 }
237}
238
239// ------------------- other utility methods -------------------
240
241double PhotoCalib::magnitudeToInstFlux(double magnitude) const {
242 return toInstFluxFromMagnitude(magnitude, _calibrationMean);
243}
244
245double PhotoCalib::magnitudeToInstFlux(double magnitude, lsst::geom::Point<double, 2> const &point) const {
246 return toInstFluxFromMagnitude(magnitude, evaluate(point));
247}
248
249std::shared_ptr<math::BoundedField> PhotoCalib::computeScaledCalibration() const {
250 return *(_calibration) / _calibrationMean;
251}
252
253std::shared_ptr<math::BoundedField> PhotoCalib::computeScalingTo(std::shared_ptr<PhotoCalib> other) const {
254 throw LSST_EXCEPT(pex::exceptions::LogicError, "Not Implemented: See DM-10154.");
255}
256
257bool PhotoCalib::operator==(PhotoCalib const &rhs) const {
258 return (_calibrationMean == rhs._calibrationMean && _calibrationErr == rhs._calibrationErr &&
259 (*_calibration) == *(rhs._calibration));
260}
261
262double PhotoCalib::computeCalibrationMean(std::shared_ptr<afw::math::BoundedField> calibration) const {
263 return calibration->mean();
264}
265
266std::shared_ptr<typehandling::Storable> PhotoCalib::cloneStorable() const {
267 return std::make_unique<PhotoCalib>(*this);
268}
269
270std::string PhotoCalib::toString() const {
271 std::stringstream buffer;
272 if (_isConstant)
273 buffer << "spatially constant with ";
274 else
275 buffer << *_calibration << " with ";
276 buffer << "mean: " << _calibrationMean << " error: " << _calibrationErr;
277 return buffer.str();
278}
279
280bool PhotoCalib::equals(typehandling::Storable const &other) const noexcept {
281 return singleClassEquals(*this, other);
282}
283
284std::ostream &operator<<(std::ostream &os, PhotoCalib const &photoCalib) {
285 return os << photoCalib.toString();
286}
287
288MaskedImage<float> PhotoCalib::calibrateImage(MaskedImage<float> const &maskedImage,
289 bool includeScaleUncertainty) const {
290 // Deep copy construct, as we're mutiplying in-place.
291 auto result = MaskedImage<float>(maskedImage, true);
292
293 if (_isConstant) {
294 *(result.getImage()) *= _calibrationMean;
295 } else {
296 _calibration->multiplyImage(*(result.getImage()), true); // only in the overlap region
297 }
298 if (includeScaleUncertainty) {
299 toNanojanskyVariance(maskedImage.getImage()->getArray(), maskedImage.getVariance()->getArray(),
300 _calibrationErr, result.getImage()->getArray(),
301 result.getVariance()->getArray());
302 } else {
303 toNanojanskyVariance(maskedImage.getImage()->getArray(), maskedImage.getVariance()->getArray(), 0,
304 result.getImage()->getArray(), result.getVariance()->getArray());
305 }
306
307 return result;
308}
309
310MaskedImage<float> PhotoCalib::uncalibrateImage(MaskedImage<float> const &maskedImage,
311 bool includeScaleUncertainty) const {
312 // Deep copy construct, as we're mutiplying in-place.
313 auto result = MaskedImage<float>(maskedImage, true);
314
315 if (_isConstant) {
316 *(result.getImage()) /= _calibrationMean;
317 } else {
318 _calibration->divideImage(*(result.getImage()), true); // only in the overlap region
319 }
320 if (includeScaleUncertainty) {
321 fromNanojanskyVariance(maskedImage.getImage()->getArray(), maskedImage.getVariance()->getArray(),
322 _calibrationErr, result.getImage()->getArray(),
323 result.getVariance()->getArray());
324 } else {
325 fromNanojanskyVariance(maskedImage.getImage()->getArray(), maskedImage.getVariance()->getArray(), 0,
326 result.getImage()->getArray(), result.getVariance()->getArray());
327 }
328
329 return result;
330}
331
332afw::table::SourceCatalog PhotoCalib::calibrateCatalog(afw::table::SourceCatalog const &catalog,
333 std::vector<std::string> const &instFluxFields) const {
334 auto const &inSchema = catalog.getSchema();
335 afw::table::SchemaMapper mapper(inSchema, true); // true: share the alias map
336 mapper.addMinimalSchema(inSchema);
337
338 using FieldD = afw::table::Field<double>;
339
340 struct Keys {
341 table::Key<double> instFlux;
342 table::Key<double> instFluxErr;
344 table::Key<double> fluxErr;
346 table::Key<double> magErr;
347 };
348
350 keys.reserve(instFluxFields.size());
351 for (auto const &field : instFluxFields) {
352 Keys newKey;
353 newKey.instFlux = inSchema[inSchema.join(field, "instFlux")];
354 newKey.flux =
355 mapper.addOutputField(FieldD(inSchema.join(field, "flux"), "calibrated flux", "nJy"), true);
356 newKey.mag = mapper.addOutputField(
357 FieldD(inSchema.join(field, "mag"), "calibrated magnitude", "mag(AB)"), true);
358 try {
359 newKey.instFluxErr = inSchema.find<double>(inSchema.join(field, "instFluxErr")).key;
360 newKey.fluxErr = mapper.addOutputField(
361 FieldD(inSchema.join(field, "fluxErr"), "calibrated flux uncertainty", "nJy"), true);
362 newKey.magErr = mapper.addOutputField(
363 FieldD(inSchema.join(field, "magErr"), "calibrated magnitude uncertainty", "mag(AB)"),
364 true);
366 ; // Keys struct defaults to invalid keys; that marks the error as missing.
367 }
368 keys.emplace_back(newKey);
369 }
370
371 // Create the new catalog
372 afw::table::SourceCatalog output(mapper.getOutputSchema());
373 output.insert(mapper, output.begin(), catalog.begin(), catalog.end());
374
375 auto calibration = evaluateCatalog(output);
376
377 // fill in the catalog values
378 int iRec = 0;
379 for (auto &rec : output) {
380 for (auto &key : keys) {
381 double instFlux = rec.get(key.instFlux);
382 double nanojansky = toNanojansky(instFlux, calibration[iRec]);
383 rec.set(key.flux, nanojansky);
384 rec.set(key.mag, toMagnitude(instFlux, calibration[iRec]));
385 if (key.instFluxErr.isValid()) {
386 double instFluxErr = rec.get(key.instFluxErr);
387 rec.set(key.fluxErr, toNanojanskyErr(instFlux, instFluxErr, calibration[iRec],
388 _calibrationErr, nanojansky));
389 rec.set(key.magErr,
390 toMagnitudeErr(instFlux, instFluxErr, calibration[iRec], _calibrationErr));
391 }
392 }
393 ++iRec;
394 }
395
396 return output;
397}
398
399afw::table::SourceCatalog PhotoCalib::calibrateCatalog(afw::table::SourceCatalog const &catalog) const {
400 std::vector<std::string> instFluxFields;
401 static std::string const SUFFIX = "_instFlux";
402 for (auto const &name : catalog.getSchema().getNames()) {
403 // Pick every field ending in "_instFlux", grabbing everything before that prefix.
404 if (name.size() > SUFFIX.size() + 1 &&
405 name.compare(name.size() - SUFFIX.size(), SUFFIX.size(), SUFFIX) == 0) {
406 instFluxFields.emplace_back(name.substr(0, name.size() - 9));
407 }
408 }
409 return calibrateCatalog(catalog, instFluxFields);
410}
411
412// ------------------- persistence -------------------
413
414namespace {
415
416class PhotoCalibSchema {
417public:
418 table::Schema schema;
419 table::Key<double> calibrationMean;
420 table::Key<double> calibrationErr;
421 table::Key<table::Flag> isConstant;
422 table::Key<int> field;
423 table::Key<int> version;
424
425 // No copying
426 PhotoCalibSchema(PhotoCalibSchema const &) = delete;
427 PhotoCalibSchema &operator=(PhotoCalibSchema const &) = delete;
428 // No moving
429 PhotoCalibSchema(PhotoCalibSchema &&) = delete;
430 PhotoCalibSchema &operator=(PhotoCalibSchema &&) = delete;
431
432 static PhotoCalibSchema const &get() {
433 static PhotoCalibSchema const instance;
434 return instance;
435 }
436
437private:
438 PhotoCalibSchema()
439 : schema(),
440 calibrationMean(schema.addField<double>(
441 "calibrationMean", "mean calibration on this PhotoCalib's domain", "count")),
443 schema.addField<double>("calibrationErr", "1-sigma error on calibrationMean", "count")),
444 isConstant(schema.addField<table::Flag>("isConstant", "Is this spatially-constant?")),
445 field(schema.addField<int>("field", "archive ID of the BoundedField object")),
446 version(schema.addField<int>("version", "version of this PhotoCalib")) {}
447};
448
449class PhotoCalibFactory : public table::io::PersistableFactory {
450public:
451 std::shared_ptr<table::io::Persistable> read(InputArchive const &archive,
452 CatalogVector const &catalogs) const override {
453 table::BaseRecord const &record = catalogs.front().front();
454 PhotoCalibSchema const &keys = PhotoCalibSchema::get();
455 int version = getVersion(record);
456 if (version < 1) {
457 throw(pex::exceptions::RuntimeError("Unsupported version (version 0 was defined in maggies): " +
459 }
460 return std::make_shared<PhotoCalib>(record.get(keys.calibrationMean), record.get(keys.calibrationErr),
461 archive.get<afw::math::BoundedField>(record.get(keys.field)),
462 record.get(keys.isConstant));
463 }
464
465 PhotoCalibFactory(std::string const &name) : afw::table::io::PersistableFactory(name) {}
466
467protected:
468 int getVersion(table::BaseRecord const &record) const {
469 int version = -1;
470 try {
471 std::string versionName = "version";
472 auto versionKey = record.getSchema().find<int>(versionName);
473 version = record.get(versionKey.key);
474 } catch (const pex::exceptions::NotFoundError &) {
475 // un-versioned files are version 0
476 version = 0;
477 }
478 return version;
479 }
480};
481
482std::string getPhotoCalibPersistenceName() { return "PhotoCalib"; }
483
484PhotoCalibFactory registration(getPhotoCalibPersistenceName());
485
486} // namespace
487
488/*
489 * Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
490 */
491
492namespace {
493int const CALIB_TABLE_CURRENT_VERSION = 2; // final version of Calib in ExposureTable
494std::string const EXPTIME_FIELD_NAME = "exptime"; // name of exposure time field (no longer used)
495
496class CalibKeys {
497public:
498 table::Schema schema;
499 table::Key<std::int64_t> midTime;
500 table::Key<double> expTime;
501 table::Key<double> fluxMag0;
502 table::Key<double> fluxMag0Err;
503
504 // No copying
505 CalibKeys(const CalibKeys &) = delete;
506 CalibKeys &operator=(const CalibKeys &) = delete;
507
508 // No moving
509 CalibKeys(CalibKeys &&) = delete;
510 CalibKeys &operator=(CalibKeys &&) = delete;
511
512 CalibKeys(int tableVersion = CALIB_TABLE_CURRENT_VERSION)
513 : schema(), midTime(), expTime(), fluxMag0(), fluxMag0Err() {
514 if (tableVersion == 1) {
515 // obsolete fields
517 "midtime", "middle of the time of the exposure relative to Unix epoch", "ns");
518 expTime = schema.addField<double>(EXPTIME_FIELD_NAME, "exposure time", "s");
519 }
520 fluxMag0 = schema.addField<double>("fluxmag0", "flux of a zero-magnitude object", "count");
521 fluxMag0Err = schema.addField<double>("fluxmag0.err", "1-sigma error on fluxmag0", "count");
522 }
523};
524
525class CalibFactory : public table::io::PersistableFactory {
526public:
527 std::shared_ptr<table::io::Persistable> read(InputArchive const &archive,
528 CatalogVector const &catalogs) const override {
529 // table version is not persisted, so we don't have a clean way to determine the version;
530 // the hack is version = 1 if exptime found, else current
531 int tableVersion = 1;
532 try {
533 catalogs.front().getSchema().find<double>(EXPTIME_FIELD_NAME);
534 } catch (pex::exceptions::NotFoundError const &) {
535 tableVersion = CALIB_TABLE_CURRENT_VERSION;
536 }
537
538 CalibKeys const keys{tableVersion};
539 LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
540 LSST_ARCHIVE_ASSERT(catalogs.front().size() == 1u);
541 LSST_ARCHIVE_ASSERT(catalogs.front().getSchema() == keys.schema);
542 table::BaseRecord const &record = catalogs.front().front();
543
544 double calibration = cpputils::referenceFlux / record.get(keys.fluxMag0);
545 double calibrationErr = cpputils::referenceFlux * record.get(keys.fluxMag0Err) /
546 std::pow(record.get(keys.fluxMag0), 2);
547 return std::make_shared<PhotoCalib>(calibration, calibrationErr);
548 }
549
550 explicit CalibFactory(std::string const &name) : table::io::PersistableFactory(name) {}
551};
552
553std::string getCalibPersistenceName() { return "Calib"; }
554
555CalibFactory calibRegistration(getCalibPersistenceName());
556
557} // namespace
558
559std::string PhotoCalib::getPersistenceName() const { return getPhotoCalibPersistenceName(); }
560
561void PhotoCalib::write(OutputArchiveHandle &handle) const {
562 PhotoCalibSchema const &keys = PhotoCalibSchema::get();
563 table::BaseCatalog catalog = handle.makeCatalog(keys.schema);
564 auto record = catalog.addNew();
565 record->set(keys.calibrationMean, _calibrationMean);
566 record->set(keys.calibrationErr, _calibrationErr);
567 record->set(keys.isConstant, _isConstant);
568 record->set(keys.field, handle.put(_calibration));
569 record->set(keys.version, SERIALIZATION_VERSION);
570 handle.saveCatalog(catalog);
571}
572
573// ------------------- private/protected helpers -------------------
574
575double PhotoCalib::evaluate(lsst::geom::Point<double, 2> const &point) const {
576 if (_isConstant)
577 return _calibrationMean;
578 else
579 return _calibration->evaluate(point);
580}
581
582ndarray::Array<double, 1> PhotoCalib::evaluateArray(ndarray::Array<double, 1> const &xx,
583 ndarray::Array<double, 1> const &yy) const {
584 if (_isConstant) {
585 ndarray::Array<double, 1> result = ndarray::allocate(ndarray::makeVector(xx.size()));
586 result.deep() = _calibrationMean;
587 return result;
588 } else {
589 return _calibration->evaluate(xx, yy);
590 }
591}
592
593ndarray::Array<double, 1> PhotoCalib::evaluateCatalog(afw::table::SourceCatalog const &sourceCatalog) const {
594 ndarray::Array<double, 1> xx = ndarray::allocate(ndarray::makeVector(sourceCatalog.size()));
595 ndarray::Array<double, 1> yy = ndarray::allocate(ndarray::makeVector(sourceCatalog.size()));
596 size_t i = 0;
597 for (auto const &rec : sourceCatalog) {
598 auto point = rec.getCentroid();
599 xx[i] = point.getX();
600 yy[i] = point.getY();
601 ++i;
602 }
603 return evaluateArray(xx, yy);
604}
605
606void PhotoCalib::instFluxToNanojanskyArray(afw::table::SourceCatalog const &sourceCatalog,
607 std::string const &instFluxField,
608 ndarray::Array<double, 2, 2> result) const {
609 auto instFluxKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFlux").key;
610 auto instFluxErrKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFluxErr").key;
611
612 auto calibration = evaluateCatalog(sourceCatalog);
613 int i = 0;
614 auto iter = result.begin();
615 for (auto const &rec : sourceCatalog) {
616 double instFlux = rec.get(instFluxKey);
617 double instFluxErr = rec.get(instFluxErrKey);
618 double nanojansky = toNanojansky(instFlux, calibration[i]);
619 (*iter)[0] = nanojansky;
620 (*iter)[1] = toNanojanskyErr(instFlux, instFluxErr, calibration[i], _calibrationErr, nanojansky);
621 ++iter;
622 ++i;
623 }
624}
625
626void PhotoCalib::instFluxToMagnitudeArray(afw::table::SourceCatalog const &sourceCatalog,
627 std::string const &instFluxField,
628 ndarray::Array<double, 2, 2> result) const {
629 auto instFluxKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFlux").key;
630 auto instFluxErrKey = sourceCatalog.getSchema().find<double>(instFluxField + "_instFluxErr").key;
631
632 auto calibration = evaluateCatalog(sourceCatalog);
633 auto iter = result.begin();
634 int i = 0;
635 for (auto const &rec : sourceCatalog) {
636 double instFlux = rec.get(instFluxKey);
637 double instFluxErr = rec.get(instFluxErrKey);
638 (*iter)[0] = toMagnitude(instFlux, calibration[i]);
639 (*iter)[1] = toMagnitudeErr(instFlux, instFluxErr, calibration[i], _calibrationErr);
640 ++iter;
641 ++i;
642 }
643}
644
646 auto key = "FLUXMAG0";
647 if (metadata.exists(key)) {
648 double instFluxMag0 = metadata.getAsDouble(key);
649 if (strip) metadata.remove(key);
650
651 double instFluxMag0Err = 0.0;
652 key = "FLUXMAG0ERR";
653 if (metadata.exists(key)) {
654 instFluxMag0Err = metadata.getAsDouble(key);
655 if (strip) metadata.remove(key);
656 }
657 return makePhotoCalibFromCalibZeroPoint(instFluxMag0, instFluxMag0Err);
658 } else {
659 return nullptr;
660 }
661}
662
663std::shared_ptr<PhotoCalib> makePhotoCalibFromCalibZeroPoint(double instFluxMag0, double instFluxMag0Err) {
664 double calibration = cpputils::referenceFlux / instFluxMag0;
665 double calibrationErr = cpputils::referenceFlux * instFluxMag0Err / std::pow(instFluxMag0, 2);
666 return std::make_shared<PhotoCalib>(calibration, calibrationErr);
667}
668
669} // namespace image
670} // namespace afw
671} // namespace lsst
py::object result
Definition _schema.cc:429
table::Key< int > field
Definition ApCorrMap.cc:77
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
Utilities for converting between flux and magnitude in C++.
table::Key< double > calibrationMean
table::Key< std::int64_t > midTime
table::Key< double > calibrationErr
table::Key< double > fluxMag0
table::Key< double > expTime
table::Key< table::Flag > isConstant
table::Key< double > fluxMag0Err
Implementation of the Photometric Calibration class.
SchemaMapper * mapper
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
Definition Persistable.h:48
table::Schema schema
Definition python.h:134
A class to manipulate images, masks, and variance as a single object.
Definition MaskedImage.h:74
VariancePtr getVariance() const
Return a (shared_ptr to) the MaskedImage's variance.
ImagePtr getImage() const
Return a (shared_ptr to) the MaskedImage's image.
The photometric calibration of an exposure.
Definition PhotoCalib.h:114
Tag types used to declare specialized field types.
Definition misc.h:31
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Definition BaseRecord.h:151
Schema getSchema() const
Return the Schema that holds this record's fields and keys.
Definition BaseRecord.h:80
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:489
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
Definition Schema.cc:479
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
Definition Schema.cc:467
A mapping between the keys of two Schemas, used to copy data between them.
Record class that contains measurements made on a single exposure.
Definition Source.h:78
CentroidSlotDefinition::MeasValue getCentroid() const
Get the value of the Centroid slot measurement.
Definition Source.h:569
An object passed to Persistable::write to allow it to persist itself.
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
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...
static std::shared_ptr< T > dynamicCast(std::shared_ptr< Persistable > const &ptr)
Dynamically cast a shared_ptr.
Interface supporting iteration over heterogenous containers.
Definition Storable.h:58
Class for storing generic metadata.
Definition PropertySet.h:66
virtual void remove(std::string const &name)
Remove all values for a property name (possibly hierarchical).
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
double getAsDouble(std::string const &name) const
Get the last value for any arithmetic property name (possibly hierarchical).
A coordinate class intended to represent absolute positions.
Definition Point.h:169
Reports errors in the logical structure of the program.
Definition Runtime.h:46
Reports attempts to access elements using an invalid key.
Definition Runtime.h:151
T emplace_back(T... args)
T find(T... args)
bool strip
Definition fits.cc:930
T get(T... args)
T hypot(T... args)
T log(T... args)
std::shared_ptr< PhotoCalib > makePhotoCalibFromMetadata(daf::base::PropertySet &metadata, bool strip=false)
Construct a PhotoCalib from FITS FLUXMAG0/FLUXMAG0ERR keywords.
SortedCatalogT< SourceRecord > SourceCatalog
Definition fwd.h:85
const double referenceFlux
The Oke & Gunn (1983) AB magnitude reference flux, in nJy (often approximated as 3631....
Definition Magnitude.h:46
double ABMagnitudeToNanojansky(double magnitude)
Convert an AB magnitude to a flux in nanojansky.
Definition Magnitude.cc:32
double nanojanskyToABMagnitude(double flux)
Convert a flux in nanojansky to AB magnitude.
Definition Magnitude.cc:30
T pow(T... args)
T setprecision(T... args)
T size(T... args)
T str(T... args)
A value and its error.
Definition PhotoCalib.h:51
T to_string(T... args)
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override