LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
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() : _midTime(), _exptime(0.0), _fluxMag0(0.0), _fluxMag0Sigma(0.0) {}
54 Calib::Calib(double fluxMag0): _midTime(), _exptime(0.0), _fluxMag0(fluxMag0), _fluxMag0Sigma(0.0) {}
60 Calib::Calib(std::vector<CONST_PTR(Calib)> const& calibs
61  ) :
62  _midTime(), _exptime(0.0), _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  double midTimeSum = 0.0; // sum(time*expTime)
73  for (std::vector<CONST_PTR(Calib)>::const_iterator ptr = calibs.begin(); ptr != calibs.end(); ++ptr) {
74  Calib const& calib = **ptr;
75 
76  if (::fabs(fluxMag00 - calib._fluxMag0) > std::numeric_limits<double>::epsilon() ||
77  ::fabs(fluxMag0Sigma0 - calib._fluxMag0Sigma) > std::numeric_limits<double>::epsilon()) {
78  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
79  (boost::format("You may only combine calibs with the same fluxMag0: "
80  "%g +- %g v %g +- %g")
81  % calib.getFluxMag0().first % calib.getFluxMag0().second
82  % calibs[0]->getFluxMag0().first % calibs[0]->getFluxMag0().second
83  ).str());
84  }
85 
86  double const exptime = calib._exptime;
87 
88  midTimeSum += calib._midTime.get()*exptime;
89  _exptime += exptime;
90  }
91 
92  daf::base::DateTime tmp(midTimeSum/_exptime); // there's no way to set the double value directly
93  using std::swap;
94  swap(_midTime, tmp);
95 }
96 
102  double exptime = 0.0;
103  double fluxMag0 = 0.0, fluxMag0Sigma = 0.0;
104 
105  std::string key = "TIME-MID";
106  if (metadata->exists(key)) {
107  midTime = lsst::daf::base::DateTime(boost::algorithm::trim_right_copy(metadata->getAsString(key)),
109  }
110 
111  key = "EXPTIME";
112  if (metadata->exists(key)) {
113  try {
114  exptime = metadata->getAsDouble(key);
115  } catch (lsst::pex::exceptions::TypeError & err) {
116  std::string exptimeStr = metadata->getAsString(key);
117  exptime = std::stod(exptimeStr);
118  }
119  }
120 
121  key = "FLUXMAG0";
122  if (metadata->exists(key)) {
123  fluxMag0 = metadata->getAsDouble(key);
124 
125  key = "FLUXMAG0ERR";
126  if (metadata->exists(key)) {
127  fluxMag0Sigma = metadata->getAsDouble(key);
128  }
129  }
130 
131  _midTime = midTime;
132  _exptime = exptime;
135 }
139 bool Calib::_throwOnNegativeFlux = true;
143 void
144 Calib::setThrowOnNegativeFlux(bool raiseException
145  )
146 {
147  _throwOnNegativeFlux = raiseException;
148 }
149 
153 bool
155 {
156  return _throwOnNegativeFlux;
157 }
158 
159 namespace detail {
166  )
167 {
168  int nstripped = 0;
169 
170  std::string key = "TIME-MID";
171  if (metadata->exists(key)) {
172  metadata->remove(key);
173  nstripped++;
174  }
175 
176  key = "EXPTIME";
177  if (metadata->exists(key)) {
178  metadata->remove(key);
179  nstripped++;
180  }
181 
182  key = "FLUXMAG0";
183  if (metadata->exists(key)) {
184  metadata->remove(key);
185  nstripped++;
186  }
187 
188  key = "FLUXMAG0ERR";
189  if (metadata->exists(key)) {
190  metadata->remove(key);
191  nstripped++;
192  }
193 
194  return nstripped;
195 }
196 }
197 
203 bool Calib::operator==(Calib const& rhs) const {
204  return
205  ::fabs(_midTime.get() - rhs._midTime.get()) < std::numeric_limits<double>::epsilon() &&
206  _exptime == rhs._exptime &&
207  _fluxMag0 == rhs._fluxMag0 &&
209 }
210 
217  )
218 {
219  _midTime = midTime;
220 }
221 
226 {
227  return _midTime;
228 }
229 
237  std::shared_ptr<const lsst::afw::cameraGeom::Detector>,
239  ) const
240 {
241  return _midTime;
242 }
243 
247 void Calib::setExptime(double exptime
248  ) {
249  _exptime = exptime;
250 }
251 
255 double Calib::getExptime() const
256 {
257  return _exptime;
258 }
259 
264  double fluxMag0Sigma
265  )
266 {
269 }
270 void Calib::setFluxMag0(std::pair<double, double> fluxMag0AndSigma
271  )
272 {
273  _fluxMag0 = fluxMag0AndSigma.first;
274  _fluxMag0Sigma = fluxMag0AndSigma.second;
275 }
276 
280 std::pair<double, double> Calib::getFluxMag0() const
281 {
282  return std::make_pair(_fluxMag0, _fluxMag0Sigma);
283 }
284 
285 namespace {
286 inline void checkNegativeFlux0(double fluxMag0) {
287  if (fluxMag0 <= 0) {
288  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
289  (boost::format("Flux of 0-mag object must be >= 0: saw %g") % fluxMag0).str());
290  }
291 }
292 inline bool isNegativeFlux(double flux, bool doThrow)
293 {
294  if (flux <= 0) {
295  if (doThrow) {
296  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
297  (boost::format("Flux must be >= 0: saw %g") % flux).str());
298  }
299  return true;
300  }
301  return false;
302 }
303 inline double convertToFlux(double fluxMag0, double mag) {
304  return fluxMag0 * ::pow(10.0, -0.4 * mag);
305 }
306 inline double convertToFluxErr(double fluxMag0InvSNR, double flux, double magErr) {
307  // Want to:
308  // return flux * hypot(_fluxMag0Sigma/_fluxMag0, 0.4*std::log(10)*magSigma/mag);
309  // But hypot is not standard C++ so use <http://en.wikipedia.org/wiki/Hypot#Implementation>
310  double a = fluxMag0InvSNR;
311  double b = 0.4 * std::log(10.0) * magErr;
312  if (std::abs(a) < std::abs(b)) {
313  std::swap(a, b);
314  }
315  return flux * std::abs(a) * std::sqrt(1 + std::pow(b / a, 2));
316 }
317 inline double convertToMag(double fluxMag0, double flux) {
318  return -2.5*::log10(flux/fluxMag0);
319 }
320 inline void convertToMagWithErr(double *mag, double *magErr, double fluxMag0, double fluxMag0InvSNR,
321  double flux, double fluxErr)
322 {
323  double const rat = flux/fluxMag0;
324  double const ratErr = ::sqrt((::pow(fluxErr, 2) + ::pow(flux*fluxMag0InvSNR, 2))/::pow(fluxMag0, 2));
325 
326  *mag = -2.5*::log10(rat);
327  *magErr = 2.5/::log(10.0)*ratErr/rat;
328 }
329 
330 } // anonymous namespace
331 
335 double Calib::getFlux(double const mag
336  ) const {
337  checkNegativeFlux0(_fluxMag0);
338  return convertToFlux(_fluxMag0, mag);
339 }
340 ndarray::Array<double,1> Calib::getFlux(ndarray::Array<double const,1> const & mag) const {
341  checkNegativeFlux0(_fluxMag0);
342  ndarray::Array<double,1> flux = ndarray::allocate(mag.size());
343  ndarray::Array<double const,1>::Iterator inIter = mag.begin();
344  ndarray::Array<double,1>::Iterator outIter = flux.begin();
345  for (; inIter != mag.end(); ++inIter, ++outIter) {
346  *outIter = convertToFlux(_fluxMag0, *inIter);
347  }
348  return flux;
349 }
350 
356 std::pair<double, double> Calib::getFlux(
357  double const mag,
358  double const magSigma
359  ) const
360 {
361  checkNegativeFlux0(_fluxMag0);
362  double const flux = convertToFlux(_fluxMag0, mag);
363  double const fluxErr = convertToFluxErr(_fluxMag0Sigma/_fluxMag0, flux, magSigma);
364  return std::make_pair(flux, fluxErr);
365 }
366 
367 std::pair<ndarray::Array<double,1>, ndarray::Array<double,1> > Calib::getFlux(
368  ndarray::Array<double const,1> const & mag,
369  ndarray::Array<double const,1> const & magErr
370 ) const {
371  checkNegativeFlux0(_fluxMag0);
372  if (mag.size() != magErr.size()) {
373  throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
374  (boost::format("Size of mag (%d) and magErr (%d) don't match") %
375  mag.size() % magErr.size()).str());
376  }
377 
378  ndarray::Array<double,1> flux = ndarray::allocate(mag.size());
379  ndarray::Array<double,1> fluxErr = ndarray::allocate(mag.size());
380  ndarray::Array<double const,1>::Iterator magIter = mag.begin();
381  ndarray::Array<double const,1>::Iterator magErrIter = magErr.begin();
382  ndarray::Array<double,1>::Iterator fluxIter = flux.begin();
383  ndarray::Array<double,1>::Iterator fluxErrIter = fluxErr.begin();
384 
385  double fluxMag0InvSNR = _fluxMag0Sigma/_fluxMag0;
386  for (; magIter != mag.end(); ++magIter, ++magErrIter, ++fluxIter, ++fluxErrIter) {
387  *fluxIter = convertToFlux(_fluxMag0, *magIter);
388  *fluxErrIter = convertToFluxErr(fluxMag0InvSNR, *fluxIter, *magErrIter);
389  }
390 
391  return std::make_pair(flux, fluxErr);
392 }
393 
397 double Calib::getMagnitude(double const flux
398  ) const
399 {
400  checkNegativeFlux0(_fluxMag0);
401  if (isNegativeFlux(flux, Calib::getThrowOnNegativeFlux())) {
402  return std::numeric_limits<double>::quiet_NaN();
403  }
404  return convertToMag(_fluxMag0, flux);
405 }
406 
410 std::pair<double, double> Calib::getMagnitude(double const flux,
411  double const fluxErr
412  ) const
413 {
414  checkNegativeFlux0(_fluxMag0);
415  if (isNegativeFlux(flux, Calib::getThrowOnNegativeFlux())) {
416  double const NaN = std::numeric_limits<double>::quiet_NaN();
417  return std::make_pair(NaN, NaN);
418  }
419 
420  double mag, magErr;
421  convertToMagWithErr(&mag, &magErr, _fluxMag0, _fluxMag0Sigma/_fluxMag0, flux, fluxErr);
422  return std::make_pair(mag, magErr);
423 }
424 
425 ndarray::Array<double,1> Calib::getMagnitude(ndarray::Array<double const,1> const & flux) const {
426  checkNegativeFlux0(_fluxMag0);
427  ndarray::Array<double,1> mag = ndarray::allocate(flux.size());
428  ndarray::Array<double const,1>::Iterator fluxIter = flux.begin();
429  ndarray::Array<double,1>::Iterator magIter = mag.begin();
430  int nonPositive = 0;
431  for (; fluxIter != flux.end(); ++fluxIter, ++magIter) {
432  if (isNegativeFlux(*fluxIter, false)) {
433  ++nonPositive;
434  *magIter = std::numeric_limits<double>::quiet_NaN();
435  continue;
436  }
437  *magIter = convertToMag(_fluxMag0, *fluxIter);
438  }
439  if (nonPositive && Calib::getThrowOnNegativeFlux()) {
440  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
441  (boost::format("Flux must be >= 0: %d non-positive seen") % nonPositive).str());
442  }
443  return mag;
444 }
445 
446 std::pair<ndarray::Array<double,1>, ndarray::Array<double,1> > Calib::getMagnitude(
447  ndarray::Array<double const,1> const & flux,
448  ndarray::Array<double const,1> const & fluxErr
449 ) const {
450  checkNegativeFlux0(_fluxMag0);
451  if (flux.size() != fluxErr.size()) {
452  throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
453  (boost::format("Size of flux (%d) and fluxErr (%d) don't match") %
454  flux.size() % fluxErr.size()).str());
455  }
456 
457  ndarray::Array<double,1> mag = ndarray::allocate(flux.size());
458  ndarray::Array<double,1> magErr = ndarray::allocate(flux.size());
459  ndarray::Array<double const,1>::Iterator fluxIter = flux.begin();
460  ndarray::Array<double const,1>::Iterator fluxErrIter = fluxErr.begin();
461  ndarray::Array<double,1>::Iterator magIter = mag.begin();
462  ndarray::Array<double,1>::Iterator magErrIter = magErr.begin();
463  int nonPositive = 0;
464  double fluxMag0InvSNR = _fluxMag0Sigma/_fluxMag0;
465  for (; fluxIter != flux.end(); ++fluxIter, ++fluxErrIter, ++magIter, ++magErrIter) {
466  if (isNegativeFlux(*fluxIter, false)) {
467  ++nonPositive;
468  double const NaN = std::numeric_limits<double>::quiet_NaN();
469  *magIter = NaN;
470  *magErrIter = NaN;
471  continue;
472  }
473  double f, df;
474  convertToMagWithErr(&f, &df, _fluxMag0, fluxMag0InvSNR, *fluxIter, *fluxErrIter);
475  *magIter = f;
476  *magErrIter = df;
477  }
478  if (nonPositive && Calib::getThrowOnNegativeFlux()) {
479  throw LSST_EXCEPT(lsst::pex::exceptions::DomainError,
480  (boost::format("Flux must be >= 0: %d non-positive seen") % nonPositive).str());
481  }
482  return std::make_pair(mag, magErr);
483 }
484 
485 namespace {
486 
487 class CalibSchema {
488 public:
489  table::Schema schema;
490  table::Key<std::int64_t> midTime;
491  table::Key<double> expTime;
492  table::Key<double> fluxMag0;
493  table::Key<double> fluxMag0Sigma;
494 
495  static CalibSchema const & get() {
496  static CalibSchema instance;
497  return instance;
498  }
499 
500  // No copying
501  CalibSchema (const CalibSchema&) = delete;
502  CalibSchema& operator=(const CalibSchema&) = delete;
503 
504  // No moving
505  CalibSchema (CalibSchema&&) = delete;
506  CalibSchema& operator=(CalibSchema&&) = delete;
507 
508 private:
509  CalibSchema() :
510  schema(),
511  midTime(schema.addField<std::int64_t>(
512  "midtime", "middle of the time of the exposure relative to Unix epoch", "ns"
513  )),
514  expTime(schema.addField<double>("exptime", "exposure time", "s")),
515  fluxMag0(schema.addField<double>("fluxmag0", "flux of a zero-magnitude object", "count")),
516  fluxMag0Sigma(schema.addField<double>("fluxmag0.err", "1-sigma error on fluxmag0", "count"))
517  {
518  schema.getCitizen().markPersistent();
519  }
520 };
521 
522 class CalibFactory : public table::io::PersistableFactory {
523 public:
524 
525  virtual PTR(table::io::Persistable)
526  read(InputArchive const & archive, CatalogVector const & catalogs) const {
527  CalibSchema const & keys = CalibSchema::get();
528  LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
529  LSST_ARCHIVE_ASSERT(catalogs.front().size() == 1u);
530  LSST_ARCHIVE_ASSERT(catalogs.front().getSchema() == keys.schema);
531  table::BaseRecord const & record = catalogs.front().front();
532  PTR(Calib) result(new Calib());
533  result->setMidTime(daf::base::DateTime(static_cast<long long>(record.get(keys.midTime))));
534  result->setExptime(record.get(keys.expTime));
535  result->setFluxMag0(record.get(keys.fluxMag0), record.get(keys.fluxMag0Sigma));
536  return result;
537  }
538 
539  explicit CalibFactory(std::string const & name) : table::io::PersistableFactory(name) {}
540 
541 };
542 
543 std::string getCalibPersistenceName() { return "Calib"; }
544 
545 CalibFactory registration(getCalibPersistenceName());
546 
547 } // anonymous
548 
549 std::string Calib::getPersistenceName() const { return getCalibPersistenceName(); }
550 
551 void Calib::write(OutputArchiveHandle & handle) const {
552  CalibSchema const & keys = CalibSchema::get();
553  table::BaseCatalog cat = handle.makeCatalog(keys.schema);
554  PTR(table::BaseRecord) record = cat.addNew();
555  record->set(keys.midTime, getMidTime().nsecs());
556  record->set(keys.expTime, getExptime());
557  std::pair<double,double> fluxMag0 = getFluxMag0();
558  record->set(keys.fluxMag0, fluxMag0.first);
559  record->set(keys.fluxMag0Sigma, fluxMag0.second);
560  handle.saveCatalog(cat);
561 }
562 
563 void Calib::operator*=(double const scale) {
564  _fluxMag0 *= scale;
566 }
567 
568 }}} // lsst::afw::image
Class for handling dates/times, including MJD, UTC, and TAI.
Definition: DateTime.h:58
table::Key< std::string > name
Definition: ApCorrMap.cc:71
table::Key< double > fluxMag0Sigma
Definition: Calib.cc:493
An object passed to Persistable::write to allow it to persist itself.
virtual void remove(std::string const &name)
Definition: PropertySet.cc:754
A custom container class for records, based on std::vector.
Definition: Catalog.h:95
afw::table::Schema schema
Definition: GaussianPsf.cc:41
double get(DateSystem system=MJD, Timescale scale=TAI) const
Definition: DateTime.cc:474
void swap(Mask< PixelT > &a, Mask< PixelT > &b)
Definition: Mask.cc:524
#define CONST_PTR(...)
Definition: base.h:47
table::Key< double > expTime
Definition: Calib.cc:491
void operator*=(double const scale)
Definition: Calib.cc:563
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
bool operator==(Calib const &rhs) const
Definition: Calib.cc:203
def log
Definition: log.py:83
table::Key< std::int64_t > midTime
Definition: Calib.cc:490
void setExptime(double exptime)
Definition: Calib.cc:247
virtual std::string getPersistenceName() const
Return the unique name used to persist this object and look up its factory.
Definition: Calib.cc:549
static void setThrowOnNegativeFlux(bool raiseException)
Definition: Calib.cc:144
double getExptime() const
Definition: Calib.cc:255
std::pair< double, double > getFluxMag0() const
Definition: Calib.cc:280
static bool _throwOnNegativeFlux
Definition: Calib.h:150
int stripCalibKeywords(boost::shared_ptr< lsst::daf::base::PropertySet > metadata)
Definition: Calib.cc:165
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
double getFlux(double const mag) const
Definition: Calib.cc:335
Base class for all records.
Definition: BaseRecord.h:27
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
Definition: Persistable.h:45
double getAsDouble(std::string const &name) const
Definition: PropertySet.cc:406
Class for storing generic metadata.
Definition: PropertySet.h:82
#define PTR(...)
Definition: base.h:41
virtual void write(OutputArchiveHandle &handle) const
Write the object to one or more catalogs.
Definition: Calib.cc:551
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
std::string getAsString(std::string const &name) const
Definition: PropertySet.cc:444
void setFluxMag0(double fluxMag0, double fluxMag0Sigma=0.0)
Definition: Calib.cc:263
afw::table::Key< double > b
static bool getThrowOnNegativeFlux()
Definition: Calib.cc:154
table::Key< double > fluxMag0
Definition: Calib.cc:492
Interface for PropertySet class.
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
Include files required for standard LSST Exception handling.
lsst::daf::base::DateTime getMidTime() const
Definition: Calib.cc:225
bool exists(std::string const &name) const
Definition: PropertySet.cc:190
lsst::daf::base::DateTime _midTime
Definition: Calib.h:146
double getMagnitude(double const flux) const
Definition: Calib.cc:397
void setMidTime(lsst::daf::base::DateTime const &midTime)
Definition: Calib.cc:216