LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
Calib.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008-2016 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
30 #include <cmath>
31 #include <cstdint>
32 #include <string>
33 
34 #include "boost/format.hpp"
35 #include "boost/algorithm/string/trim.hpp"
36 
37 #include "ndarray.h"
38 #include "lsst/pex/exceptions.h"
40 #include "lsst/afw/image/Calib.h"
45 
46 namespace lsst { namespace afw { namespace image {
50 Calib::Calib() : _fluxMag0(0.0), _fluxMag0Sigma(0.0) {}
54 Calib::Calib(double fluxMag0): _fluxMag0(fluxMag0), _fluxMag0Sigma(0.0) {}
60 Calib::Calib(std::vector<CONST_PTR(Calib)> const& calibs
61  ) :
62  _fluxMag0(0.0), _fluxMag0Sigma(0.0)
63 {
64  if (calibs.empty()) {
65  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
66  "You must provide at least one input Calib");
67  }
68 
69  double const fluxMag00 = calibs[0]->_fluxMag0;
70  double const fluxMag0Sigma0 = calibs[0]->_fluxMag0Sigma;
71 
72  for (std::vector<CONST_PTR(Calib)>::const_iterator ptr = calibs.begin(); ptr != calibs.end(); ++ptr) {
73  Calib const& calib = **ptr;
74 
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")
80  % calib.getFluxMag0().first % calib.getFluxMag0().second
81  % calibs[0]->getFluxMag0().first % calibs[0]->getFluxMag0().second
82  ).str());
83  }
84  }
85 
86 }
87 
92  double fluxMag0 = 0.0, fluxMag0Sigma = 0.0;
93 
94  auto key = "FLUXMAG0";
95  if (metadata->exists(key)) {
96  fluxMag0 = metadata->getAsDouble(key);
97 
98  key = "FLUXMAG0ERR";
99  if (metadata->exists(key)) {
100  fluxMag0Sigma = metadata->getAsDouble(key);
101  }
102  }
103 
106 }
110 bool Calib::_throwOnNegativeFlux = true;
114 void
115 Calib::setThrowOnNegativeFlux(bool raiseException
116  )
117 {
118  _throwOnNegativeFlux = raiseException;
119 }
120 
124 bool
126 {
127  return _throwOnNegativeFlux;
128 }
129 
130 namespace detail {
137  )
138 {
139  int nstripped = 0;
140 
141  auto key = "FLUXMAG0";
142  if (metadata->exists(key)) {
143  metadata->remove(key);
144  nstripped++;
145  }
146 
147  key = "FLUXMAG0ERR";
148  if (metadata->exists(key)) {
149  metadata->remove(key);
150  nstripped++;
151  }
152 
153  return nstripped;
154 }
155 }
156 
162 bool Calib::operator==(Calib const& rhs) const {
163  return
164  _fluxMag0 == rhs._fluxMag0 &&
166 }
167 
172  double fluxMag0Sigma
173  )
174 {
177 }
178 void Calib::setFluxMag0(std::pair<double, double> fluxMag0AndSigma
179  )
180 {
181  _fluxMag0 = fluxMag0AndSigma.first;
182  _fluxMag0Sigma = fluxMag0AndSigma.second;
183 }
184 
188 std::pair<double, double> Calib::getFluxMag0() const
189 {
190  return std::make_pair(_fluxMag0, _fluxMag0Sigma);
191 }
192 
193 namespace {
194 
195 inline void checkNegativeFlux0(double fluxMag0) {
196  if (fluxMag0 <= 0) {
197  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
198  (boost::format("Flux of 0-mag object must be >= 0: saw %g") % fluxMag0).str());
199  }
200 }
201 inline bool isNegativeFlux(double flux, bool doThrow)
202 {
203  if (flux <= 0) {
204  if (doThrow) {
205  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
206  (boost::format("Flux must be >= 0: saw %g") % flux).str());
207  }
208  return true;
209  }
210  return false;
211 }
212 inline double convertToFlux(double fluxMag0, double mag) {
213  return fluxMag0 * ::pow(10.0, -0.4 * mag);
214 }
215 inline double convertToFluxErr(double fluxMag0InvSNR, double flux, double magErr) {
216  // Want to:
217  // return flux * hypot(_fluxMag0Sigma/_fluxMag0, 0.4*std::log(10)*magSigma/mag);
218  // But hypot is not standard C++ so use <http://en.wikipedia.org/wiki/Hypot#Implementation>
219  double a = fluxMag0InvSNR;
220  double b = 0.4 * std::log(10.0) * magErr;
221  if (std::abs(a) < std::abs(b)) {
222  std::swap(a, b);
223  }
224  return flux * std::abs(a) * std::sqrt(1 + std::pow(b / a, 2));
225 }
226 inline double convertToMag(double fluxMag0, double flux) {
227  return -2.5*::log10(flux/fluxMag0);
228 }
229 inline void convertToMagWithErr(double *mag, double *magErr, double fluxMag0, double fluxMag0InvSNR,
230  double flux, double fluxErr)
231 {
232  double const rat = flux/fluxMag0;
233  double const ratErr = ::sqrt((::pow(fluxErr, 2) + ::pow(flux*fluxMag0InvSNR, 2))/::pow(fluxMag0, 2));
234 
235  *mag = -2.5*::log10(rat);
236  *magErr = 2.5/::log(10.0)*ratErr/rat;
237 }
238 
239 } // anonymous namespace
240 
244 double Calib::getFlux(double const mag
245  ) const {
246  checkNegativeFlux0(_fluxMag0);
247  return convertToFlux(_fluxMag0, mag);
248 }
249 ndarray::Array<double,1> Calib::getFlux(ndarray::Array<double const,1> const & mag) const {
250  checkNegativeFlux0(_fluxMag0);
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);
256  }
257  return flux;
258 }
259 
265 std::pair<double, double> Calib::getFlux(
266  double const mag,
267  double const magSigma
268  ) const
269 {
270  checkNegativeFlux0(_fluxMag0);
271  double const flux = convertToFlux(_fluxMag0, mag);
272  double const fluxErr = convertToFluxErr(_fluxMag0Sigma/_fluxMag0, flux, magSigma);
273  return std::make_pair(flux, fluxErr);
274 }
275 
276 std::pair<ndarray::Array<double,1>, ndarray::Array<double,1> > Calib::getFlux(
277  ndarray::Array<double const,1> const & mag,
278  ndarray::Array<double const,1> const & magErr
279 ) const {
280  checkNegativeFlux0(_fluxMag0);
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());
285  }
286 
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();
293 
294  double fluxMag0InvSNR = _fluxMag0Sigma/_fluxMag0;
295  for (; magIter != mag.end(); ++magIter, ++magErrIter, ++fluxIter, ++fluxErrIter) {
296  *fluxIter = convertToFlux(_fluxMag0, *magIter);
297  *fluxErrIter = convertToFluxErr(fluxMag0InvSNR, *fluxIter, *magErrIter);
298  }
299 
300  return std::make_pair(flux, fluxErr);
301 }
302 
306 double Calib::getMagnitude(double const flux
307  ) const
308 {
309  checkNegativeFlux0(_fluxMag0);
310  if (isNegativeFlux(flux, Calib::getThrowOnNegativeFlux())) {
311  return std::numeric_limits<double>::quiet_NaN();
312  }
313  return convertToMag(_fluxMag0, flux);
314 }
315 
319 std::pair<double, double> Calib::getMagnitude(double const flux,
320  double const fluxErr
321  ) const
322 {
323  checkNegativeFlux0(_fluxMag0);
324  if (isNegativeFlux(flux, Calib::getThrowOnNegativeFlux())) {
325  double const NaN = std::numeric_limits<double>::quiet_NaN();
326  return std::make_pair(NaN, NaN);
327  }
328 
329  double mag, magErr;
330  convertToMagWithErr(&mag, &magErr, _fluxMag0, _fluxMag0Sigma/_fluxMag0, flux, fluxErr);
331  return std::make_pair(mag, magErr);
332 }
333 
334 ndarray::Array<double,1> Calib::getMagnitude(ndarray::Array<double const,1> const & flux) const {
335  checkNegativeFlux0(_fluxMag0);
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();
339  int nonPositive = 0;
340  for (; fluxIter != flux.end(); ++fluxIter, ++magIter) {
341  if (isNegativeFlux(*fluxIter, false)) {
342  ++nonPositive;
343  *magIter = std::numeric_limits<double>::quiet_NaN();
344  continue;
345  }
346  *magIter = convertToMag(_fluxMag0, *fluxIter);
347  }
348  if (nonPositive && Calib::getThrowOnNegativeFlux()) {
349  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
350  (boost::format("Flux must be >= 0: %d non-positive seen") % nonPositive).str());
351  }
352  return mag;
353 }
354 
355 std::pair<ndarray::Array<double,1>, ndarray::Array<double,1> > Calib::getMagnitude(
356  ndarray::Array<double const,1> const & flux,
357  ndarray::Array<double const,1> const & fluxErr
358 ) const {
359  checkNegativeFlux0(_fluxMag0);
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());
364  }
365 
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();
372  int nonPositive = 0;
373  double fluxMag0InvSNR = _fluxMag0Sigma/_fluxMag0;
374  for (; fluxIter != flux.end(); ++fluxIter, ++fluxErrIter, ++magIter, ++magErrIter) {
375  if (isNegativeFlux(*fluxIter, false)) {
376  ++nonPositive;
377  double const NaN = std::numeric_limits<double>::quiet_NaN();
378  *magIter = NaN;
379  *magErrIter = NaN;
380  continue;
381  }
382  double f, df;
383  convertToMagWithErr(&f, &df, _fluxMag0, fluxMag0InvSNR, *fluxIter, *fluxErrIter);
384  *magIter = f;
385  *magErrIter = df;
386  }
387  if (nonPositive && Calib::getThrowOnNegativeFlux()) {
388  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
389  (boost::format("Flux must be >= 0: %d non-positive seen") % nonPositive).str());
390  }
391  return std::make_pair(mag, magErr);
392 }
393 
394 namespace {
395 
396 int const CALIB_TABLE_CURRENT_VERSION = 2; // current version of ExposureTable
397 std::string const EXPTIME_FIELD_NAME = "exptime"; // name of exposure time field
398 
399 class CalibKeys {
400 public:
401  table::Schema schema;
402  table::Key<std::int64_t> midTime;
403  table::Key<double> expTime;
404  table::Key<double> fluxMag0;
405  table::Key<double> fluxMag0Sigma;
406 
407  // No copying
408  CalibKeys (const CalibKeys&) = delete;
409  CalibKeys& operator=(const CalibKeys&) = delete;
410 
411  // No moving
412  CalibKeys (CalibKeys&&) = delete;
413  CalibKeys& operator=(CalibKeys&&) = delete;
414 
415  CalibKeys(int tableVersion=CALIB_TABLE_CURRENT_VERSION) :
416  schema(),
417  midTime(),
418  expTime(),
419  fluxMag0(),
420  fluxMag0Sigma()
421  {
422  if (tableVersion == 1) {
423  // obsolete fields
424  midTime = schema.addField<std::int64_t>(
425  "midtime", "middle of the time of the exposure relative to Unix epoch", "ns"
426  );
427  expTime = schema.addField<double>(EXPTIME_FIELD_NAME, "exposure time", "s");
428  }
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");
431  }
432 };
433 
434 class CalibFactory : public table::io::PersistableFactory {
435 public:
436 
437  virtual PTR(table::io::Persistable)
438  read(InputArchive const & archive, CatalogVector const & catalogs) const {
439  // table version is not persisted, so we don't have a clean way to determine the version;
440  // the hack is version = 1 if exptime found, else current
441  int tableVersion = 1;
442  try {
443  catalogs.front().getSchema().find<double>(EXPTIME_FIELD_NAME);
444  } catch (pex::exceptions::NotFoundError) {
445  tableVersion = CALIB_TABLE_CURRENT_VERSION;
446  }
447 
448  CalibKeys const keys{tableVersion};
449  LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
450  LSST_ARCHIVE_ASSERT(catalogs.front().size() == 1u);
451  LSST_ARCHIVE_ASSERT(catalogs.front().getSchema() == keys.schema);
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));
455  return result;
456  }
457 
458  explicit CalibFactory(std::string const & name) : table::io::PersistableFactory(name) {}
459 
460 };
461 
462 std::string getCalibPersistenceName() { return "Calib"; }
463 
464 CalibFactory registration(getCalibPersistenceName());
465 
466 } // anonymous
467 
468 std::string Calib::getPersistenceName() const { return getCalibPersistenceName(); }
469 
470 void Calib::write(OutputArchiveHandle & handle) const {
471  CalibKeys const keys{};
472  table::BaseCatalog cat = handle.makeCatalog(keys.schema);
473  PTR(table::BaseRecord) record = cat.addNew();
474  std::pair<double,double> fluxMag0 = getFluxMag0();
475  record->set(keys.fluxMag0, fluxMag0.first);
476  record->set(keys.fluxMag0Sigma, fluxMag0.second);
477  handle.saveCatalog(cat);
478 }
479 
480 void Calib::operator*=(double const scale) {
481  _fluxMag0 *= scale;
483 }
484 
485 }}} // lsst::afw::image
table::Key< std::string > name
Definition: ApCorrMap.cc:71
table::Key< double > fluxMag0Sigma
Definition: Calib.cc:405
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.
Definition: Persistable.h:45
A custom container class for records, based on std::vector.
Definition: Catalog.h:95
afw::table::Schema schema
Definition: GaussianPsf.cc:41
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)
Definition: Mask.cc:524
double getAsDouble(std::string const &name) const
Get the last value for any arithmetic property name (possibly hierarchical).
Definition: PropertySet.cc:406
table::Key< double > expTime
Definition: Calib.cc:403
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
Definition: PropertySet.cc:190
void operator*=(double const scale)
Definition: Calib.cc:480
Describe an exposure&#39;s calibration.
Definition: Calib.h:82
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
boost::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:470
bool operator==(Calib const &rhs) const
Are two Calibs identical?
Definition: Calib.cc:162
def log
Definition: log.py:83
table::Key< std::int64_t > midTime
Definition: Calib.cc:402
virtual std::string getPersistenceName() const
Return the unique name used to persist this object and look up its factory.
Definition: Calib.cc:468
static void setThrowOnNegativeFlux(bool raiseException)
Set whether Calib should throw an exception when asked to convert a flux to a magnitude.
Definition: Calib.cc:115
std::pair< double, double > getFluxMag0() const
Return the flux, and error in flux, of a zero-magnitude object.
Definition: Calib.cc:188
static bool _throwOnNegativeFlux
Control whether we throw an exception when faced with a negative flux.
Definition: Calib.h:140
Classes to support calibration (e.g.
int stripCalibKeywords(boost::shared_ptr< lsst::daf::base::PropertySet > metadata)
Remove Calib-related keywords from the metadata.
Definition: Calib.cc:136
#define LSST_EXCEPT(type,...)
Create an exception with a given type and message and optionally other arguments (dependent on the ty...
Definition: Exception.h:46
double getFlux(double const mag) const
Return a flux (in ADUs) given a magnitude.
Definition: Calib.cc:244
Base class for all records.
Definition: BaseRecord.h:27
#define PTR(...)
Definition: base.h:41
Class for storing generic metadata.
Definition: PropertySet.h:82
virtual void write(OutputArchiveHandle &handle) const
Write the object to one or more catalogs.
Definition: Calib.cc:470
void setFluxMag0(double fluxMag0, double fluxMag0Sigma=0.0)
Set the flux of a zero-magnitude object.
Definition: Calib.cc:171
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.
Definition: Calib.cc:125
table::Key< double > fluxMag0
Definition: Calib.cc:404
Interface for PropertySet class.
virtual void remove(std::string const &name)
Removes all values for a property name (possibly hierarchical).
Definition: PropertySet.cc:754
#define CONST_PTR(...)
A shared pointer to a const object.
Definition: base.h:47
double getMagnitude(double const flux) const
Return a magnitude given a flux.
Definition: Calib.cc:306