LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
SourceCluster.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 /*
3  * LSST Data Management System
4  * Copyright 2012 LSST Corporation.
5  *
6  * This product includes software developed by the
7  * LSST Project (http://www.lsst.org/).
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the LSST License Statement and
20  * the GNU General Public License along with this program. If not,
21  * see <http://www.lsstcorp.org/LegalNotices/>.
22  */
23 
32 
33 #include <boost/algorithm/string/case_conv.hpp>
34 
35 
36 // boilerplate macros for saving/loading slot definitionsa
37 
38 #define SAVE_SLOT(NAME, Name) \
39  if (table->get ## Name ## Key().isValid()) { \
40  std::string s = table->getSchema().find(table->get ## Name ## Key()).field.getName(); \
41  std::replace(s.begin(), s.end(), '.', '_'); \
42  _fits->writeKey(#NAME "_SLOT", s.c_str(), "Defines the " #Name " slot"); \
43  }
44 
45 #define SAVE_FILTER_SLOT(FILTER, filter, NAME, Name) \
46  if (table->get ## Name ## Key(filter).isValid()) { \
47  std::string s = table->getSchema().find(table->get ## Name ## Key(filter)).field.getName(); \
48  std::replace(s.begin(), s.end(), '.', '_'); \
49  _fits->writeKey(FILTER + "_" #NAME "_SLOT", s.c_str(), \
50  "Defines the " #Name " slot in the " + filter + " filter"); \
51  }
52 
53 #define SAVE_COMPOUND_SLOT(FILTER, filter, NAME, Name) \
54  SAVE_FILTER_SLOT(FILTER, filter, NAME, Name) \
55  SAVE_FILTER_SLOT(FILTER, filter, NAME ## _ERR, Name ## Err) \
56  SAVE_FILTER_SLOT(FILTER, filter, NAME ## _COUNT, Name ## Count)
57 
58 #define LOAD_SLOT(NAME, Name) \
59  { \
60  _fits->behavior &= ~lsst::afw::fits::Fits::AUTO_CHECK; \
61  std::string s; \
62  _fits->readKey(#NAME "_SLOT", s); \
63  if (_fits->status == 0) { \
64  metadata->remove(#NAME "_SLOT"); \
65  std::replace(s.begin(), s.end(), '_', '.'); \
66  table->define ## Name (schema[s]); \
67  } else { \
68  _fits->status = 0; \
69  } \
70  _fits->behavior |= lsst::afw::fits::Fits::AUTO_CHECK; \
71  }
72 
73 #define LOAD_FILTER_SLOT(FILTER, filter, NAME, Name) \
74  { \
75  _fits->behavior &= ~lsst::afw::fits::Fits::AUTO_CHECK; \
76  std::string s; \
77  _fits->readKey(FILTER + "_" #NAME "_SLOT", s); \
78  if (_fits->status == 0) { \
79  metadata->remove(FILTER + "_" #NAME "_SLOT"); \
80  std::replace(s.begin(), s.end(), '_', '.'); \
81  table->define ## Name (filter, schema[s]); \
82  } else { \
83  _fits->status = 0; \
84  } \
85  _fits->behavior |= lsst::afw::fits::Fits::AUTO_CHECK; \
86  }
87 
88 #define LOAD_COMPOUND_SLOT(FILTER, filter, NAME, Name) \
89  { \
90  _fits->behavior &= ~lsst::afw::fits::Fits::AUTO_CHECK; \
91  std::string s, sErr, sCount; \
92  _fits->readKey(FILTER + "_" #NAME "_SLOT", s); \
93  _fits->readKey(FILTER + "_" #NAME "_ERR_SLOT", sErr); \
94  _fits->readKey(FILTER + "_" #NAME "_COUNT_SLOT", sCount); \
95  if (_fits->status == 0) { \
96  metadata->remove(FILTER + "_" #NAME "_SLOT"); \
97  metadata->remove(FILTER + "_" #NAME "_ERR_SLOT"); \
98  metadata->remove(FILTER + "_" #NAME "_COUNT_SLOT"); \
99  std::replace(s.begin(), s.end(), '_', '.'); \
100  std::replace(sErr.begin(), sErr.end(), '_', '.'); \
101  std::replace(sCount.begin(), sCount.end(), '_', '.'); \
102  table->define ## Name (filter, schema[s], schema[sErr], schema[sCount]); \
103  } else { \
104  _fits->status = 0; \
105  } \
106  _fits->behavior |= lsst::afw::fits::Fits::AUTO_CHECK; \
107  }
108 
109 
110 namespace except = lsst::pex::exceptions;
111 
116 
117 
118 namespace lsst { namespace ap { namespace cluster {
119 
120 // -- SourceClusterRecordImpl and SourceClusterTableImpl --------
121 //
122 // These are a private table/record pair -- they're what you actually get when
123 // you do SourceClusterTable::make(), but are hidden to avoid the friending that
124 // would be necessary if they had to make their constructors private or protected.
125 
126 namespace {
127 
128  class SourceClusterTableImpl;
129 
130  class SourceClusterRecordImpl : public SourceClusterRecord {
131  public:
132  explicit SourceClusterRecordImpl(PTR(SourceClusterTable) const & table) :
133  SourceClusterRecord(table) { }
134  };
135 
136  class SourceClusterTableImpl : public SourceClusterTable {
137  public:
138  explicit SourceClusterTableImpl(
140  PTR(lsst::afw::table::IdFactory) const & idFactory
141  ) : SourceClusterTable(schema, idFactory) { }
142 
143  SourceClusterTableImpl(SourceClusterTableImpl const & other) :
144  SourceClusterTable(other) { }
145 
146  private:
147  virtual PTR(BaseTable) _clone() const {
148  return boost::make_shared<SourceClusterTableImpl>(*this);
149  }
150 
151  virtual PTR(lsst::afw::table::BaseRecord) _makeRecord() {
152  PTR(SourceClusterRecord) record =
153  boost::make_shared<SourceClusterRecordImpl>(
154  getSelf<SourceClusterTableImpl>());
155  if (getIdFactory()) {
156  record->setId((*getIdFactory())());
157  }
158  return record;
159  }
160  };
161 
162 } // namespace <anonymous>
163 
164 
165 // -- SourceClusterFitsWriter --------
166 
167 namespace {
168 
169  // A custom FitsWriter for source cluster tables - this adds header keys that
170  // define the slots. It also sets the AFW_TYPE key to SOURCE_CLUSTER, which
171  // should ensure that SourceClusterFitsReader is used to read it.
172  class SourceClusterFitsWriter : public FitsWriter {
173  public:
174  explicit SourceClusterFitsWriter(lsst::afw::fits::Fits * fits, int flags) :
175  FitsWriter(fits, flags) { }
176  protected:
177  virtual void _writeTable(CONST_PTR(BaseTable) const & table, size_t nRows);
178  };
179 
180  void SourceClusterFitsWriter::_writeTable(CONST_PTR(BaseTable) const & t, size_t nRows) {
181  CONST_PTR(SourceClusterTable) table =
182  boost::dynamic_pointer_cast<SourceClusterTable const>(t);
183  if (!table) {
184  throw LSST_EXCEPT(except::LogicError, "SourceClusterFitsWriter "
185  "can only write out SourceClusterTable instances!");
186  }
187  PTR(PropertyList) metadata = table->getMetadata();
188  // exploit hole in constness of table and remove the FILTERS key if present
189  if (metadata && metadata->exists("FILTERS")) {
190  metadata->remove("FILTERS");
191  }
192  FitsWriter::_writeTable(table, nRows);
193  metadata = boost::make_shared<PropertyList>();
194  std::vector<std::string> const filters = table->getFilters();
195  if (!filters.empty()) {
196  metadata->set<std::string>("FILTERS", filters);
197  _fits->writeMetadata(*metadata);
198  }
199  _fits->writeKey("AFW_TYPE", "SOURCE_CLUSTER",
200  "Tells lsst::afw to load this as a SourceClusterTable.");
201  // save filter agnostic slots
202  SAVE_SLOT(COORD_ERR, CoordErr)
203  SAVE_SLOT(WMCOORD, WeightedMeanCoord)
204  SAVE_SLOT(WMCOORD_ERR, WeightedMeanCoordErr)
205  SAVE_SLOT(WMCOORD_COUNT, WeightedMeanCoordCount)
206  SAVE_SLOT(NUM_SOURCES, NumSources)
207  SAVE_SLOT(TIME_MIN, TimeMin)
208  SAVE_SLOT(TIME_MEAN, TimeMean)
209  SAVE_SLOT(TIME_MAX, TimeMax)
210  // save filters and filter-specific slots
211  typedef std::vector<std::string>::const_iterator Iter;
212  for (Iter i = filters.begin(), e = filters.end(); i != e; ++i) {
213  std::string const f = *i;
214  std::string const F = boost::to_upper_copy(f);
215  SAVE_FILTER_SLOT(F, f, NUM_SOURCES, NumSources)
216  SAVE_FILTER_SLOT(F, f, TIME_MIN, TimeMin)
217  SAVE_FILTER_SLOT(F, f, TIME_MAX, TimeMax)
218  SAVE_COMPOUND_SLOT(F, f, PSF_FLUX, PsfFlux)
219  SAVE_COMPOUND_SLOT(F, f, MODEL_FLUX, ModelFlux)
220  SAVE_COMPOUND_SLOT(F, f, AP_FLUX, ApFlux)
221  SAVE_COMPOUND_SLOT(F, f, INST_FLUX, InstFlux)
222  SAVE_COMPOUND_SLOT(F, f, SHAPE, Shape)
223  }
224  }
225 
226 } // namespace <anonymous>
227 
228 
229 // -- SourceClusterFitsReader --------
230 
231 namespace {
232 
233  // A custom FitsReader for source cluster tables - this reads header keys
234  // defining slots. It is registered with the name SOURCE_CLUSTER, so will
235  // be used whenever a table with AFW_TYPE set to that value is read.
236  class SourceClusterFitsReader : public FitsReader {
237  public:
238  explicit SourceClusterFitsReader(
239  lsst::afw::fits::Fits * fits,
240  boost::shared_ptr<lsst::afw::table::io::InputArchive> archive,
241  int flags
242  ) : FitsReader(fits, archive, flags) { }
243  protected:
244  virtual PTR(BaseTable) _readTable();
245  };
246 
247  PTR(BaseTable) SourceClusterFitsReader::_readTable() {
248  PTR(PropertyList) metadata = boost::make_shared<PropertyList>();
249  _fits->readMetadata(*metadata, true);
250  if (metadata->exists("AFW_TYPE")) {
251  metadata->remove("AFW_TYPE");
252  }
253  lsst::afw::table::Schema schema(*metadata, true);
254  PTR(SourceClusterTable) table = SourceClusterTable::make(
255  schema, PTR(lsst::afw::table::IdFactory)());
256  // read in filter agnostic slots
257  LOAD_SLOT(COORD_ERR, CoordErr)
258  LOAD_SLOT(WMCOORD, WeightedMeanCoord)
259  LOAD_SLOT(WMCOORD_ERR, WeightedMeanCoordErr)
260  LOAD_SLOT(WMCOORD_COUNT, WeightedMeanCoordCount)
261  LOAD_SLOT(NUM_SOURCES, NumSources)
262  LOAD_SLOT(TIME_MIN, TimeMin)
263  LOAD_SLOT(TIME_MEAN, TimeMean)
264  LOAD_SLOT(TIME_MAX, TimeMax)
265  // read in filter specific slots
266  std::vector<std::string> filters;
267  if (metadata->exists("FILTERS")) {
268  filters = metadata->getArray<std::string>("FILTERS");
269  metadata->remove("FILTERS");
270  }
271  typedef std::vector<std::string>::const_iterator Iter;
272  for (Iter i = filters.begin(), e = filters.end(); i != e; ++i) {
273  std::string const f = *i;
274  std::string const F = boost::to_upper_copy(f);
275  LOAD_FILTER_SLOT(F, f, NUM_SOURCES, NumSources)
276  LOAD_FILTER_SLOT(F, f, TIME_MIN, TimeMin)
277  LOAD_FILTER_SLOT(F, f, TIME_MAX, TimeMax)
278  LOAD_COMPOUND_SLOT(F, f, PSF_FLUX, PsfFlux)
279  LOAD_COMPOUND_SLOT(F, f, MODEL_FLUX, ModelFlux)
280  LOAD_COMPOUND_SLOT(F, f, AP_FLUX, ApFlux)
281  LOAD_COMPOUND_SLOT(F, f, INST_FLUX, InstFlux)
282  LOAD_COMPOUND_SLOT(F, f, SHAPE, Shape)
283  }
284  _startRecords(*table);
285  table->setMetadata(metadata);
286  return table;
287  }
288 
289  // registers the reader so FitsReader::make can use it.
290  static FitsReader::FactoryT<SourceClusterFitsReader> sourceClusterFitsReaderFactory("SOURCE_CLUSTER");
291 
292 } // namespace <anonymous>
293 
294 
295 // -- SourceClusterRecord implementation --------
296 
298  lsst::afw::table::SimpleRecord(table) { }
299 
300 
301 // -- SourceClusterTable implementation --------
302 
304  lsst::afw::table::Schema const & schema,
305  PTR(lsst::afw::table::IdFactory) const & idFactory)
306 {
307  if (!checkSchema(schema)) {
308  throw LSST_EXCEPT(except::InvalidParameterError,
309  "Schema for SourceClusterTable must contain at least the keys "
310  "defined by getMinimalSchema().");
311  }
312  return boost::make_shared<SourceClusterTableImpl>(schema, idFactory);
313 }
314 
317  PTR(lsst::afw::table::IdFactory) const & idFactory
318 ) : lsst::afw::table::SimpleTable(schema, idFactory),
319  _keyCoordErr(),
320  _keyWeightedMeanCoord(),
321  _keyWeightedMeanCoordErr(),
322  _keyWeightedMeanCoordCount(),
323  _keyNumSources(),
324  _keyTimeMin(),
325  _keyTimeMean(),
326  _keyTimeMax(),
327  _filterSlots()
328 { }
329 
331  lsst::afw::table::SimpleTable(other),
332  _keyCoordErr(other._keyCoordErr),
333  _keyWeightedMeanCoord(other._keyWeightedMeanCoord),
334  _keyWeightedMeanCoordErr(other._keyWeightedMeanCoordErr),
335  _keyWeightedMeanCoordCount(other._keyWeightedMeanCoordCount),
336  _keyNumSources(other._keyNumSources),
337  _keyTimeMin(other._keyTimeMin),
338  _keyTimeMean(other._keyTimeMean),
339  _keyTimeMax(other._keyTimeMax),
340  _filterSlots(other._filterSlots)
341 { }
342 
344 
345 std::vector<std::string> const SourceClusterTable::getFilters() const {
346  typedef FilterSlotsMap::const_iterator Iter;
347  std::vector<std::string> filters;
348  for (Iter i = _filterSlots.begin(), e = _filterSlots.end(); i != e; ++i) {
349  filters.push_back(i->first);
350  }
351  std::sort(filters.begin(), filters.end());
352  return filters;
353 }
354 
356  lsst::afw::table::io::FitsWriter::Fits * fits, int flags) const
357 {
358  return boost::make_shared<SourceClusterFitsWriter>(fits, flags);
359 }
360 
361 SourceClusterTable::FilterSlots const & SourceClusterTable::getFilterSlots(std::string const & filter) const {
362  FilterSlotsMap::const_iterator i = _filterSlots.find(filter);
363  if (i == _filterSlots.end()) {
364  throw LSST_EXCEPT(except::NotFoundError, "SourceClusterTable "
365  "contains no slot mappings for the filter named " + filter);
366  }
367  return i->second;
368 }
369 
371  keyTimeMin(),
372  keyTimeMax(),
373  keyNumSources(),
374  keyPsfFlux(),
375  keyModelFlux(),
376  keyApFlux(),
377  keyInstFlux(),
378  keyShape()
379 { }
380 
382 
383 
384 // -- SourceClusterIdFactory implementation --------
385 
387  _id(static_cast<lsst::afw::table::RecordId>(skyTileId) << 32),
388  _skyTileId(skyTileId)
389 { }
390 
392 
395  if (static_cast<int>(id >> 32) != _skyTileId) {
396  throw LSST_EXCEPT(except::OverflowError,
397  "Source cluster ID space exhausted! Note that SourceClusterIdFactory "
398  "can hand out a maximum of 2^32 - 1 IDs for a given sky-tile. If there "
399  "are more than that many clusters, the sky-tile size must be reduced.");
400  }
401  _id = id;
402  return id;
403 }
404 
406  throw LSST_EXCEPT(except::LogicError,
407  "SourceClusterIdFactory does not support the notify() method "
408  "of lsst::afw::table::IdFactory");
409 }
410 
411 
412 // -- UUtility function implementations --------
413 
415  lsst::afw::table::Schema & schema,
416  std::string const & filter,
417  std::string const & name,
418  std::string const & doc)
419 {
421  KeyTuple<Shape> kt;
422  kt.mean = schema.addField<Shape::MeasTag>(
423  filter + "." + name, doc, "rad^2");
424  kt.err = schema.addField<Shape::ErrTag>(
425  filter + "." + name + ".err",
426  "covariance matrix for " + filter + "." + name,
427  "rad^4");
428  kt.count = schema.addField<int>(
429  filter + "." + name + ".count",
430  "Number of samples used to compute the " + filter +
431  "." + name + " mean");
432  return kt;
433 }
434 
436  lsst::afw::table::Schema & schema,
437  std::string const & filter,
438  std::string const & name,
439  std::string const & doc,
440  std::string const & unit)
441 {
443  KeyTuple<Flux> kt;
444  kt.mean = schema.addField<Flux::MeasTag>(
445  filter + "." + name, doc, unit);
446  kt.err = schema.addField<Flux::ErrTag>(
447  filter + "." + name + ".err",
448  "uncertainty for " + filter + "." + name,
449  unit);
450  kt.count = schema.addField<int>(
451  filter + "." + name + ".count",
452  "Number of samples used to compute the " + filter +
453  "." + name + " mean");
454  return kt;
455 }
456 
457 }}} // namespace lsst::ap::cluster
458 
459 // -- Explicit instantiations
460 
465 
Defines the fields and offsets for a table.
Definition: Schema.h:46
#define CONST_PTR(...)
Definition: base.h:47
Writer subclass for FITS binary tables.
Definition: FitsWriter.h:20
A Reader subclass for FITS binary tables.
Definition: FitsReader.h:27
int64_t _id
virtual lsst::afw::table::RecordId operator()()
Return a new unique RecordId.
A custom container class for records, based on std::vector.
Definition: Catalog.h:94
Class for storing ordered metadata with comments.
Definition: PropertyList.h:81
std::vector< SourceCatalog > const cluster(SourceCatalog const &sources, ClusteringControl const &control)
Definition: clustering.cc:578
#define PTR(...)
Definition: base.h:41
lsst::daf::base::PropertyList PropertyList
Definition: Wcs.cc:58
A simple struct that combines the two arguments that must be passed to most cfitsio routines and cont...
Definition: fits.h:194
#define SAVE_FILTER_SLOT(FILTER, filter, NAME, Name)
MeasurementT::MeasKey mean
Key used for the mean measured value.
Definition: SourceCluster.h:58
Table class that contains measurement means on clusters of single exposure sources.
afw::geom::ellipses::Quadrupole Shape
Definition: constants.h:58
Table and record classes for source cluster attributes.
virtual void notify(lsst::afw::table::RecordId id)
Notify the IdFactory that the given ID has been used and must not be returned by operator().
FilterSlots const & getFilterSlots(std::string const &filter) const
tbl::Schema schema
Definition: CoaddPsf.cc:324
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Definition: fwd.h:55
#define SAVE_COMPOUND_SLOT(FILTER, filter, NAME, Name)
SourceClusterTable(lsst::afw::table::Schema const &schema, boost::shared_ptr< lsst::afw::table::IdFactory > const &idFactory)
#define SAVE_SLOT(NAME, Name)
static boost::shared_ptr< SourceClusterTable > make(lsst::afw::table::Schema const &schema, boost::shared_ptr< lsst::afw::table::IdFactory > const &idFactory)
Construct a new table.
#define LOAD_SLOT(NAME, Name)
A polymorphic functor base class for generating record IDs for a table.
Definition: IdFactory.h:19
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
Base class for all records.
Definition: BaseRecord.h:27
std::vector< std::string > const getFilters() const
Get the lexicographically sorted list of filter names for which slots have been defined.
lsst::afw::table::Key< int > count
Key used for the sample count.
Definition: SourceCluster.h:60
int id
Definition: CR.cc:151
MeasurementT::ErrKey err
Key used for the uncertainty.
Definition: SourceCluster.h:59
#define LOAD_FILTER_SLOT(FILTER, filter, NAME, Name)
SourceClusterRecord(boost::shared_ptr< SourceClusterTable > const &table)
boost::int64_t RecordId
Type used for unique IDs for records.
Definition: misc.h:21
std::map< Citizen const *, CitizenInfo > table
Definition: Citizen.h:93
#define LOAD_COMPOUND_SLOT(FILTER, filter, NAME, Name)
Base class for all tables.
Definition: BaseTable.h:44
KeyTuple< lsst::afw::table::Shape > addShapeFields(lsst::afw::table::Schema &schema, std::string const &filter, std::string const &name, std::string const &doc)
Convenience function to setup fields for shapes.
KeyTuple< lsst::afw::table::Flux > addFluxFields(lsst::afw::table::Schema &schema, std::string const &filter, std::string const &name, std::string const &doc, std::string const &unit)
Convenience function to setup fields for fluxes.