LSSTApplications  8.0.0.0+107,8.0.0.1+13,9.1+18,9.2,master-g084aeec0a4,master-g0aced2eed8+6,master-g15627eb03c,master-g28afc54ef9,master-g3391ba5ea0,master-g3d0fb8ae5f,master-g4432ae2e89+36,master-g5c3c32f3ec+17,master-g60f1e072bb+1,master-g6a3ac32d1b,master-g76a88a4307+1,master-g7bce1f4e06+57,master-g8ff4092549+31,master-g98e65bf68e,master-ga6b77976b1+53,master-gae20e2b580+3,master-gb584cd3397+53,master-gc5448b162b+1,master-gc54cf9771d,master-gc69578ece6+1,master-gcbf758c456+22,master-gcec1da163f+63,master-gcf15f11bcc,master-gd167108223,master-gf44c96c709
LSSTDataManagementBasePackage
utils.cc
Go to the documentation of this file.
1 // -*- lsst-C++ -*-
2 // Astrometry.net include files:
3 extern "C" {
4 #include "astrometry/solver.h"
5 #include "astrometry/index.h"
6 #include "astrometry/starkd.h"
7 #include "astrometry/fitsioutils.h"
8 #include "astrometry/fitstable.h"
9 
10 #undef ATTRIB_FORMAT
11 #undef FALSE
12 #undef TRUE
13 }
14 
15 #include <set>
16 #include "boost/cstdint.hpp"
17 #include "boost/format.hpp"
18 
20 #include "lsst/base.h"
21 #include "lsst/pex/exceptions.h"
22 #include "lsst/afw/table/Source.h"
23 #pragma clang diagnostic push
24 #pragma clang diagnostic ignored "-Wunused-variable"
25 #include "lsst/afw/geom/Angle.h"
26 #pragma clang diagnostic pop
27 
28 namespace afwCoord = lsst::afw::coord;
29 namespace afwTable = lsst::afw::table;
30 namespace afwGeom = lsst::afw::geom;
31 
32 namespace lsst {
33 namespace meas {
34 namespace astrom {
35 namespace detail {
36 
37 static float *
38 read_column(fitstable_t* tag,
39  char const *colName,
40  tfits_type type,
41  int *starinds,
42  int nstars,
43  char const *indexName)
44 {
45  float *col =
46  static_cast<float*>(fitstable_read_column_inds(tag, colName, type, starinds, nstars));
47  if (!col) {
48  throw LSST_EXCEPT(lsst::pex::exceptions::NotFoundError,
49  str(boost::format("Unable to read data for %s from %s") % colName % indexName));
50  }
51 
52  return col;
53 }
54 
55 
56 /*
57  * Implementation for index_t::getCatalog method
58  */
60 getCatalogImpl(std::vector<index_t*> inds,
61  double ra, double dec, double radius,
62  char const* idcol,
63  std::vector<mag_column_t> const& magcols,
64  char const* stargalcol,
65  char const* varcol,
66  bool unique_ids)
67 {
68  /*
69  If unique_ids == true: return only reference sources with unique IDs;
70  arbitrarily keep the first star found with each ID.
71  */
72 
73  size_t const nMag = magcols.size(); /* number of magnitude[error] columns */
74  std::vector<mag_column_t>::const_iterator mc;
75 
76  for (mc = magcols.begin(); mc != magcols.end(); ++mc) {
77  if (mc->name.size() == 0) {
78  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
79  "Magnitude names cannot be empty strings.");
80  }
81  // We enforce this condition because we convert the mags to fluxes, and
82  // we need a flux to compute a flux error!
83  if (mc->magcol.size() == 0) {
84  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
85  "Magnitude column names cannot be empty string.");
86  }
87  //printf("mag col \"%s\", \"%s\", \"%s\"\n", mc->name.c_str(), mc->magcol.c_str(), mc->magerrcol.c_str());
88  }
89 
90  double xyz[3];
91  radecdeg2xyzarr(ra, dec, xyz);
92  double r2 = deg2distsq(radius);
93 
95  std::vector<afwTable::Key<double> > fluxKey; // these are double for consistency with measured fluxes;
96  std::vector<afwTable::Key<double> > fluxErrKey; // may be unnecessary, but less surprising.
97  fluxKey.reserve(nMag);
98  fluxErrKey.reserve(nMag);
99 
100  for (mc = magcols.begin(); mc != magcols.end(); ++mc) {
101  // Add schema elements for each requested mag (and optionally mag error)
102  // avoid the comment "flux flux"
103  std::string comment = (mc->name == "flux" ? "flux" : mc->name + std::string(" flux"));
104  fluxKey.push_back(schema.addField<double>(mc->name, comment));
105  if (mc->hasErr()) {
106  std::string comment = (mc->name == "flux" ? "flux uncertainty" : mc->name + std::string(" flux uncertainty"));
107  fluxErrKey.push_back(schema.addField<double>(mc->name + ".err", comment));
108  }
109  }
110 
112  if (stargalcol) {
113  stargalKey = schema.addField<afwTable::Flag>(
114  "stargal", "set if the reference object is a star");
115  }
117  if (varcol) {
118  varKey = schema.addField<afwTable::Flag>("var", "set if the reference object is variable");
119  }
120  afwTable::Key<afwTable::Flag> photometricKey = schema.addField<afwTable::Flag>(
121  "photometric", "set if the reference object can be used in photometric calibration"
122  );
123 
125  if (idcol) {
126  // make catalog with no IdFactory, since IDs are external
128  } else {
129  // let the catalog assign IDs
131  }
132 
133  // for unique_ids: keep track of the IDs we have already added to the result set.
134  std::set<boost::int64_t> uids;
135 
136  for (std::vector<index_t*>::iterator pind = inds.begin();
137  pind != inds.end(); ++pind) {
138 
139  index_t* ind = (*pind);
140  // Find nearby stars
141  double *radecs = NULL;
142  int *starinds = NULL;
143  int nstars = 0;
144  startree_search_for(ind->starkd, xyz, r2, NULL,
145  &radecs, &starinds, &nstars);
146  //printf("found %i in \"%s\"\n", nstars, ind->indexname);
147  if (nstars == 0) {
148  continue;
149  }
150 
151  std::vector<float*> mag;
152  std::vector<float*> magerr;
153  mag.reserve(nMag);
154  magerr.reserve(nMag);
155  boost::int64_t* id = NULL;
156  bool* stargal = NULL;
157  bool* var = NULL;
158  if (idcol || nMag || stargalcol || varcol) {
159  fitstable_t* tag = startree_get_tagalong(ind->starkd);
160  tfits_type flt = fitscolumn_float_type();
161  tfits_type boo = fitscolumn_boolean_type();
162  tfits_type i64 = fitscolumn_i64_type();
163 
164  if (!tag) {
165  std::string msg = boost::str(boost::format("astrometry_net_data index file %s does not contain a tag-along table, "
166  "so can't retrieve extra columns. idcol=%s, stargalcol=%s, varcol=%s") %
167  ind->indexname % idcol % stargalcol % varcol);
168  msg += ", mag columns=[";
169  for (unsigned int i=0; i<nMag; i++) {
170  if (i) {
171  msg += ",";
172  }
173  msg += " name='" + magcols[i].name +
174  "', mag='" + magcols[i].magcol +
175  "', magerr='" + magcols[i].magerrcol + "'";
176  }
177  msg += " ]. You may need to edit the $ASTROMETRY_NET_DATA_DIR/andConfig.py file to set idColumn=None, etc.";
178  throw LSST_EXCEPT(lsst::pex::exceptions::NotFoundError, msg);
179  }
180 
181  if (idcol) {
182  id = static_cast<int64_t*>(fitstable_read_column_inds(tag, idcol, i64, starinds, nstars));
183  if (!id) {
184  throw LSST_EXCEPT(lsst::pex::exceptions::NotFoundError,
185  str(boost::format("Unable to read data for %s from %s") %
186  idcol % ind->indexname));
187  }
188  }
189  if (id && unique_ids) {
190  // remove duplicate IDs.
191 
192  // FIXME -- this shouldn't be necessary once we get astrometry_net 0.40
193  // multi-index functionality in place.
194 
195  if (uids.empty()) {
196  uids = std::set<boost::int64_t>(id, id+nstars);
197  } else {
198  int nkeep = 0;
199  for (int i=0; i<nstars; i++) {
200  //std::pair<std::set<boost::int64_t>::iterator, bool>
201  if (uids.insert(id[i]).second) {
202  // inserted; keep this one.
203  if (nkeep != i) {
204  // compact the arrays.
205  starinds[nkeep] = starinds[i];
206  radecs[nkeep*2+0] = radecs[i*2+0];
207  radecs[nkeep*2+1] = radecs[i*2+1];
208  id[nkeep] = id[i];
209  }
210  nkeep++;
211  } else {
212  // did not insert (this id has already been found);
213  // drop this star.
214  }
215  }
216  nstars = nkeep;
217  // if they were all duplicate IDs...
218  if (nstars == 0) {
219  free(starinds);
220  free(radecs);
221  free(id);
222  continue;
223  }
224  }
225  }
226 
227  for (mc = magcols.begin(); mc != magcols.end(); ++mc) {
228  char const* col = mc->magcol.c_str();
229  mag.push_back(read_column(tag, col, flt, starinds, nstars, ind->indexname));
230  if (mc->hasErr()) {
231  char const* col = mc->magerrcol.c_str();
232  magerr.push_back(read_column(tag, col, flt, starinds, nstars, ind->indexname));
233  }
234  }
235  if (stargalcol) {
236  /* There is something weird going on with handling of bools; maybe "T" vs "F"?
237  stargal = static_cast<bool*>(fitstable_read_column_inds(tag, stargalcol, boo, starinds, nstars));
238  for (int j=0; j<nstars; j++) {
239  printf(" sg %i = %i, vs %i\n", j, (int)sg[j], stargal[j] ? 1:0);
240  }
241  */
242  uint8_t* sg = static_cast<uint8_t*>(fitstable_read_column_inds(tag, stargalcol, fitscolumn_u8_type(), starinds, nstars));
243  stargal = static_cast<bool*>(malloc(nstars));
244  if (!stargal) {
245  throw LSST_EXCEPT(lsst::pex::exceptions::NotFoundError,
246  str(boost::format("Unable to read data for %s from %s") %
247  stargalcol % ind->indexname));
248  }
249  for (int j=0; j<nstars; j++) {
250  stargal[j] = (sg[j] > 0);
251  }
252  free(sg);
253  }
254  if (varcol) {
255  var = static_cast<bool*>(fitstable_read_column_inds(tag, varcol, boo, starinds, nstars));
256  if (!var) {
257  throw LSST_EXCEPT(lsst::pex::exceptions::NotFoundError,
258  str(boost::format("Unable to read data for %s from %s") %
259  varcol % ind->indexname));
260  }
261  }
262  }
263 
264  for (int i=0; i<nstars; i++) {
265  PTR(afwTable::SimpleRecord) src = cat.addNew();
266 
267  // Note that all coords in afwTable catalogs are ICRS; hopefully that's what the
268  // reference catalogs are (and that's what the code assumed before JFB modified it).
269  src->setCoord(
271  radecs[i * 2 + 0] * afwGeom::degrees,
272  radecs[i * 2 + 1] * afwGeom::degrees
273  )
274  );
275 
276  if (id) {
277  src->setId(id[i]);
278  }
279 
280  assert(fluxKey.size() == nMag);
281  // only non-empty error columns are populated in these vectors.
282  assert(fluxErrKey.size() == magerr.size());
283  // index into non-empty error columns.
284  size_t ej = 0;
285  size_t j = 0;
286  for (mc = magcols.begin(); mc != magcols.end(); ++mc, ++j) {
287  // Dustin thinks converting to flux is 'LAME!';
288  // Jim thinks it's nice for consistency (and photocal wants fluxes
289  // as inputs, so we'll continue to go with that for now) even though
290  // we don't need to anymore.
291  // Dustin rebuts that photocal immediately converts those fluxes into mags,
292  // so :-P
293  //printf("mag %s = %g\n", mc->name.c_str(), mag[j][i]);
294  double flux = pow(10.0, -0.4*mag[j][i]);
295  //printf("flux %g\n", flux);
296  src->set(fluxKey[j], flux);
297  if (mc->hasErr()) {
298  //printf("mag err = %g\n", magerr[ej][i]);
299  double fluxerr = fabs(-0.4*magerr[ej][i]*flux*std::log(10.0));
300  //printf("flux err = %g\n", fluxerr);
301  src->set(fluxErrKey[ej], fluxerr);
302  ej++;
303  }
304  }
305  assert(ej == fluxErrKey.size());
306 
307  bool ok = true;
308  if (stargal) {
309  src->set(stargalKey, stargal[i]);
310  ok &= stargal[i];
311  }
312  if (var) {
313  src->set(varKey, var[i]);
314  ok &= (!var[i]);
315  }
316  src->set(photometricKey, ok);
317  }
318 
319  free(id);
320  for (size_t j=0; j<mag.size(); ++j) {
321  free(mag[j]);
322  }
323  for (size_t j=0; j<magerr.size(); ++j) {
324  free(magerr[j]);
325  }
326  free(stargal);
327  free(var);
328  free(radecs);
329  free(starinds);
330  }
331  return cat;
332 }
333 }}}}
334 
Defines the fields and offsets for a table.
Definition: Schema.h:46
#define PTR(...)
Definition: base.h:41
static Schema makeMinimalSchema()
Return a minimal schema for Simple tables and records.
Definition: Simple.h:120
afwTable::SimpleCatalog getCatalogImpl(std::vector< index_t * > inds, double ra, double dec, double radius, char const *idcol, std::vector< mag_column_t > const &magcols, char const *stargalcol, char const *varcol, bool unique_ids)
Definition: utils.cc:60
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
Include files required for standard LSST Exception handling.
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
A polymorphic functor base class for generating record IDs for a table.
Definition: IdFactory.h:19
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
A class used as a handle to a particular field in a table.
Definition: fwd.h:44
int id
Definition: CR.cc:151
AngleUnit const degrees
Definition: Angle.h:92
static boost::shared_ptr< SimpleTable > make(Schema const &schema, boost::shared_ptr< IdFactory > const &idFactory)
Construct a new table.
Record class that must contain a unique ID field and a celestial coordinate field.
Definition: Simple.h:45
SortedCatalogT< SimpleRecord > SimpleCatalog
Definition: fwd.h:69
A class to handle Icrs coordinates (inherits from Coord)
Definition: Coord.h:156
int col
Definition: CR.cc:152