LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
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 
25 /*
26  * Classes to support calibration (e.g. photometric zero points, exposure times)
27  */
28 #include <cmath>
29 #include <cstdint>
30 #include <string>
31 
32 #include "boost/format.hpp"
33 #include "boost/algorithm/string/trim.hpp"
34 
35 #include "ndarray.h"
36 #include "lsst/utils/hashCombine.h"
37 #include "lsst/pex/exceptions.h"
39 #include "lsst/afw/image/Calib.h"
44 #include "lsst/afw/table/io/Persistable.cc"
45 
46 namespace lsst {
47 namespace afw {
48 
51 
52 namespace image {
53 
55 template <typename T>
56 ndarray::Array<T, 1> abMagFromFlux(ndarray::Array<T const, 1> const& flux) {
57  ndarray::Array<T, 1> out = ndarray::allocate(flux.getShape());
58  for (std::size_t ii = 0; ii < flux.getNumElements(); ++ii) {
59  out[ii] = abMagFromFlux(flux[ii]);
60  }
61  return out;
62 }
63 
65 template <typename T>
66 ndarray::Array<T, 1> abMagErrFromFluxErr(ndarray::Array<T const, 1> const& fluxErr,
67  ndarray::Array<T const, 1> const& flux) {
68  if (flux.getNumElements() != fluxErr.getNumElements()) {
69  throw LSST_EXCEPT(pex::exceptions::LengthError, (boost::format("Length mismatch: %d vs %d") %
70  flux.getNumElements() % fluxErr.getNumElements())
71  .str());
72  }
73  ndarray::Array<T, 1> out = ndarray::allocate(flux.getShape());
74  for (std::size_t ii = 0; ii < flux.getNumElements(); ++ii) {
75  out[ii] = abMagErrFromFluxErr(fluxErr[ii], flux[ii]);
76  }
77  return out;
78 }
79 
81 template <typename T>
82 ndarray::Array<T, 1> fluxFromABMag(ndarray::Array<T const, 1> const& mag) {
83  ndarray::Array<T, 1> out = ndarray::allocate(mag.getShape());
84  for (std::size_t ii = 0; ii < mag.getNumElements(); ++ii) {
85  out[ii] = fluxFromABMag(mag[ii]);
86  }
87  return out;
88 }
89 
91 template <typename T>
92 ndarray::Array<T, 1> fluxErrFromABMagErr(ndarray::Array<T const, 1> const& magErr,
93  ndarray::Array<T const, 1> const& mag) {
94  if (mag.getNumElements() != magErr.getNumElements()) {
95  throw LSST_EXCEPT(pex::exceptions::LengthError, (boost::format("Length mismatch: %d vs %d") %
96  mag.getNumElements() % magErr.getNumElements())
97  .str());
98  }
99  ndarray::Array<T, 1> out = ndarray::allocate(mag.getShape());
100  for (std::size_t ii = 0; ii < mag.getNumElements(); ++ii) {
101  out[ii] = fluxErrFromABMagErr(magErr[ii], mag[ii]);
102  }
103  return out;
104 }
105 
106 Calib::Calib() noexcept : _fluxMag0(0.0), _fluxMag0Err(0.0) {}
107 Calib::Calib(double fluxMag0) : _fluxMag0(fluxMag0), _fluxMag0Err(0.0) {}
108 Calib::Calib(std::vector<std::shared_ptr<Calib const>> const& calibs) : _fluxMag0(0.0), _fluxMag0Err(0.0) {
109  if (calibs.empty()) {
111  "You must provide at least one input Calib");
112  }
113 
114  double const fluxMag00 = calibs[0]->_fluxMag0;
115  double const fluxMag0Err0 = calibs[0]->_fluxMag0Err;
116 
117  for (std::vector<std::shared_ptr<Calib const>>::const_iterator ptr = calibs.begin(); ptr != calibs.end();
118  ++ptr) {
119  Calib const& calib = **ptr;
120 
121  if (::fabs(fluxMag00 - calib._fluxMag0) > std::numeric_limits<double>::epsilon() ||
122  ::fabs(fluxMag0Err0 - calib._fluxMag0Err) > std::numeric_limits<double>::epsilon()) {
124  (boost::format("You may only combine calibs with the same fluxMag0: "
125  "%g +- %g v %g +- %g") %
126  calib.getFluxMag0().first % calib.getFluxMag0().second %
127  calibs[0]->getFluxMag0().first % calibs[0]->getFluxMag0().second)
128  .str());
129  }
130  }
131 }
132 
134  double fluxMag0 = 0.0, fluxMag0Err = 0.0;
135 
136  auto key = "FLUXMAG0";
137  if (metadata->exists(key)) {
138  fluxMag0 = metadata->getAsDouble(key);
139 
140  key = "FLUXMAG0ERR";
141  if (metadata->exists(key)) {
142  fluxMag0Err = metadata->getAsDouble(key);
143  }
144  }
145 
146  _fluxMag0 = fluxMag0;
147  _fluxMag0Err = fluxMag0Err;
148 }
149 bool Calib::_throwOnNegativeFlux = true;
150 void Calib::setThrowOnNegativeFlux(bool raiseException) noexcept { _throwOnNegativeFlux = raiseException; }
151 
152 bool Calib::getThrowOnNegativeFlux() noexcept { return _throwOnNegativeFlux; }
153 
154 Calib::Calib(Calib const&) noexcept = default;
155 Calib::Calib(Calib&&) noexcept = default;
156 Calib& Calib::operator=(Calib const&) noexcept = default;
157 Calib& Calib::operator=(Calib&&) noexcept = default;
158 Calib::~Calib() noexcept = default;
159 
160 namespace detail {
162  int nstripped = 0;
163 
164  auto key = "FLUXMAG0";
165  if (metadata->exists(key)) {
166  metadata->remove(key);
167  nstripped++;
168  }
169 
170  key = "FLUXMAG0ERR";
171  if (metadata->exists(key)) {
172  metadata->remove(key);
173  nstripped++;
174  }
175 
176  return nstripped;
177 }
178 } // namespace detail
179 
180 bool Calib::operator==(Calib const& rhs) const noexcept {
181  return _fluxMag0 == rhs._fluxMag0 && _fluxMag0Err == rhs._fluxMag0Err;
182 }
183 
184 std::size_t Calib::hash_value() const noexcept {
185  // Completely arbitrary seed
186  return utils::hashCombine(17, _fluxMag0, _fluxMag0Err);
187 }
188 
190  _fluxMag0 = fluxMag0;
191  _fluxMag0Err = fluxMag0Err;
192 }
194  _fluxMag0 = fluxMag0AndErr.first;
195  _fluxMag0Err = fluxMag0AndErr.second;
196 }
197 
198 std::pair<double, double> Calib::getFluxMag0() const { return std::make_pair(_fluxMag0, _fluxMag0Err); }
199 
200 namespace {
201 
202 inline void checkNegativeFlux0(double fluxMag0) {
203  if (fluxMag0 <= 0) {
205  (boost::format("Flux of 0-mag object must be >= 0: saw %g") % fluxMag0).str());
206  }
207 }
208 inline bool isNegativeFlux(double flux, bool doThrow) {
209  if (flux <= 0) {
210  if (doThrow) {
212  (boost::format("Flux must be >= 0: saw %g") % flux).str());
213  }
214  return true;
215  }
216  return false;
217 }
218 inline double convertToFlux(double fluxMag0, double mag) { return fluxMag0 * ::pow(10.0, -0.4 * mag); }
219 inline double convertToFluxErr(double fluxMag0InvSNR, double flux, double magErr) {
220  // Want to:
221  // return flux * hypot(_fluxMag0Err/_fluxMag0, 0.4*std::log(10)*magSigma/mag);
222  // But hypot is not standard C++ so use <http://en.wikipedia.org/wiki/Hypot#Implementation>
223  double a = fluxMag0InvSNR;
224  double b = 0.4 * std::log(10.0) * magErr;
225  if (std::abs(a) < std::abs(b)) {
226  std::swap(a, b);
227  }
228  return flux * std::abs(a) * std::sqrt(1 + std::pow(b / a, 2));
229 }
230 inline double convertToMag(double fluxMag0, double flux) { return -2.5 * ::log10(flux / fluxMag0); }
231 
232 inline void convertToMagWithErr(double* mag, double* magErr, double fluxMag0, double fluxMag0Err, double flux,
233  double fluxErr) {
234  *mag = -2.5 * std::log10(flux / fluxMag0);
235  double const x = fluxErr / flux;
236  double const y = fluxMag0Err / fluxMag0;
237  *magErr = (2.5 / std::log(10.0)) * std::sqrt(x * x + y * y);
238 }
239 
240 } // anonymous namespace
241 
242 double Calib::getFlux(double const mag) const {
243  checkNegativeFlux0(_fluxMag0);
244  return convertToFlux(_fluxMag0, mag);
245 }
246 ndarray::Array<double, 1> Calib::getFlux(ndarray::Array<double const, 1> const& mag) const {
247  checkNegativeFlux0(_fluxMag0);
248  ndarray::Array<double, 1> flux = ndarray::allocate(mag.size());
249  ndarray::Array<double const, 1>::Iterator inIter = mag.begin();
250  ndarray::Array<double, 1>::Iterator outIter = flux.begin();
251  for (; inIter != mag.end(); ++inIter, ++outIter) {
252  *outIter = convertToFlux(_fluxMag0, *inIter);
253  }
254  return flux;
255 }
256 
257 std::pair<double, double> Calib::getFlux(double const mag, double const magSigma) const {
258  checkNegativeFlux0(_fluxMag0);
259  double const flux = convertToFlux(_fluxMag0, mag);
260  double const fluxErr = convertToFluxErr(_fluxMag0Err / _fluxMag0, flux, magSigma);
261  return std::make_pair(flux, fluxErr);
262 }
263 
265  ndarray::Array<double const, 1> const& mag, ndarray::Array<double const, 1> const& magErr) const {
266  checkNegativeFlux0(_fluxMag0);
267  if (mag.size() != magErr.size()) {
268  throw LSST_EXCEPT(
270  (boost::format("Size of mag (%d) and magErr (%d) don't match") % mag.size() % magErr.size())
271  .str());
272  }
273 
274  ndarray::Array<double, 1> flux = ndarray::allocate(mag.size());
275  ndarray::Array<double, 1> fluxErr = ndarray::allocate(mag.size());
276  ndarray::Array<double const, 1>::Iterator magIter = mag.begin();
277  ndarray::Array<double const, 1>::Iterator magErrIter = magErr.begin();
278  ndarray::Array<double, 1>::Iterator fluxIter = flux.begin();
279  ndarray::Array<double, 1>::Iterator fluxErrIter = fluxErr.begin();
280 
281  double fluxMag0InvSNR = _fluxMag0Err / _fluxMag0;
282  for (; magIter != mag.end(); ++magIter, ++magErrIter, ++fluxIter, ++fluxErrIter) {
283  *fluxIter = convertToFlux(_fluxMag0, *magIter);
284  *fluxErrIter = convertToFluxErr(fluxMag0InvSNR, *fluxIter, *magErrIter);
285  }
286 
287  return std::make_pair(flux, fluxErr);
288 }
289 
290 double Calib::getMagnitude(double const flux) const {
291  checkNegativeFlux0(_fluxMag0);
292  if (isNegativeFlux(flux, Calib::getThrowOnNegativeFlux())) {
294  }
295  return convertToMag(_fluxMag0, flux);
296 }
297 
298 std::pair<double, double> Calib::getMagnitude(double const flux, double const fluxErr) const {
299  checkNegativeFlux0(_fluxMag0);
300  if (isNegativeFlux(flux, Calib::getThrowOnNegativeFlux())) {
302  return std::make_pair(NaN, NaN);
303  }
304 
305  double mag, magErr;
306  convertToMagWithErr(&mag, &magErr, _fluxMag0, _fluxMag0Err, flux, fluxErr);
307  return std::make_pair(mag, magErr);
308 }
309 
310 ndarray::Array<double, 1> Calib::getMagnitude(ndarray::Array<double const, 1> const& flux) const {
311  checkNegativeFlux0(_fluxMag0);
312  ndarray::Array<double, 1> mag = ndarray::allocate(flux.size());
313  ndarray::Array<double const, 1>::Iterator fluxIter = flux.begin();
314  ndarray::Array<double, 1>::Iterator magIter = mag.begin();
315  int nonPositive = 0;
316  for (; fluxIter != flux.end(); ++fluxIter, ++magIter) {
317  if (isNegativeFlux(*fluxIter, false)) {
318  ++nonPositive;
320  continue;
321  }
322  *magIter = convertToMag(_fluxMag0, *fluxIter);
323  }
324  if (nonPositive && Calib::getThrowOnNegativeFlux()) {
326  (boost::format("Flux must be >= 0: %d non-positive seen") % nonPositive).str());
327  }
328  return mag;
329 }
330 
332  ndarray::Array<double const, 1> const& flux, ndarray::Array<double const, 1> const& fluxErr) const {
333  checkNegativeFlux0(_fluxMag0);
334  if (flux.size() != fluxErr.size()) {
336  (boost::format("Size of flux (%d) and fluxErr (%d) don't match") % flux.size() %
337  fluxErr.size())
338  .str());
339  }
340 
341  ndarray::Array<double, 1> mag = ndarray::allocate(flux.size());
342  ndarray::Array<double, 1> magErr = ndarray::allocate(flux.size());
343  ndarray::Array<double const, 1>::Iterator fluxIter = flux.begin();
344  ndarray::Array<double const, 1>::Iterator fluxErrIter = fluxErr.begin();
345  ndarray::Array<double, 1>::Iterator magIter = mag.begin();
346  ndarray::Array<double, 1>::Iterator magErrIter = magErr.begin();
347  int nonPositive = 0;
348  for (; fluxIter != flux.end(); ++fluxIter, ++fluxErrIter, ++magIter, ++magErrIter) {
349  if (isNegativeFlux(*fluxIter, false)) {
350  ++nonPositive;
352  *magIter = NaN;
353  *magErrIter = NaN;
354  continue;
355  }
356  double f, df;
357  convertToMagWithErr(&f, &df, _fluxMag0, _fluxMag0Err, *fluxIter, *fluxErrIter);
358  *magIter = f;
359  *magErrIter = df;
360  }
361  if (nonPositive && Calib::getThrowOnNegativeFlux()) {
363  (boost::format("Flux must be >= 0: %d non-positive seen") % nonPositive).str());
364  }
365  return std::make_pair(mag, magErr);
366 }
367 
368 namespace {
369 
370 int const CALIB_TABLE_CURRENT_VERSION = 2; // current version of ExposureTable
371 std::string const EXPTIME_FIELD_NAME = "exptime"; // name of exposure time field
372 
373 class CalibKeys {
374 public:
375  table::Schema schema;
376  table::Key<std::int64_t> midTime;
377  table::Key<double> expTime;
378  table::Key<double> fluxMag0;
379  table::Key<double> fluxMag0Err;
380 
381  // No copying
382  CalibKeys(const CalibKeys&) = delete;
383  CalibKeys& operator=(const CalibKeys&) = delete;
384 
385  // No moving
386  CalibKeys(CalibKeys&&) = delete;
387  CalibKeys& operator=(CalibKeys&&) = delete;
388 
389  CalibKeys(int tableVersion = CALIB_TABLE_CURRENT_VERSION)
390  : schema(), midTime(), expTime(), fluxMag0(), fluxMag0Err() {
391  if (tableVersion == 1) {
392  // obsolete fields
393  midTime = schema.addField<std::int64_t>(
394  "midtime", "middle of the time of the exposure relative to Unix epoch", "ns");
395  expTime = schema.addField<double>(EXPTIME_FIELD_NAME, "exposure time", "s");
396  }
397  fluxMag0 = schema.addField<double>("fluxmag0", "flux of a zero-magnitude object", "count");
398  fluxMag0Err = schema.addField<double>("fluxmag0.err", "1-sigma error on fluxmag0", "count");
399  }
400 };
401 
402 class CalibFactory : public table::io::PersistableFactory {
403 public:
404  std::shared_ptr<table::io::Persistable> read(InputArchive const& archive,
405  CatalogVector const& catalogs) const override {
406  // table version is not persisted, so we don't have a clean way to determine the version;
407  // the hack is version = 1 if exptime found, else current
408  int tableVersion = 1;
409  try {
410  catalogs.front().getSchema().find<double>(EXPTIME_FIELD_NAME);
411  } catch (pex::exceptions::NotFoundError) {
412  tableVersion = CALIB_TABLE_CURRENT_VERSION;
413  }
414 
415  CalibKeys const keys{tableVersion};
416  LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
417  LSST_ARCHIVE_ASSERT(catalogs.front().size() == 1u);
418  LSST_ARCHIVE_ASSERT(catalogs.front().getSchema() == keys.schema);
419  table::BaseRecord const& record = catalogs.front().front();
421  result->setFluxMag0(record.get(keys.fluxMag0), record.get(keys.fluxMag0Err));
422  return result;
423  }
424 
425  explicit CalibFactory(std::string const& name) : table::io::PersistableFactory(name) {}
426 };
427 
428 std::string getCalibPersistenceName() { return "Calib"; }
429 
430 CalibFactory registration(getCalibPersistenceName());
431 
432 } // namespace
433 
434 std::string Calib::getPersistenceName() const { return getCalibPersistenceName(); }
435 
436 void Calib::write(OutputArchiveHandle& handle) const {
437  CalibKeys const keys{};
438  table::BaseCatalog cat = handle.makeCatalog(keys.schema);
441  record->set(keys.fluxMag0, fluxMag0.first);
442  record->set(keys.fluxMag0Err, fluxMag0.second);
443  handle.saveCatalog(cat);
444 }
445 
446 Calib& Calib::operator*=(double const scale) {
447  _fluxMag0 *= scale;
448  _fluxMag0Err *= scale;
449  return *this;
450 }
451 
452 // Explicit instantiation
453 #define INSTANTIATE(TYPE) \
454  template ndarray::Array<TYPE, 1> abMagFromFlux(ndarray::Array<TYPE const, 1> const& flux); \
455  template ndarray::Array<TYPE, 1> abMagErrFromFluxErr(ndarray::Array<TYPE const, 1> const& fluxErr, \
456  ndarray::Array<TYPE const, 1> const& flux); \
457  template ndarray::Array<TYPE, 1> fluxFromABMag(ndarray::Array<TYPE const, 1> const& mag); \
458  template ndarray::Array<TYPE, 1> fluxErrFromABMagErr(ndarray::Array<TYPE const, 1> const& magErr, \
459  ndarray::Array<TYPE const, 1> const& mag);
460 
461 INSTANTIATE(float);
462 INSTANTIATE(double);
463 
464 } // namespace image
465 } // namespace afw
466 } // namespace lsst
table::Schema schema
Definition: Calib.cc:375
Angle abs(Angle const &a)
Definition: Angle.h:106
uint64_t * ptr
Definition: RangeSet.cc:88
static std::shared_ptr< T > dynamicCast(std::shared_ptr< Persistable > const &ptr)
Dynamically cast a shared_ptr.
Definition: Persistable.cc:18
std::size_t hash_value() const noexcept
Return a hash of this object.
Definition: Calib.cc:184
template ndarray::Array< double, 1 > abMagErrFromFluxErr(ndarray::Array< double const, 1 > const &fluxErr, ndarray::Array< double const, 1 > const &flux)
Calib() noexcept
ctor
Definition: Calib.cc:106
An object passed to Persistable::write to allow it to persist itself.
T swap(T... args)
T epsilon(T... args)
T log(T... args)
static void setThrowOnNegativeFlux(bool raiseException) noexcept
Set whether Calib should throw an exception when asked to convert a flux to a magnitude.
Definition: Calib.cc:150
table::Key< int > b
Reports attempts to exceed implementation-defined length limits for some classes. ...
Definition: Runtime.h:76
def scale(algorithm, min, max=None, frame=None)
Definition: ds9.py:109
double getFlux(double const mag) const
Return a flux (in ADUs) given a magnitude.
Definition: Calib.cc:242
template ndarray::Array< double, 1 > fluxFromABMag(ndarray::Array< double const, 1 > const &mag)
py::object result
Definition: schema.cc:284
int y
Definition: SpanSet.cc:49
Calib & operator=(Calib const &) noexcept
table::Key< int > a
Reports arguments outside the domain of an operation.
Definition: Runtime.h:57
static bool getThrowOnNegativeFlux() noexcept
Tell me whether Calib will throw an exception if asked to convert a flux to a magnitude.
Definition: Calib.cc:152
T log10(T... args)
Describe an exposure&#39;s calibration.
Definition: Calib.h:95
Calib & operator*=(double const scale)
Definition: Calib.cc:446
template ndarray::Array< double, 1 > abMagFromFlux(ndarray::Array< double const, 1 > const &flux)
STL class.
#define INSTANTIATE(TYPE)
Definition: Calib.cc:453
FastFinder::Iterator Iterator
Definition: FastFinder.cc:179
A base class for image defects.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:168
std::string getPersistenceName() const override
Return the unique name used to persist this object and look up its factory.
Definition: Calib.cc:434
T make_pair(T... args)
void setFluxMag0(double fluxMag0, double fluxMag0Err=0.0)
Set the flux of a zero-magnitude object.
Definition: Calib.cc:189
int stripCalibKeywords(std::shared_ptr< lsst::daf::base::PropertySet > metadata)
Remove Calib-related keywords from the metadata.
Definition: Calib.cc:161
template ndarray::Array< double, 1 > fluxErrFromABMagErr(ndarray::Array< double const, 1 > const &magErr, ndarray::Array< double const, 1 > const &mag)
double x
double getMagnitude(double const flux) const
Return a magnitude given a flux.
Definition: Calib.cc:290
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
table::Key< double > expTime
Definition: Calib.cc:377
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
STL class.
~Calib() noexcept override
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
Definition: Persistable.h:48
Key< U > key
Definition: Schema.cc:281
T pow(T... args)
table::Key< std::int64_t > midTime
Definition: Calib.cc:376
Reports invalid arguments.
Definition: Runtime.h:66
table::Key< double > fluxMag0
Definition: Calib.cc:378
std::pair< double, double > getFluxMag0() const
Return the flux, and error in flux, of a zero-magnitude object.
Definition: Calib.cc:198
T quiet_NaN(T... args)
T sqrt(T... args)
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
std::size_t hashCombine(std::size_t seed) noexcept
Combine hashes.
Definition: hashCombine.h:35
bool operator==(Calib const &rhs) const noexcept
Are two Calibs identical?
Definition: Calib.cc:180
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
Definition: Calib.cc:436
table::Key< double > fluxMag0Err
Definition: Calib.cc:379
std::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:472