35 #include "lsst/afw/table/io/Persistable.cc" 50 s << std::setprecision(std::numeric_limits<long double>::digits10 + 1);
51 s <<
"value=" << measurement.
value <<
", error=" << measurement.
error;
57 int const SERIALIZATION_VERSION = 1;
59 double toNanojansky(
double instFlux,
double scale) {
return instFlux *
scale; }
63 double toInstFluxFromMagnitude(
double magnitude,
double scale) {
68 double toNanojanskyErr(
double instFlux,
double instFluxErr,
double scale,
double scaleErr,
70 return std::abs(nanojansky) * hypot(instFluxErr / instFlux, scaleErr / scale);
86 void toNanojanskyVariance(ndarray::Array<float const, 2, 1>
const &instFlux,
87 ndarray::Array<float const, 2, 1>
const &instFluxVar,
float scaleErr,
88 ndarray::Array<float const, 2, 1>
const &flux, ndarray::Array<float, 2, 1> out) {
89 auto eigenFlux = ndarray::asEigen<Eigen::ArrayXpr>(flux);
90 auto eigenInstFluxVar = ndarray::asEigen<Eigen::ArrayXpr>(instFluxVar);
91 auto eigenInstFlux = ndarray::asEigen<Eigen::ArrayXpr>(instFlux);
92 auto eigenOut = ndarray::asEigen<Eigen::ArrayXpr>(out);
93 eigenOut = eigenFlux.square() *
94 (eigenInstFluxVar / eigenInstFlux.square() + (scaleErr / eigenFlux * eigenInstFlux).square());
97 double toMagnitudeErr(
double instFlux,
double instFluxErr,
double scale,
double scaleErr) {
98 return 2.5 /
log(10.0) * hypot(instFluxErr / instFlux, scaleErr / scale);
105 ndarray::Array<double, 1> PhotoCalib::getMagnitude(ndarray::Array<double const, 1>
const &instFlux)
const {
106 ndarray::Array<double, 1>
result = ndarray::allocate(ndarray::makeVector(
int(instFlux.size())));
107 auto iter = result.begin();
108 for (
auto const &i : instFlux) {
109 *iter = toMagnitude(i, _calibrationMean);
116 ndarray::Array<double const, 1>
const &instFlux,
117 ndarray::Array<double const, 1>
const &instFluxErr)
const {
118 ndarray::Array<double, 1> mag = ndarray::allocate(ndarray::makeVector(
int(instFlux.size())));
119 ndarray::Array<double, 1> magErr = ndarray::allocate(ndarray::makeVector(
int(instFlux.size())));
120 auto iMag = mag.begin();
121 auto iMagErr = magErr.begin();
122 for (
auto i = instFlux.begin(), iErr = instFluxErr.begin(); i != instFlux.end(); ++i, ++iErr) {
123 *iMag = toMagnitude(*i, _calibrationMean);
124 *iMagErr = toMagnitudeErr(*i, *iErr, _calibrationMean, _calibrationErr);
134 return toNanojansky(instFlux, evaluate(point));
137 double PhotoCalib::instFluxToNanojansky(
double instFlux)
const {
138 return toNanojansky(instFlux, _calibrationMean);
141 Measurement PhotoCalib::instFluxToNanojansky(
double instFlux,
double instFluxErr,
143 double calibration,
error, nanojansky;
144 calibration = evaluate(point);
145 nanojansky = toNanojansky(instFlux, calibration);
146 error = toNanojanskyErr(instFlux, instFluxErr, calibration, _calibrationErr, nanojansky);
150 Measurement PhotoCalib::instFluxToNanojansky(
double instFlux,
double instFluxErr)
const {
151 double nanojansky = toNanojansky(instFlux, _calibrationMean);
152 double error = toNanojanskyErr(instFlux, instFluxErr, _calibrationMean, _calibrationErr, nanojansky);
159 auto instFluxKey = sourceRecord.
getSchema().
find<
double>(instFluxField +
"_instFlux").
key;
160 auto instFluxErrKey = sourceRecord.
getSchema().
find<
double>(instFluxField +
"_instFluxErr").
key;
161 return instFluxToNanojansky(sourceRecord.
get(instFluxKey), sourceRecord.
get(instFluxErrKey), position);
165 ndarray::Array<double, 2, 2>
result =
166 ndarray::allocate(ndarray::makeVector(
int(sourceCatalog.size()), 2));
167 instFluxToNanojanskyArray(sourceCatalog, instFluxField, result);
173 auto instFluxKey = sourceCatalog.getSchema().find<
double>(instFluxField +
"_instFlux").
key;
174 auto instFluxErrKey = sourceCatalog.getSchema().find<
double>(instFluxField +
"_instFluxErr").
key;
175 auto nanojanskyKey = sourceCatalog.getSchema().find<
double>(outField +
"_flux").
key;
176 auto nanojanskyErrKey = sourceCatalog.getSchema().find<
double>(outField +
"_fluxErr").
key;
177 for (
auto &record : sourceCatalog) {
178 auto result = instFluxToNanojansky(record.get(instFluxKey), record.get(instFluxErrKey),
179 record.getCentroid());
180 record.set(nanojanskyKey,
result.value);
181 record.set(nanojanskyErrKey,
result.error);
188 return toMagnitude(instFlux, evaluate(point));
191 double PhotoCalib::instFluxToMagnitude(
double instFlux)
const {
192 return toMagnitude(instFlux, _calibrationMean);
195 Measurement PhotoCalib::instFluxToMagnitude(
double instFlux,
double instFluxErr,
197 double calibration,
error, magnitude;
198 calibration = evaluate(point);
199 magnitude = toMagnitude(instFlux, calibration);
200 error = toMagnitudeErr(instFlux, instFluxErr, calibration, _calibrationErr);
204 Measurement PhotoCalib::instFluxToMagnitude(
double instFlux,
double instFluxErr)
const {
205 double magnitude = toMagnitude(instFlux, _calibrationMean);
206 double error = toMagnitudeErr(instFlux, instFluxErr, _calibrationMean, _calibrationErr);
213 auto instFluxKey = sourceRecord.
getSchema().
find<
double>(instFluxField +
"_instFlux").
key;
214 auto instFluxErrKey = sourceRecord.
getSchema().
find<
double>(instFluxField +
"_instFluxErr").
key;
215 return instFluxToMagnitude(sourceRecord.
get(instFluxKey), sourceRecord.
get(instFluxErrKey), position);
220 ndarray::Array<double, 2, 2>
result =
221 ndarray::allocate(ndarray::makeVector(
int(sourceCatalog.size()), 2));
222 instFluxToMagnitudeArray(sourceCatalog, instFluxField, result);
228 auto instFluxKey = sourceCatalog.getSchema().find<
double>(instFluxField +
"_instFlux").
key;
229 auto instFluxErrKey = sourceCatalog.getSchema().find<
double>(instFluxField +
"_instFluxErr").
key;
230 auto magKey = sourceCatalog.getSchema().find<
double>(outField +
"_mag").
key;
231 auto magErrKey = sourceCatalog.getSchema().find<
double>(outField +
"_magErr").
key;
232 for (
auto &record : sourceCatalog) {
233 auto result = instFluxToMagnitude(record.get(instFluxKey), record.get(instFluxErrKey),
234 record.getCentroid());
235 record.set(magKey,
result.value);
236 record.set(magErrKey,
result.error);
242 double PhotoCalib::magnitudeToInstFlux(
double magnitude)
const {
243 return toInstFluxFromMagnitude(magnitude, _calibrationMean);
247 return toInstFluxFromMagnitude(magnitude, evaluate(point));
251 return *(_calibration) / _calibrationMean;
259 return (_calibrationMean == rhs._calibrationMean && _calibrationErr == rhs._calibrationErr &&
260 (*_calibration) == *(rhs._calibration));
264 return calibration->mean();
268 return std::make_unique<PhotoCalib>(*this);
274 buffer <<
"spatially constant with ";
276 buffer << *_calibration <<
" with ";
277 buffer <<
"mean: " << _calibrationMean <<
" error: " << _calibrationErr;
282 return singleClassEquals(*
this,
other);
290 bool includeScaleUncertainty)
const {
295 *(
result.getImage()) *= _calibrationMean;
297 _calibration->multiplyImage(*(
result.getImage()),
true);
299 if (includeScaleUncertainty) {
300 toNanojanskyVariance(maskedImage.
getImage()->getArray(), maskedImage.
getVariance()->getArray(),
301 _calibrationErr,
result.getImage()->getArray(),
302 result.getVariance()->getArray());
304 toNanojanskyVariance(maskedImage.
getImage()->getArray(), maskedImage.
getVariance()->getArray(), 0,
305 result.getImage()->getArray(),
result.getVariance()->getArray());
313 auto const &inSchema = catalog.getSchema();
330 for (
auto const &
field : instFluxFields) {
332 newKey.instFlux = inSchema[inSchema.join(
field,
"instFlux")];
334 mapper.
addOutputField(FieldD(inSchema.join(
field,
"flux"),
"calibrated flux",
"nJy"),
true);
336 FieldD(inSchema.join(
field,
"mag"),
"calibrated magnitude",
"mag(AB)"),
true);
338 newKey.instFluxErr = inSchema.find<
double>(inSchema.join(
field,
"instFluxErr")).
key;
340 FieldD(inSchema.join(
field,
"fluxErr"),
"calibrated flux uncertainty",
"nJy"),
true);
342 FieldD(inSchema.join(
field,
"magErr"),
"calibrated magnitude uncertainty",
"mag(AB)"),
352 output.insert(mapper,
output.begin(), catalog.begin(), catalog.end());
354 auto calibration = evaluateCatalog(
output);
358 for (
auto &rec :
output) {
359 for (
auto &
key : keys) {
360 double instFlux = rec.get(
key.instFlux);
361 double nanojansky = toNanojansky(instFlux, calibration[iRec]);
362 rec.set(
key.flux, nanojansky);
363 rec.set(
key.mag, toMagnitude(instFlux, calibration[iRec]));
364 if (
key.instFluxErr.isValid()) {
365 double instFluxErr = rec.get(
key.instFluxErr);
366 rec.set(
key.fluxErr, toNanojanskyErr(instFlux, instFluxErr, calibration[iRec],
367 _calibrationErr, nanojansky));
369 toMagnitudeErr(instFlux, instFluxErr, calibration[iRec], _calibrationErr));
381 for (
auto const &
name : catalog.getSchema().getNames()) {
383 if (
name.size() > SUFFIX.
size() + 1 &&
388 return calibrateCatalog(catalog, instFluxFields);
395 class PhotoCalibSchema {
405 PhotoCalibSchema(PhotoCalibSchema
const &) =
delete;
406 PhotoCalibSchema &operator=(PhotoCalibSchema
const &) =
delete;
408 PhotoCalibSchema(PhotoCalibSchema &&) =
delete;
409 PhotoCalibSchema &operator=(PhotoCalibSchema &&) =
delete;
411 static PhotoCalibSchema
const &
get() {
412 static PhotoCalibSchema
const instance;
420 "calibrationMean",
"mean calibration on this PhotoCalib's domain",
"count")),
422 schema.addField<
double>(
"calibrationErr",
"1-sigma error on calibrationMean",
"count")),
423 isConstant(schema.addField<table::Flag>(
"isConstant",
"Is this spatially-constant?")),
424 field(schema.addField<
int>(
"field",
"archive ID of the BoundedField object")),
425 version(schema.addField<
int>(
"version",
"version of this PhotoCalib")) {
426 schema.getCitizen().markPersistent();
430 class PhotoCalibFactory :
public table::io::PersistableFactory {
432 PTR(table::io::Persistable)
433 read(InputArchive
const &archive, CatalogVector
const &catalogs)
const override {
434 table::BaseRecord
const &record = catalogs.front().front();
435 PhotoCalibSchema
const &
keys = PhotoCalibSchema::get();
436 int version = getVersion(record);
438 throw(pex::exceptions::RuntimeError(
"Unsupported version (version 0 was defined in maggies): " +
441 return std::make_shared<PhotoCalib>(record.get(keys.calibrationMean), record.get(keys.calibrationErr),
442 archive.get<afw::math::BoundedField>(record.get(keys.field)),
443 record.get(keys.isConstant));
446 PhotoCalibFactory(
std::string const &
name) : afw::table::io::PersistableFactory(name) {}
449 int getVersion(table::BaseRecord
const &record)
const {
453 auto versionKey = record.getSchema().
find<
int>(versionName);
454 version = record.get(versionKey.key);
455 }
catch (
const pex::exceptions::NotFoundError &) {
463 std::string getPhotoCalibPersistenceName() {
return "PhotoCalib"; }
465 PhotoCalibFactory registration(getPhotoCalibPersistenceName());
474 int const CALIB_TABLE_CURRENT_VERSION = 2;
486 CalibKeys(
const CalibKeys &) =
delete;
487 CalibKeys &operator=(
const CalibKeys &) =
delete;
490 CalibKeys(CalibKeys &&) =
delete;
491 CalibKeys &operator=(CalibKeys &&) =
delete;
493 CalibKeys(
int tableVersion = CALIB_TABLE_CURRENT_VERSION)
494 : schema(), midTime(), expTime(), fluxMag0(), fluxMag0Err() {
495 if (tableVersion == 1) {
498 "midtime",
"middle of the time of the exposure relative to Unix epoch",
"ns");
499 expTime = schema.addField<
double>(EXPTIME_FIELD_NAME,
"exposure time",
"s");
501 fluxMag0 = schema.addField<
double>(
"fluxmag0",
"flux of a zero-magnitude object",
"count");
502 fluxMag0Err = schema.addField<
double>(
"fluxmag0.err",
"1-sigma error on fluxmag0",
"count");
506 class CalibFactory :
public table::io::PersistableFactory {
509 CatalogVector
const &catalogs)
const override {
512 int tableVersion = 1;
514 catalogs.front().getSchema().find<
double>(EXPTIME_FIELD_NAME);
515 }
catch (pex::exceptions::NotFoundError) {
516 tableVersion = CALIB_TABLE_CURRENT_VERSION;
519 CalibKeys
const keys{tableVersion};
523 table::BaseRecord
const &record = catalogs.front().front();
531 explicit CalibFactory(
std::string const &
name) : table::io::PersistableFactory(name) {}
534 std::string getCalibPersistenceName() {
return "Calib"; }
536 CalibFactory calibRegistration(getCalibPersistenceName());
540 std::string PhotoCalib::getPersistenceName()
const {
return getPhotoCalibPersistenceName(); }
543 PhotoCalibSchema
const &
keys = PhotoCalibSchema::get();
545 auto record = catalog.
addNew();
546 record->set(keys.calibrationMean, _calibrationMean);
547 record->set(keys.calibrationErr, _calibrationErr);
548 record->set(keys.isConstant, _isConstant);
549 record->set(keys.field, handle.
put(_calibration));
550 record->set(keys.version, SERIALIZATION_VERSION);
558 return _calibrationMean;
560 return _calibration->evaluate(point);
563 ndarray::Array<double, 1> PhotoCalib::evaluateArray(ndarray::Array<double, 1>
const &xx,
564 ndarray::Array<double, 1>
const &yy)
const {
566 ndarray::Array<double, 1>
result = ndarray::allocate(ndarray::makeVector(xx.size()));
567 result.deep() = _calibrationMean;
570 return _calibration->evaluate(xx, yy);
575 ndarray::Array<double, 1> xx = ndarray::allocate(ndarray::makeVector(sourceCatalog.size()));
576 ndarray::Array<double, 1> yy = ndarray::allocate(ndarray::makeVector(sourceCatalog.size()));
578 for (
auto const &rec : sourceCatalog) {
579 auto point = rec.getCentroid();
580 xx[i] = point.getX();
581 yy[i] = point.getY();
584 return evaluateArray(xx, yy);
589 ndarray::Array<double, 2, 2>
result)
const {
590 auto instFluxKey = sourceCatalog.getSchema().find<
double>(instFluxField +
"_instFlux").
key;
591 auto instFluxErrKey = sourceCatalog.getSchema().find<
double>(instFluxField +
"_instFluxErr").
key;
593 auto calibration = evaluateCatalog(sourceCatalog);
595 auto iter = result.begin();
596 for (
auto const &rec : sourceCatalog) {
597 double instFlux = rec.get(instFluxKey);
598 double instFluxErr = rec.get(instFluxErrKey);
599 double nanojansky = toNanojansky(instFlux, calibration[i]);
600 (*iter)[0] = nanojansky;
601 (*iter)[1] = toNanojanskyErr(instFlux, instFluxErr, calibration[i], _calibrationErr, nanojansky);
609 ndarray::Array<double, 2, 2> result)
const {
610 auto instFluxKey = sourceCatalog.getSchema().find<
double>(instFluxField +
"_instFlux").
key;
611 auto instFluxErrKey = sourceCatalog.getSchema().find<
double>(instFluxField +
"_instFluxErr").
key;
613 auto calibration = evaluateCatalog(sourceCatalog);
614 auto iter = result.begin();
616 for (
auto const &rec : sourceCatalog) {
617 double instFlux = rec.get(instFluxKey);
618 double instFluxErr = rec.get(instFluxErrKey);
619 (*iter)[0] = toMagnitude(instFlux, calibration[i]);
620 (*iter)[1] = toMagnitudeErr(instFlux, instFluxErr, calibration[i], _calibrationErr);
627 auto key =
"FLUXMAG0";
632 double instFluxMag0Err = 0.0;
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's variance.
std::shared_ptr< PhotoCalib > makePhotoCalibFromMetadata(daf::base::PropertySet &metadata, bool strip=false)
Construct a PhotoCalib from FITS FLUXMAG0/FLUXMAG0ERR keywords.
std::string toString() const override
Create a string representation of this object.
Angle abs(Angle const &a)
static std::shared_ptr< T > dynamicCast(std::shared_ptr< Persistable > const &ptr)
Dynamically cast a shared_ptr.
std::shared_ptr< PhotoCalib > makePhotoCalibFromCalibZeroPoint(double instFluxMag0, double instFluxMag0Err)
Construct a PhotoCalib from the deprecated Calib-style instFluxMag0/instFluxMag0Err values...
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...
table::Key< double > fluxMag0
An object passed to Persistable::write to allow it to persist itself.
The photometric calibration of an exposure.
A mapping between the keys of two Schemas, used to copy data between them.
Interface supporting iteration over heterogenous containers.
Key< T > addOutputField(Field< T > const &newField, bool doReplace=false)
Add a new field to the output Schema that is not connected to the input Schema.
def scale(algorithm, min, max=None, frame=None)
Schema const getOutputSchema() const
Return the output schema (copy-on-write).
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.
ImagePtr getImage() const
Return a (shared_ptr to) the MaskedImage's image.
Reports attempts to access elements using an invalid key.
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
A base class for image defects.
A class to manipulate images, masks, and variance as a single object.
Utilities for converting between flux and magnitude in C++.
A description of a field in a table.
Schema getSchema() const
Return the Schema that holds this record's fields and keys.
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
table::Key< double > calibrationMean
table::Key< double > expTime
Reports errors in the logical structure of the program.
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
std::ostream & operator<<(std::ostream &os, PhotoCalib const &photoCalib)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
double nanojanskyToABMagnitude(double flux)
Convert a flux in nanojansky to AB magnitude.
void addMinimalSchema(Schema const &minimal, bool doMap=true)
Add the given minimal schema to the output schema.
table::Key< std::int64_t > midTime
Class for storing generic metadata.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
table::Key< double > fluxMag0Err
ItemVariant const * other
Record class that contains measurements made on a single exposure.
table::Key< table::Flag > isConstant
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
table::Key< int > version
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
const double referenceFlux
The Oke & Gunn (1983) AB magnitude reference flux, in nJy (often approximated as 3631.0).
double ABMagnitudeToNanojansky(double magnitude)
Convert an AB magnitude to a flux in nanojansky.
Implementation of the Photometric Calibration class.
table::Key< double > calibrationErr
CentroidSlotDefinition::MeasValue getCentroid() const
Get the value of the Centroid slot measurement.
std::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
T emplace_back(T... args)