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
Match.h
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 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 
49 #ifndef LSST_AP_MATCH_H
50 #define LSST_AP_MATCH_H
51 
52 #if LSST_AP_HAVE_OPEN_MP
53 # include <omp.h>
54 #endif
55 
56 #include <stdexcept>
57 #include <vector>
58 
59 #include "Common.h"
60 #include "EllipseTypes.h"
61 #include "SpatialUtil.h"
62 #include "ZoneTypes.h"
63 
64 
65 namespace lsst { namespace ap {
66 
67 
69 template <typename T>
71  bool operator() (T const &) { return true; }
72 };
73 
74 
76 template <typename T>
78 
80 
81  MatchWithDistance(T * const match, double const d2) :
82  _match(match),
83  _distance(2.0*std::asin(0.5*std::sqrt(d2)))
84  {}
85 
86  T & operator*() const { return *_match; }
87  T * operator->() const { return _match; }
88 
89  T * _match;
90  double _distance;
91 };
92 
93 
95 template <typename T>
97 
99 
100  MatchWithoutDistance(T * const match, double const) : _match(match) {}
101 
102  T & operator*() const { return *_match; }
103  T * operator->() const { return _match; }
104 
105  T * _match;
106 };
107 
108 
110 template <typename F, typename M>
112 
113  typedef M Match;
114  typedef typename std::vector<M>::const_iterator MatchIterator;
115 
116  inline void operator() (F & entity, MatchIterator begin, MatchIterator end) {}
117 };
118 
119 
121 template <typename F, typename S>
123  inline void operator() (F & first, S & second) {}
124 };
125 
126 
146 template <
147  typename FirstEntryT,
148  typename SecondEntryT,
149  typename FirstFilterT, // = PassthroughFilter<FirstEntryT>,
150  typename SecondFilterT, // = PassthroughFilter<SecondEntryT>,
151  typename MatchListProcessorT // = EmptyMatchListProcessor<FirstEntryT, MatchWithoutDistance<SecondEntryT> >
152 >
153 std::size_t distanceMatch(
154  ZoneIndex<FirstEntryT> & first,
155  ZoneIndex<SecondEntryT> & second,
156  double const radius,
157  FirstFilterT & firstFilter,
158  SecondFilterT & secondFilter,
159  MatchListProcessorT & matchListProcessor
160 ) {
161  typedef typename ZoneIndex<FirstEntryT>::Zone FirstZone;
162  typedef typename ZoneIndex<SecondEntryT>::Zone SecondZone;
163  typedef typename MatchListProcessorT::Match Match;
164 
165  if (radius < 0) {
166  throw LSST_EXCEPT(lsst::pex::exceptions::RangeError,
167  "match radius must be greater than or equal to zero degrees");
168  }
169 
170  double const shr = std::sin(radians(radius*0.5));
171  double const d2Limit = 4.0*shr*shr;
172  int const minZone = first.getMinZone();
173  int const maxZone = first.getMaxZone();
174  boost::int32_t const deltaDec = deltaDecToScaledInteger(radius);
175 
176  std::size_t numMatchPairs = 0;
177 
178  second.computeMatchParams(radius);
179 
180  // loop over first set of zones in parallel
181 #if LSST_AP_HAVE_OPEN_MP
182 # pragma omp parallel default(shared)
183 #endif
184  {
185  // allocate per-thread data structures
186  SecondZone * zones[2048];
187  int limits[2048];
188  std::vector<Match> matches;
189  matches.reserve(32);
190 
191  // loop over the first set of zones in parallel, assigning batches of
192  // adjacent zones to threads (for cache coherency)
193 #if LSST_AP_HAVE_OPEN_MP
194 # pragma omp for \
195  reduction(+:numMatchPairs) \
196  schedule(static,8)
197 #endif
198  for (int fzi = minZone; fzi <= maxZone; ++fzi) {
199 
200  FirstZone * const __restrict fz = first.getZone(fzi);
201  FirstEntryT * const __restrict fze = fz->_entries;
202  int const nfze = fz->_size;
203  if (nfze <= 0) {
204  continue; // no entries in first zone
205  }
206 
207  // populate secondary zone array with potentially matching zones
208  int nsz = 0;
209  {
210  double d = first.getDecomposition().getZoneDecMin(fz->_zone) - radius;
211  int const minz = second.getDecomposition().decToZone(d <= -90.0 ? -90.0 : d);
212  d = first.getDecomposition().getZoneDecMax(fz->_zone) + radius;
213  int const maxz = second.getDecomposition().decToZone(d >= 90.0 ? 90.0 : d);
214  // A search circle should never cover more than 2048 zones
215  assert(maxz - minz + 1 <= 2048 && "match radius too large");
216 
217  SecondZone * __restrict sz = second.firstZone(minz, maxz);
218  SecondZone * const __restrict szend = second.endZone(minz, maxz);
219 
220  for ( ; sz < szend; ++sz) {
221  int const nsze = sz->_size;
222  if (nsze > 0) {
223  zones[nsz] = sz;
224  if ((nsze >> 4) > nfze) {
225  // second set much larger than first, use binary search in inner loop
226  limits[nsz] = -1;
227  } else {
228  // use linear walk in inner loop, find starting point now
229  limits[nsz] = sz->findGte(fze[0]._ra - sz->_deltaRa);
230  }
231  ++nsz;
232  }
233  }
234  }
235 
236  if (nsz == 0) {
237  // no entries in any potentially matching zones
238  continue;
239  }
240 
241  // loop over entries in first zone
242  for (int fe = 0; fe < nfze; ++fe) {
243 
244  if (!firstFilter(fze[fe])) {
245  continue; // entry was filtered out
246  }
247 
248  matches.clear();
249 
250  boost::uint32_t const ra = fze[fe]._ra;
251  boost::int32_t const dec = fze[fe]._dec;
252  double const fx = fze[fe]._x;
253  double const fy = fze[fe]._y;
254  double const fz = fze[fe]._z;
255 
256  // loop over all potentially matching zones.
257  for (int szi = 0; szi < nsz; ++szi) {
258 
259  SecondZone * const __restrict sz = zones[szi];
260  SecondEntryT * const __restrict sze = sz->_entries;
261 
262  int const seWrap = sz->_size;
263  boost::uint32_t const deltaRa = sz->_deltaRa;
264  boost::uint32_t const deltaRaWrap = -deltaRa;
265 
266  int start = limits[szi];
267  int se;
268  boost::uint32_t dra;
269 
270  if (start >= 0) {
271 
272  // use linear walk from last starting point
273  // to get to first point in ra range
274  se = start;
275  dra = ra - sze[se]._ra;
276  if (dra > deltaRa && dra < deltaRaWrap) {
277 
278  bool cont = false;
279  do {
280  ++se;
281  if (se == seWrap) {
282  se = 0; // ra wrap around
283  }
284  if (se == start) {
285  cont = true;
286  break; // avoid infinite loops
287  }
288  dra = ra - sze[se]._ra;
289  } while (dra > deltaRa && dra < deltaRaWrap);
290 
291  if (cont) {
292  continue; // no starting point found
293  }
294  // found starting point -- remember it
295  limits[szi] = se;
296  start = se;
297  }
298 
299  } else {
300 
301  // use binary search to find starting point
302  start = sz->findGte(ra - deltaRa);
303  se = start;
304  dra = ra - sze[se]._ra;
305  if (dra > deltaRa && dra < deltaRaWrap) {
306  continue; // no starting point found
307  }
308 
309  }
310 
311  // At this point, entry se is within ra range of fe --
312  // loop over all zone entries within ra range
313  do {
314 
315  // test whether entry se is within dec range
316  if (abs(dec - sze[se]._dec) <= deltaDec) {
317  // yes -- perform detailed distance test
318  double xd = (fx - sze[se]._x);
319  double yd = (fy - sze[se]._y);
320  double zd = (fz - sze[se]._z);
321  double d2 = xd*xd + yd*yd + zd*zd;
322  if (d2 < d2Limit) {
323  // Note: this isn't necessarily the best place for the second filter test...
324  if (secondFilter(sze[se])) {
325  // found a match, record it
326  matches.push_back(Match(&sze[se], d2));
327  }
328  }
329  }
330  ++se;
331  if (se == seWrap) {
332  se = 0; // ra wrap around
333  }
334  if (se == start) {
335  break; // avoid infinite loops
336  }
337  dra = ra - sze[se]._ra;
338 
339  } while (dra <= deltaRa || dra >= deltaRaWrap);
340 
341  } // end of loop over potentially matching zones
342 
343  // All matches (if any) for fe are found
344  std::size_t nm = matches.size();
345  if (nm > 0) {
346  // pass them on to the match processor
347  numMatchPairs = numMatchPairs + nm;
348  matchListProcessor(fze[fe], matches.begin(), matches.end());
349  }
350 
351  } // end of loop over entries in first zone
352 
353  } // end of omp for
354  } // end of omp parallel
355 
356  return numMatchPairs;
357 }
358 
359 
379 template <
380  typename FirstEntryT,
381  typename SecondEntryT,
382  typename FirstFilterT, // = PassthroughFilter<FirstEntryT>,
383  typename SecondFilterT, // = PassthroughFilter<SecondEntryT>,
384  typename MatchPairProcessorT // = EmptyMatchPairProcessor<FirstEntryT, SecondEntryT>
385 >
386 std::size_t ellipseMatch(
387  EllipseList<FirstEntryT> & first,
388  ZoneIndex<SecondEntryT> & second,
389  FirstFilterT & firstFilter,
390  SecondFilterT & secondFilter,
391  MatchPairProcessorT & matchPairProcessor
392 ) {
393  typedef typename ZoneIndex<SecondEntryT>::Zone SecondZone;
394 
395  int const minZone = second.getMinZone();
396  int const maxZone = second.getMaxZone();
397  std::size_t numMatchPairs = 0;
398 
399  first.prepareForMatch(second.getDecomposition());
400 
401  Ellipse<FirstEntryT> * activeHead = 0;
402  Ellipse<FirstEntryT> * activeTail = 0;
403  Ellipse<FirstEntryT> * searchHead = &(*first.begin());
404  Ellipse<FirstEntryT> * const end = &(*first.end());
405 
406  // initialize the linked list of active ellipses (those that intersect minZone)
407  while (searchHead < end && searchHead->_minZone <= minZone) {
408  if (searchHead->_maxZone >= minZone) {
409  if (firstFilter(*searchHead)) {
410  if (activeTail == 0) {
411  activeHead = searchHead;
412  } else {
413  activeTail->_next = searchHead;
414  }
415  activeTail = searchHead;
416  }
417  }
418  ++searchHead;
419  }
420 
421  // loop over the set of zones in the second entity set
422  int szi = minZone;
423  while (true) {
424 
425  SecondZone * const __restrict sz = second.getZone(szi);
426  SecondEntryT * const __restrict sze = sz->_entries;
427  int const seWrap = sz->_size;
428 
429  ++szi;
430 
431  // Traverse the active ellipse list
432  Ellipse<FirstEntryT> * active = activeHead;
433  Ellipse<FirstEntryT> * prev = 0;
434 
435  while (active != 0) {
436 
437 #if defined(__GNUC__)
438  __builtin_prefetch(active->_next, 0, 3);
439 #endif
440  if (seWrap > 0) {
441  // find entries within ra range of the active ellipse
442  boost::uint32_t const ra = active->_ra;
443  boost::uint32_t const deltaRa = active->_deltaRa;
444  boost::uint32_t const deltaRaWrap = -deltaRa;
445  int const start = sz->findGte(ra - deltaRa);
446 
447  int se = start;
448  boost::uint32_t dra = ra - sze[se]._ra;
449  while (dra <= deltaRa || dra >= deltaRaWrap) {
450 
451  // check whether entry is within dec range
452  boost::int32_t const dec = sze[se]._dec;
453  if (dec >= active->_minDec && dec <= active->_maxDec) {
454  // perform detailed in ellipse test
455  if (active->contains(sze[se]._x, sze[se]._y, sze[se]._z)) {
456  if (secondFilter(sze[se])) {
457  // process match pair
458  matchPairProcessor(*active, sze[se]);
459  ++numMatchPairs;
460  }
461  }
462  }
463  ++se;
464  if (se == seWrap) {
465  se = 0; // ra wrap around
466  }
467  if (se == start) {
468  break; // avoid infinite loops
469  }
470  dra = ra - sze[se]._ra;
471  }
472  }
473 
474  if (active->_maxZone < szi) {
475  // ellipse no longer active in following zone -- remove it from the active list
476  active = active->_next;
477  if (prev == 0) {
478  activeHead = active;
479  } else {
480  prev->_next = active;
481  }
482  } else {
483  // keep ellipse in the active list
484  prev = active;
485  active = active->_next;
486  }
487 
488  } // end of loop over active ellipses
489 
490  if (szi > maxZone) {
491  break;
492  }
493 
494  // Add ellipses with minimum zone equal to szi (the zone that will be searched
495  // in the next loop iteration) to the active list
496  while (searchHead < end && searchHead->_minZone <= szi) {
497  if (firstFilter(*searchHead)) {
498  if (prev == 0) {
499  activeHead = searchHead;
500  } else {
501  prev->_next = searchHead;
502  }
503  prev = searchHead;
504  }
505  ++searchHead;
506  }
507 
508  } // end of loop over second set of zones
509 
510  return numMatchPairs;
511 }
512 
513 
528 template <
529  typename FirstEntryT,
530  typename SecondEntryT,
531  typename FirstFilterT, // = PassthroughFilter<FirstEntryT>,
532  typename SecondFilterT, // = PassthroughFilter<SecondEntryT>,
533  typename MatchListProcessorT // = EmptyMatchListProcessor<FirstEntryT, SecondEntryT>
534 >
536  EllipseList<FirstEntryT> & first,
537  ZoneIndex<SecondEntryT> & second,
538  FirstFilterT & firstFilter,
539  SecondFilterT & secondFilter,
540  MatchListProcessorT & matchListProcessor
541 ) {
542  typedef typename ZoneIndex<SecondEntryT>::Zone SecondZone;
543  typedef typename MatchListProcessorT::Match Match;
544 
545  std::size_t const numEllipses = first.size();
546  std::size_t numMatchPairs = 0;
547 
548  first.prepareForMatch(second.getDecomposition());
549 
550 #if LSST_AP_HAVE_OPEN_MP
551 # pragma omp parallel default(shared)
552 #endif
553  {
554  // allocate per-thread match list
555  std::vector<Match> matches;
556  matches.reserve(2048);
557 
558  // loop over the list of ellipses in parallel
559 #if LSST_AP_HAVE_OPEN_MP
560 # pragma omp for \
561  reduction(+:numMatchPairs) \
562  schedule(static,128)
563 #endif
564  for (std::size_t i = 0; i < numEllipses; ++i) {
565 
566  if (!firstFilter(first[i])) {
567  continue; // ellipse was filtered out
568  }
569 
570  Ellipse<FirstEntryT> * const __restrict ell = &first[i];
571  SecondZone * __restrict sz = second.firstZone(ell->_minZone, ell->_maxZone);
572  SecondZone * const __restrict szend = second.endZone(ell->_minZone, ell->_maxZone);
573 
574  boost::uint32_t const ra = ell->_ra;
575  boost::uint32_t const deltaRa = ell->_deltaRa;
576  boost::uint32_t const deltaRaWrap = -deltaRa;
577 
578  matches.clear();
579 
580  // loop over zones covered by the ellipse
581  for ( ; sz < szend; ++sz) {
582 
583  int const seWrap = sz->_size;
584  if (seWrap == 0) {
585  continue;
586  }
587 
588  // find starting point within ra range of the ellipse
589  SecondEntryT * const __restrict sze = sz->_entries;
590  int const start = sz->findGte(ra - deltaRa);
591  int se = start;
592  boost::uint32_t dra = ra - sze[se]._ra;
593 
594  while (dra <= deltaRa || dra >= deltaRaWrap) {
595 
596  // check whether entry is within dec range
597  boost::int32_t const dec = sze[se]._dec;
598  if (dec >= ell->_minDec && dec <= ell->_maxDec) {
599  // perform detailed in ellipse test
600  if (ell->contains(sze[se]._x, sze[se]._y, sze[se]._z)) {
601  if (secondFilter(sze[se])) {
602  // record match
603  matches.push_back(Match(&sze[se]));
604  }
605  }
606  }
607  ++se;
608  if (se == seWrap) {
609  se = 0; // ra wrap around
610  }
611  if (se == start) {
612  break; // avoid infinite loops
613  }
614  dra = ra - sze[se]._ra;
615  }
616  }
617 
618  // All matches (if any) for ell are found
619  std::size_t nm = matches.size();
620  if (nm > 0) {
621  // pass them on to the match processor
622  numMatchPairs = numMatchPairs + nm;
623  matchListProcessor(*ell, matches.begin(), matches.end());
624  }
625 
626  } // end of omp for
627  } // end of omp parallel
628 
629  return numMatchPairs;
630 }
631 
632 
633 }} // end of namespace lsst::ap
634 
635 #endif // LSST_AP_MATCH_H
T * operator->() const
Definition: Match.h:87
void operator()(F &entity, MatchIterator begin, MatchIterator end)
Definition: Match.h:116
boost::int32_t deltaDecToScaledInteger(double const delta)
Definition: SpatialUtil.h:270
A default &quot;do nothing&quot; match pair processing implementation.
Definition: Match.h:122
double getZoneDecMax(int const zone) const
Definition: SpatialUtil.h:146
int decToZone(double const dec) const
Definition: SpatialUtil.h:132
void computeMatchParams(double const radius)
Prepares for distance based matches of the given maximum radius.
Definition: ZoneTypes.cc:269
boost::uint32_t _deltaRa
width (in right ascension) of ellipse
Definition: EllipseTypes.h:61
std::size_t ellipseMatch(EllipseList< FirstEntryT > &first, ZoneIndex< SecondEntryT > &second, FirstFilterT &firstFilter, SecondFilterT &secondFilter, MatchPairProcessorT &matchPairProcessor)
Definition: Match.h:386
boost::int32_t _minDec
minimum declination of ellipse bounding-box
Definition: EllipseTypes.h:62
void operator()(F &first, S &second)
Definition: Match.h:123
std::size_t distanceMatch(ZoneIndex< FirstEntryT > &first, ZoneIndex< SecondEntryT > &second, double const radius, FirstFilterT &firstFilter, SecondFilterT &secondFilter, MatchListProcessorT &matchListProcessor)
Definition: Match.h:153
A default &quot;let everything through&quot; filter implementation.
Definition: Match.h:70
Contains spatial information for a single ellipse on the unit sphere (sky).
Definition: EllipseTypes.h:52
A list of ellipses, implemented using std::vector.
Definition: EllipseTypes.h:141
int d
Definition: KDTree.cc:89
Contains a pointer to a match.
Definition: Match.h:96
ZoneStripeChunkDecomposition const & getDecomposition() const
Definition: ZoneTypes.h:281
Container for a sequence of adjacent zones.
Definition: ZoneTypes.h:199
boost::uint32_t _ra
right ascension of ellipse center
Definition: EllipseTypes.h:60
Stores entries inside a single zone (a narrow declination stripe) in a sorted array.
Definition: ZoneTypes.h:113
AngleUnit const radians
constant with units of radians
Definition: Angle.h:91
Ellipse * _next
pointer to next active ellipse in search
Definition: EllipseTypes.h:56
void prepareForMatch(ZoneStripeChunkDecomposition const &zsc)
Definition: EllipseTypes.cc:95
int _minZone
minimum zone of ellipse bounding-box
Definition: EllipseTypes.h:58
double getZoneDecMin(int const zone) const
Definition: SpatialUtil.h:139
Contains a pointer to a match and an associated distance.
Definition: Match.h:77
Zone * firstZone(int const minZone, int const maxZone)
Definition: ZoneTypes.h:257
int _maxZone
maximum zone of ellipse bounding-box
Definition: EllipseTypes.h:59
bool operator()(T const &)
Definition: Match.h:71
Master header file for the association pipeline.
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
bool contains(double const x, double const y, double const z) const
Returns true if the ellipse contains the given unit vector.
Definition: EllipseTypes.h:77
int getMaxZone() const
Definition: ZoneTypes.h:240
T & operator*() const
Definition: Match.h:86
MatchWithDistance(T *const match, double const d2)
Definition: Match.h:81
MatchWithoutDistance(T *const match, double const)
Definition: Match.h:100
Classes for zone entries, zones, and zone indexes.
A default &quot;do nothing&quot; match list processing implementation.
Definition: Match.h:111
int getMinZone() const
Definition: ZoneTypes.h:237
std::size_t ellipseGroupedMatch(EllipseList< FirstEntryT > &first, ZoneIndex< SecondEntryT > &second, FirstFilterT &firstFilter, SecondFilterT &secondFilter, MatchListProcessorT &matchListProcessor)
Definition: Match.h:535
Zone * getZone(int const zone)
Definition: ZoneTypes.h:246
Types involved in algorithms for matching points inside ellipses.
Zone * endZone(int const minZone, int const maxZone)
Definition: ZoneTypes.h:271
Class and helper functions related to spatial partitioning.
std::vector< M >::const_iterator MatchIterator
Definition: Match.h:114