51 s <<
"value=" << measurement.value <<
", error=" << measurement.error;
57int const SERIALIZATION_VERSION = 1;
59double toNanojansky(
double instFlux,
double scale) {
return instFlux * scale; }
61double toMagnitude(
double instFlux,
double scale) {
65double toInstFluxFromMagnitude(
double magnitude,
double scale) {
70double toNanojanskyErr(
double instFlux,
double instFluxErr,
double scale,
double scaleErr,
72 return std::abs(nanojansky) *
hypot(instFluxErr / instFlux, scaleErr / scale);
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());
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());
124double toMagnitudeErr(
double instFlux,
double instFluxErr,
double scale,
double scaleErr) {
125 return 2.5 /
std::log(10.0) *
hypot(instFluxErr / instFlux, scaleErr / scale);
133 return toNanojansky(instFlux, evaluate(point));
136double PhotoCalib::instFluxToNanojansky(
double instFlux)
const {
137 return toNanojansky(instFlux, _calibrationMean);
140Measurement PhotoCalib::instFluxToNanojansky(
double instFlux,
double instFluxErr,
142 double calibration, error, nanojansky;
143 calibration = evaluate(point);
144 nanojansky = toNanojansky(instFlux, calibration);
145 error = toNanojanskyErr(instFlux, instFluxErr, calibration, _calibrationErr, nanojansky);
149Measurement PhotoCalib::instFluxToNanojansky(
double instFlux,
double instFluxErr)
const {
150 double nanojansky = toNanojansky(instFlux, _calibrationMean);
151 double error = toNanojanskyErr(instFlux, instFluxErr, _calibrationMean, _calibrationErr, nanojansky);
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);
164 ndarray::Array<double, 2, 2>
result =
165 ndarray::allocate(ndarray::makeVector(
int(sourceCatalog.size()), 2));
166 instFluxToNanojanskyArray(sourceCatalog, instFluxField,
result);
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);
187 return toMagnitude(instFlux, evaluate(point));
190double PhotoCalib::instFluxToMagnitude(
double instFlux)
const {
191 return toMagnitude(instFlux, _calibrationMean);
194Measurement PhotoCalib::instFluxToMagnitude(
double instFlux,
double instFluxErr,
196 double calibration, error, magnitude;
197 calibration = evaluate(point);
198 magnitude = toMagnitude(instFlux, calibration);
199 error = toMagnitudeErr(instFlux, instFluxErr, calibration, _calibrationErr);
203Measurement PhotoCalib::instFluxToMagnitude(
double instFlux,
double instFluxErr)
const {
204 double magnitude = toMagnitude(instFlux, _calibrationMean);
205 double error = toMagnitudeErr(instFlux, instFluxErr, _calibrationMean, _calibrationErr);
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);
219 ndarray::Array<double, 2, 2>
result =
220 ndarray::allocate(ndarray::makeVector(
int(sourceCatalog.size()), 2));
221 instFluxToMagnitudeArray(sourceCatalog, instFluxField,
result);
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);
241double PhotoCalib::magnitudeToInstFlux(
double magnitude)
const {
242 return toInstFluxFromMagnitude(magnitude, _calibrationMean);
246 return toInstFluxFromMagnitude(magnitude, evaluate(point));
250 return *(_calibration) / _calibrationMean;
258 return (_calibrationMean == rhs._calibrationMean && _calibrationErr == rhs._calibrationErr &&
259 (*_calibration) == *(rhs._calibration));
263 return calibration->mean();
267 return std::make_unique<PhotoCalib>(*
this);
273 buffer <<
"spatially constant with ";
275 buffer << *_calibration <<
" with ";
276 buffer <<
"mean: " << _calibrationMean <<
" error: " << _calibrationErr;
281 return singleClassEquals(*
this, other);
285 return os << photoCalib.toString();
289 bool includeScaleUncertainty)
const {
294 *(
result.getImage()) *= _calibrationMean;
296 _calibration->multiplyImage(*(
result.getImage()),
true);
298 if (includeScaleUncertainty) {
300 _calibrationErr,
result.getImage()->getArray(),
301 result.getVariance()->getArray());
304 result.getImage()->getArray(),
result.getVariance()->getArray());
311 bool includeScaleUncertainty)
const {
316 *(
result.getImage()) /= _calibrationMean;
318 _calibration->divideImage(*(
result.getImage()),
true);
320 if (includeScaleUncertainty) {
322 _calibrationErr,
result.getImage()->getArray(),
323 result.getVariance()->getArray());
326 result.getImage()->getArray(),
result.getVariance()->getArray());
334 auto const &inSchema = catalog.getSchema();
336 mapper.addMinimalSchema(inSchema);
350 keys.reserve(instFluxFields.
size());
351 for (
auto const &field : instFluxFields) {
353 newKey.instFlux = inSchema[inSchema.join(field,
"instFlux")];
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);
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)"),
368 keys.emplace_back(newKey);
373 output.insert(
mapper, output.begin(), catalog.begin(), catalog.end());
375 auto calibration = evaluateCatalog(output);
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));
390 toMagnitudeErr(instFlux, instFluxErr, calibration[iRec], _calibrationErr));
402 for (
auto const &name : catalog.getSchema().getNames()) {
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));
409 return calibrateCatalog(catalog, instFluxFields);
416class PhotoCalibSchema {
426 PhotoCalibSchema(PhotoCalibSchema
const &) =
delete;
427 PhotoCalibSchema &operator=(PhotoCalibSchema
const &) =
delete;
429 PhotoCalibSchema(PhotoCalibSchema &&) =
delete;
430 PhotoCalibSchema &operator=(PhotoCalibSchema &&) =
delete;
432 static PhotoCalibSchema
const &get() {
433 static PhotoCalibSchema
const instance;
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")),
449class PhotoCalibFactory :
public table::io::PersistableFactory {
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);
457 throw(pex::exceptions::RuntimeError(
"Unsupported version (version 0 was defined in maggies): " +
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));
465 PhotoCalibFactory(
std::string const &name) :
afw::table::io::PersistableFactory(name) {}
468 int getVersion(table::BaseRecord
const &record)
const {
472 auto versionKey = record.getSchema().
find<
int>(versionName);
473 version = record.get(versionKey.key);
474 }
catch (
const pex::exceptions::NotFoundError &) {
482std::string getPhotoCalibPersistenceName() {
return "PhotoCalib"; }
484PhotoCalibFactory registration(getPhotoCalibPersistenceName());
493int const CALIB_TABLE_CURRENT_VERSION = 2;
505 CalibKeys(
const CalibKeys &) =
delete;
506 CalibKeys &operator=(
const CalibKeys &) =
delete;
509 CalibKeys(CalibKeys &&) =
delete;
510 CalibKeys &operator=(CalibKeys &&) =
delete;
512 CalibKeys(
int tableVersion = CALIB_TABLE_CURRENT_VERSION)
514 if (tableVersion == 1) {
517 "midtime",
"middle of the time of the exposure relative to Unix epoch",
"ns");
525class CalibFactory :
public table::io::PersistableFactory {
528 CatalogVector
const &catalogs)
const override {
531 int tableVersion = 1;
533 catalogs.front().getSchema().find<
double>(EXPTIME_FIELD_NAME);
534 }
catch (pex::exceptions::NotFoundError
const &) {
535 tableVersion = CALIB_TABLE_CURRENT_VERSION;
538 CalibKeys
const keys{tableVersion};
542 table::BaseRecord
const &record =
catalogs.front().front();
550 explicit CalibFactory(
std::string const &name) : table::io::PersistableFactory(name) {}
553std::string getCalibPersistenceName() {
return "Calib"; }
555CalibFactory calibRegistration(getCalibPersistenceName());
559std::string PhotoCalib::getPersistenceName()
const {
return getPhotoCalibPersistenceName(); }
562 PhotoCalibSchema
const &keys = PhotoCalibSchema::get();
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);
577 return _calibrationMean;
579 return _calibration->evaluate(point);
582ndarray::Array<double, 1> PhotoCalib::evaluateArray(ndarray::Array<double, 1>
const &xx,
583 ndarray::Array<double, 1>
const &yy)
const {
585 ndarray::Array<double, 1>
result = ndarray::allocate(ndarray::makeVector(xx.size()));
586 result.deep() = _calibrationMean;
589 return _calibration->evaluate(xx, yy);
594 ndarray::Array<double, 1> xx = ndarray::allocate(ndarray::makeVector(sourceCatalog.size()));
595 ndarray::Array<double, 1> yy = ndarray::allocate(ndarray::makeVector(sourceCatalog.size()));
597 for (
auto const &rec : sourceCatalog) {
598 auto point = rec.getCentroid();
599 xx[i] = point.getX();
600 yy[i] = point.getY();
603 return evaluateArray(xx, yy);
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;
612 auto calibration = evaluateCatalog(sourceCatalog);
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);
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;
632 auto calibration = evaluateCatalog(sourceCatalog);
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);
646 auto key =
"FLUXMAG0";
647 if (metadata.
exists(key)) {
651 double instFluxMag0Err = 0.0;
653 if (metadata.
exists(key)) {
657 return makePhotoCalibFromCalibZeroPoint(instFluxMag0, instFluxMag0Err);
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
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.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
A class to manipulate images, masks, and variance as a single object.
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.
Tag types used to declare specialized field types.
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Schema getSchema() const
Return the Schema that holds this record's fields and keys.
std::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
A mapping between the keys of two Schemas, used to copy data between them.
Record class that contains measurements made on a single exposure.
CentroidSlotDefinition::MeasValue getCentroid() const
Get the value of the Centroid slot measurement.
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.
Class for storing generic metadata.
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.
Reports errors in the logical structure of the program.
Reports attempts to access elements using an invalid key.
Utilities for converting between flux and magnitude in C++.
T emplace_back(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
const double referenceFlux
The Oke & Gunn (1983) AB magnitude reference flux, in nJy (often approximated as 3631....
double ABMagnitudeToNanojansky(double magnitude)
Convert an AB magnitude to a flux in nanojansky.
double nanojanskyToABMagnitude(double flux)
Convert a flux in nanojansky to AB magnitude.
T setprecision(T... args)
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override