34 #include "boost/format.hpp"
35 #include "boost/algorithm/string/trim.hpp"
46 namespace lsst {
namespace afw {
namespace image {
62 _fluxMag0(0.0), _fluxMag0Sigma(0.0)
65 throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
66 "You must provide at least one input Calib");
69 double const fluxMag00 = calibs[0]->_fluxMag0;
70 double const fluxMag0Sigma0 = calibs[0]->_fluxMag0Sigma;
72 for (std::vector<
CONST_PTR(
Calib)>::const_iterator ptr = calibs.begin(); ptr != calibs.end(); ++ptr) {
73 Calib const& calib = **ptr;
75 if (::fabs(fluxMag00 - calib.
_fluxMag0) > std::numeric_limits<double>::epsilon() ||
76 ::fabs(fluxMag0Sigma0 - calib.
_fluxMag0Sigma) > std::numeric_limits<double>::epsilon()) {
77 throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
78 (
boost::format(
"You may only combine calibs with the same fluxMag0: "
79 "%g +- %g v %g +- %g")
81 % calibs[0]->getFluxMag0().first % calibs[0]->getFluxMag0().second
94 auto key =
"FLUXMAG0";
95 if (metadata->
exists(key)) {
99 if (metadata->
exists(key)) {
141 auto key =
"FLUXMAG0";
142 if (metadata->
exists(key)) {
148 if (metadata->
exists(key)) {
195 inline void checkNegativeFlux0(
double fluxMag0) {
197 throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
198 (
boost::format(
"Flux of 0-mag object must be >= 0: saw %g") % fluxMag0).str());
201 inline bool isNegativeFlux(
double flux,
bool doThrow)
205 throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
212 inline double convertToFlux(
double fluxMag0,
double mag) {
213 return fluxMag0 * ::pow(10.0, -0.4 * mag);
215 inline double convertToFluxErr(
double fluxMag0InvSNR,
double flux,
double magErr) {
219 double a = fluxMag0InvSNR;
220 double b = 0.4 *
std::log(10.0) * magErr;
221 if (std::abs(a) < std::abs(b)) {
224 return flux * std::abs(a) * std::sqrt(1 + std::pow(b / a, 2));
226 inline double convertToMag(
double fluxMag0,
double flux) {
227 return -2.5*::log10(flux/fluxMag0);
229 inline void convertToMagWithErr(
double *mag,
double *magErr,
double fluxMag0,
double fluxMag0InvSNR,
230 double flux,
double fluxErr)
233 double const ratErr = ::sqrt((::pow(fluxErr, 2) + ::pow(flux*fluxMag0InvSNR, 2))/::pow(fluxMag0, 2));
235 *mag = -2.5*::log10(rat);
236 *magErr = 2.5/
::log(10.0)*ratErr/rat;
249 ndarray::Array<double,1>
Calib::getFlux(ndarray::Array<double const,1>
const & mag)
const {
251 ndarray::Array<double,1> flux = ndarray::allocate(mag.size());
252 ndarray::Array<double const,1>::Iterator inIter = mag.begin();
253 ndarray::Array<double,1>::Iterator outIter = flux.begin();
254 for (; inIter != mag.end(); ++inIter, ++outIter) {
255 *outIter = convertToFlux(
_fluxMag0, *inIter);
267 double const magSigma
271 double const flux = convertToFlux(
_fluxMag0, mag);
273 return std::make_pair(flux, fluxErr);
277 ndarray::Array<double const,1>
const & mag,
278 ndarray::Array<double const,1>
const & magErr
281 if (mag.size() != magErr.size()) {
282 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
283 (
boost::format(
"Size of mag (%d) and magErr (%d) don't match") %
284 mag.size() % magErr.size()).str());
287 ndarray::Array<double,1> flux = ndarray::allocate(mag.size());
288 ndarray::Array<double,1> fluxErr = ndarray::allocate(mag.size());
289 ndarray::Array<double const,1>::Iterator magIter = mag.begin();
290 ndarray::Array<double const,1>::Iterator magErrIter = magErr.begin();
291 ndarray::Array<double,1>::Iterator fluxIter = flux.begin();
292 ndarray::Array<double,1>::Iterator fluxErrIter = fluxErr.begin();
295 for (; magIter != mag.end(); ++magIter, ++magErrIter, ++fluxIter, ++fluxErrIter) {
296 *fluxIter = convertToFlux(
_fluxMag0, *magIter);
297 *fluxErrIter = convertToFluxErr(fluxMag0InvSNR, *fluxIter, *magErrIter);
300 return std::make_pair(flux, fluxErr);
311 return std::numeric_limits<double>::quiet_NaN();
325 double const NaN = std::numeric_limits<double>::quiet_NaN();
326 return std::make_pair(NaN, NaN);
331 return std::make_pair(mag, magErr);
336 ndarray::Array<double,1> mag = ndarray::allocate(flux.size());
337 ndarray::Array<double const,1>::Iterator fluxIter = flux.begin();
338 ndarray::Array<double,1>::Iterator magIter = mag.begin();
340 for (; fluxIter != flux.end(); ++fluxIter, ++magIter) {
341 if (isNegativeFlux(*fluxIter,
false)) {
343 *magIter = std::numeric_limits<double>::quiet_NaN();
346 *magIter = convertToMag(
_fluxMag0, *fluxIter);
349 throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
350 (
boost::format(
"Flux must be >= 0: %d non-positive seen") % nonPositive).str());
356 ndarray::Array<double const,1>
const & flux,
357 ndarray::Array<double const,1>
const & fluxErr
360 if (flux.size() != fluxErr.size()) {
361 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
362 (
boost::format(
"Size of flux (%d) and fluxErr (%d) don't match") %
363 flux.size() % fluxErr.size()).str());
366 ndarray::Array<double,1> mag = ndarray::allocate(flux.size());
367 ndarray::Array<double,1> magErr = ndarray::allocate(flux.size());
368 ndarray::Array<double const,1>::Iterator fluxIter = flux.begin();
369 ndarray::Array<double const,1>::Iterator fluxErrIter = fluxErr.begin();
370 ndarray::Array<double,1>::Iterator magIter = mag.begin();
371 ndarray::Array<double,1>::Iterator magErrIter = magErr.begin();
374 for (; fluxIter != flux.end(); ++fluxIter, ++fluxErrIter, ++magIter, ++magErrIter) {
375 if (isNegativeFlux(*fluxIter,
false)) {
377 double const NaN = std::numeric_limits<double>::quiet_NaN();
383 convertToMagWithErr(&f, &df,
_fluxMag0, fluxMag0InvSNR, *fluxIter, *fluxErrIter);
388 throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
389 (
boost::format(
"Flux must be >= 0: %d non-positive seen") % nonPositive).str());
391 return std::make_pair(mag, magErr);
396 int const CALIB_TABLE_CURRENT_VERSION = 2;
397 std::string
const EXPTIME_FIELD_NAME =
"exptime";
408 CalibKeys (
const CalibKeys&) =
delete;
409 CalibKeys& operator=(
const CalibKeys&) =
delete;
412 CalibKeys (CalibKeys&&) =
delete;
413 CalibKeys& operator=(CalibKeys&&) =
delete;
415 CalibKeys(
int tableVersion=CALIB_TABLE_CURRENT_VERSION) :
422 if (tableVersion == 1) {
425 "midtime",
"middle of the time of the exposure relative to Unix epoch",
"ns"
427 expTime =
schema.addField<
double>(EXPTIME_FIELD_NAME,
"exposure time",
"s");
429 fluxMag0 =
schema.addField<
double>(
"fluxmag0",
"flux of a zero-magnitude object",
"count");
430 fluxMag0Sigma =
schema.addField<
double>(
"fluxmag0.err",
"1-sigma error on fluxmag0",
"count");
434 class CalibFactory :
public table::io::PersistableFactory {
437 virtual PTR(table::io::Persistable)
438 read(InputArchive const & archive, CatalogVector const & catalogs)
const {
441 int tableVersion = 1;
443 catalogs.front().getSchema().find<
double>(EXPTIME_FIELD_NAME);
444 }
catch (pex::exceptions::NotFoundError) {
445 tableVersion = CALIB_TABLE_CURRENT_VERSION;
448 CalibKeys
const keys{tableVersion};
452 table::BaseRecord
const & record = catalogs.front().front();
453 PTR(Calib) result(new Calib());
454 result->setFluxMag0(record.get(keys.fluxMag0), record.get(keys.
fluxMag0Sigma));
458 explicit CalibFactory(std::
string const &
name) : table::io::PersistableFactory(
name) {}
462 std::string getCalibPersistenceName() {
return "Calib"; }
464 CalibFactory registration(getCalibPersistenceName());
471 CalibKeys
const keys{};
475 record->set(keys.fluxMag0, fluxMag0.first);
476 record->set(keys.fluxMag0Sigma, fluxMag0.second);
table::Key< std::string > name
table::Key< double > fluxMag0Sigma
An object passed to Persistable::write to allow it to persist itself.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
A custom container class for records, based on std::vector.
afw::table::Schema schema
Include files required for standard LSST Exception handling.
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
void swap(Mask< PixelT > &a, Mask< PixelT > &b)
double getAsDouble(std::string const &name) const
Get the last value for any arithmetic property name (possibly hierarchical).
table::Key< double > expTime
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
void operator*=(double const scale)
Describe an exposure's calibration.
table::Key< table::Array< Kernel::Pixel > > image
boost::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
bool operator==(Calib const &rhs) const
Are two Calibs identical?
table::Key< std::int64_t > midTime
virtual std::string getPersistenceName() const
Return the unique name used to persist this object and look up its factory.
static void setThrowOnNegativeFlux(bool raiseException)
Set whether Calib should throw an exception when asked to convert a flux to a magnitude.
std::pair< double, double > getFluxMag0() const
Return the flux, and error in flux, of a zero-magnitude object.
static bool _throwOnNegativeFlux
Control whether we throw an exception when faced with a negative flux.
Classes to support calibration (e.g.
int stripCalibKeywords(boost::shared_ptr< lsst::daf::base::PropertySet > metadata)
Remove Calib-related keywords from the metadata.
#define LSST_EXCEPT(type,...)
Create an exception with a given type and message and optionally other arguments (dependent on the ty...
double getFlux(double const mag) const
Return a flux (in ADUs) given a magnitude.
Base class for all records.
Class for storing generic metadata.
virtual void write(OutputArchiveHandle &handle) const
Write the object to one or more catalogs.
void setFluxMag0(double fluxMag0, double fluxMag0Sigma=0.0)
Set the flux of a zero-magnitude object.
afw::table::Key< double > b
static bool getThrowOnNegativeFlux()
Tell me whether Calib will throw an exception if asked to convert a flux to a magnitude.
table::Key< double > fluxMag0
Interface for PropertySet class.
virtual void remove(std::string const &name)
Removes all values for a property name (possibly hierarchical).
#define CONST_PTR(...)
A shared pointer to a const object.
double getMagnitude(double const flux) const
Return a magnitude given a flux.