LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
PointIsolationAlg.h
Go to the documentation of this file.
1 
12 #ifndef LAREXAMPLES_ALGORITHMS_REMOVEISOLATEDSPACEPOINTS_POINTISOLATIONALG_H
13 #define LAREXAMPLES_ALGORITHMS_REMOVEISOLATEDSPACEPOINTS_POINTISOLATIONALG_H
14 
15 // LArSoft libraries
17 
18 // infrastructure and utilities
19 #include "cetlib/pow.h" // cet::sum_squares()
20 
21 // C/C++ standard libraries
22 #include <cassert> // assert()
23 #include <cmath> // std::sqrt()
24 #include <vector>
25 #include <array>
26 #include <string>
27 #include <type_traits> // std::add_const_t<>
28 #include <iterator> // std::cbegin(), std::cend(), std::distance()
29 #include <stdexcept> // std::runtime_error
30 
31 
32 namespace lar {
33  namespace example {
34 
35  // BEGIN RemoveIsolatedSpacePoints group -----------------------------------
38 
127  template <typename Coord = double>
129 
130  public:
132  using Coord_t = Coord;
134 
141  size_t maxMemory = 100 * 1048576;
143  }; // Configuration_t
144 
145 
148 
156  PointIsolationAlg(Configuration_t const& first_config)
157  : config(first_config)
158  {}
159 
163  void reconfigure(Configuration_t const& new_config)
164  { config = new_config; }
165 
166 
169  Configuration_t& configuration() const { return config; }
170 
172 
173 
191  template <typename PointIter>
192  std::vector<size_t> removeIsolatedPoints
193  (PointIter begin, PointIter end) const;
194 
195 
202  template <typename Cont>
203  std::vector<size_t> removeIsolatedPoints (Cont const& points) const
204  { return removeIsolatedPoints(std::cbegin(points), std::cend(points)); }
205 
206 
220  template <typename PointIter>
221  std::vector<size_t> bruteRemoveIsolatedPoints
222  (PointIter begin, PointIter end) const;
223 
224 
227 
230  static void validateConfiguration(Configuration_t const& config);
231 
233 
234 
237  { return radius / std::sqrt(3.); }
238 
239 
240  private:
242  using Indexer_t = util::GridContainer3DIndices; // same in GridContainer
243 
245  using NeighAddresses_t = std::vector<Indexer_t::CellIndexOffset_t>;
246 
247  template <typename PointIter>
249 
250  template <typename PointIter>
251  using Point_t = decltype(*PointIter());
252 
253 
255 
256 
258  template <typename PointIter = std::array<double, 3> const*>
259  Coord_t computeCellSize() const;
260 
261 
264  (Indexer_t const& indexer, unsigned int neighExtent) const;
265 
267  template <typename PointIter>
268  bool isPointIsolatedFrom(
269  Point_t<PointIter> const& point,
270  typename Partition_t<PointIter>::Cell_t const& otherPoints
271  ) const;
272 
274  template <typename PointIter>
276  Partition_t<PointIter> const& partition,
277  Indexer_t::CellIndex_t cellIndex,
278  Point_t<PointIter> const& point,
279  NeighAddresses_t const& neighList
280  ) const;
281 
283  template <typename Point>
284  bool closeEnough(Point const& A, Point const& B) const;
285 
286 
288  static std::string rangeString(Coord_t from, Coord_t to);
289 
291  static std::string rangeString(Range_t range)
292  { return rangeString(range.lower, range.upper); }
293 
294  }; // class PointIsolationAlg
295 
296 
297  //--------------------------------------------------------------------------
299  // END RemoveIsolatedSpacePoints group -------------------------------------
300 
301  } // namespace example
302 } // namespace lar
303 
304 
305 
306 //------------------------------------------------------------------------------
307 //--- template implementation
308 //------------------------------------------------------------------------------
309 template <typename Coord>
310 template <typename PointIter>
312  (PointIter begin, PointIter end) const
313 {
314 
315  std::vector<size_t> nonIsolated;
316 
317  Coord_t const R = std::sqrt(config.radius2);
318 
319  //
320  // determine space partition settings: cell size
321  //
322  // maximum: the volume of a single cell must be contained in a sphere with
323  // radius equal to the isolation radius R
324  //
325  // minimum: needs tuning
326  //
327 
328  Coord_t cellSize = computeCellSize<PointIter>();
329  assert(cellSize > 0);
330  Partition_t<PointIter> partition(
331  { config.rangeX, cellSize },
332  { config.rangeY, cellSize },
333  { config.rangeZ, cellSize }
334  );
335 
336  // if a cell is contained in a sphere with
337  bool const cellContainedInIsolationSphere
338  = (cellSize <= maximumOptimalCellSize(R));
339 
340  //
341  // determine neighbourhood
342  // the neighbourhood is the number of cells that might contain points closer
343  // than R to a cell; it is equal to R in cell size units, rounded up;
344  // it's expressed as a list of coordinate shifts from a base cell to all the
345  // others in the neighbourhood; it is contained in a cube
346  //
347  unsigned int const neighExtent = (int) std::ceil(R / cellSize);
348  NeighAddresses_t neighList
349  = buildNeighborhood(partition.indexManager(), neighExtent);
350 
351  // if a cell is not fully contained in a isolation radius, we need to check
352  // the points of the cell with each other: their cell becomes part of the
353  // neighbourhood
354  if (!cellContainedInIsolationSphere)
355  neighList.insert(neighList.begin(), { 0, 0, 0 });
356 
357  //
358  // populate the partition
359  //
360  partition.fill(begin, end);
361 
362  //
363  // for each cell in the partition:
364  //
365  size_t const nCells = partition.indexManager().size();
366  for (Indexer_t::CellIndex_t cellIndex = 0; cellIndex < nCells; ++cellIndex) {
367  auto const& cellPoints = partition[cellIndex];
368 
369  //
370  // if the cell has more than one element, mark all points as non-isolated;
371  // true only if the cell is completely contained within a R rßadius
372  //
373  if (cellContainedInIsolationSphere && (cellPoints.size() > 1)) {
374  for (auto const& pointPtr: cellPoints)
375  nonIsolated.push_back(std::distance(begin, pointPtr));
376  continue;
377  } // if all non-isolated
378 
379  //
380  // brute force approach: try all the points in this cell against all the
381  // points in the neighbourhood
382  //
383  for (auto const pointPtr: cellPoints) {
384  //
385  // optimisation (speed): mark the points from other cells as non-isolated
386  // when they trigger non-isolation in points of the current one
387  //
388 
389  // TODO
390 
392  (partition, cellIndex, *pointPtr, neighList)
393  )
394  {
395  nonIsolated.push_back(std::distance(begin, pointPtr));
396  }
397  } // for points in cell
398 
399  } // for cell
400 
401  return nonIsolated;
402 } // lar::example::PointIsolationAlg::removeIsolatedPoints()
403 
404 
405 //--------------------------------------------------------------------------
406 template <typename Coord>
409 {
410  std::vector<std::string> errors;
411  if (config.radius2 < Coord_t(0)) {
412  errors.push_back
413  ("invalid radius squared (" + std::to_string(config.radius2) + ")");
414  }
415  if (!config.rangeX.valid()) {
416  errors.push_back("invalid x range " + rangeString(config.rangeX));
417  }
418  if (!config.rangeY.valid()) {
419  errors.push_back("invalid y range " + rangeString(config.rangeY));
420  }
421  if (!config.rangeZ.valid()) {
422  errors.push_back("invalid z range " + rangeString(config.rangeZ));
423  }
424 
425  if (errors.empty()) return;
426 
427  // compose the full error message as concatenation:
428  std::string message
429  (std::to_string(errors.size()) + " configuration errors found:");
430 
431  for (auto const& error: errors) message += "\n * " + error;
432  throw std::runtime_error(message);
433 
434 } // lar::example::PointIsolationAlg::validateConfiguration()
435 
436 
437 //--------------------------------------------------------------------------
438 template <typename Coord>
439 template <typename PointIter /* = std::array<double, 3> const* */>
441 
442  Coord_t const R = std::sqrt(config.radius2);
443 
444  // maximum: the maximum distance between two points in the cell (that is,
445  // the diagonal of the cell) must be no larger than the isolation radius R;
446  // minimum: needs tuning
447  Coord_t cellSize = maximumOptimalCellSize(R); // try the minimum for now
448 
449  //
450  // optimisation (memory): determine minimum size of box
451  //
452 
453  // TODO
454 
455  if (config.maxMemory == 0) return cellSize;
456 
457  do {
458  std::array<size_t, 3> partition = details::diceVolume(
459  CoordRangeCells<Coord_t>{ config.rangeX, cellSize },
460  CoordRangeCells<Coord_t>{ config.rangeY, cellSize },
461  CoordRangeCells<Coord_t>{ config.rangeZ, cellSize }
462  );
463 
464  size_t const nCells = partition[0] * partition[1] * partition[2];
465  if (nCells <= 1) break; // we can't reduce it any further
466 
467  // is memory low enough?
468  size_t const memory
469  = nCells * sizeof(typename SpacePartition<PointIter>::Cell_t);
470  if (memory < config.maxMemory) break;
471 
472  cellSize *= 2;
473  } while (true);
474 
475  return cellSize;
476 } // lar::example::PointIsolationAlg<Coord>::computeCellSize()
477 
478 
479 //------------------------------------------------------------------------------
480 template <typename Coord>
483  (Indexer_t const& indexer, unsigned int neighExtent) const
484 {
485  unsigned int const neighSize = 1 + 2 * neighExtent;
486  NeighAddresses_t neighList;
487  neighList.reserve(neighSize * neighSize * neighSize - 1);
488 
489  using CellID_t = Indexer_t::CellID_t;
490  using CellDimIndex_t = Indexer_t::CellDimIndex_t;
491 
492  //
493  // optimisation (speed): reshape the neighbourhood
494  // neighbourhood might cut out cells close to the vertices
495  //
496 
497  // TODO
498 
499  CellDimIndex_t const ext = neighExtent; // convert into the right signedness
500 
501  CellID_t center{{ 0, 0, 0 }}, cellID;
502  for (CellDimIndex_t ixOfs = -ext; ixOfs <= ext; ++ixOfs) {
503  cellID[0] = ixOfs;
504  for (CellDimIndex_t iyOfs = -ext; iyOfs <= ext; ++iyOfs) {
505  cellID[1] = iyOfs;
506  for (CellDimIndex_t izOfs = -ext; izOfs <= ext; ++izOfs) {
507  if ((ixOfs == 0) && (iyOfs == 0) && (izOfs == 0)) continue;
508  cellID[2] = izOfs;
509 
510  neighList.push_back(indexer.offset(center, cellID));
511 
512  } // for ixOfs
513  } // for iyOfs
514  } // for izOfs
515 
516  return neighList;
517 } // lar::example::PointIsolationAlg<Coord>::buildNeighborhood()
518 
519 
520 //--------------------------------------------------------------------------
521 template <typename Coord>
522 template <typename PointIter>
524  Point_t<PointIter> const& point,
525  typename Partition_t<PointIter>::Cell_t const& otherPoints
526 ) const
527 {
528 
529  for (auto const& otherPointPtr: otherPoints) {
530  // make sure that we did not compare the point with itself
531  if (closeEnough(point, *otherPointPtr) && (&point != &*otherPointPtr))
532  return false;
533  }
534 
535  return true;
536 
537 } // lar::example::PointIsolationAlg<Coord>::isPointIsolatedFrom()
538 
539 
540 //--------------------------------------------------------------------------
541 template <typename Coord>
542 template <typename PointIter>
544  Partition_t<PointIter> const& partition,
545  Indexer_t::CellIndex_t cellIndex,
546  Point_t<PointIter> const& point,
547  NeighAddresses_t const& neighList
548 ) const
549 {
550 
551  // check in all cells of the neighbourhood
552  for (Indexer_t::CellIndexOffset_t neighOfs: neighList) {
553 
554  //
555  // optimisation (speed): have neighbour offsets so that the invalid ones
556  // are all at the beginning and at the end, so that skipping is faster
557  //
558 
559  if (!partition.has(cellIndex + neighOfs)) continue;
560  auto const& neighCellPoints = partition[cellIndex + neighOfs];
561 
562  if (!isPointIsolatedFrom<PointIter>(point, neighCellPoints)) return false;
563 
564  } // for neigh cell
565 
566  return true;
567 
568 } // lar::example::PointIsolationAlg<Coord>::isPointIsolatedWithinNeighborhood()
569 
570 
571 //--------------------------------------------------------------------------
572 template <typename Coord>
573 template <typename PointIter>
574 std::vector<size_t>
576  (PointIter begin, PointIter end) const
577 {
578  //
579  // reference implementation: brute force dumb one
580  //
581 
582  std::vector<size_t> nonIsolated;
583 
584  size_t i = 0;
585  for (auto it = begin; it != end; ++it, ++i) {
586 
587  for (auto ioth = begin; ioth != end; ++ioth) {
588  if (it == ioth) continue;
589 
590  if (closeEnough(*it, *ioth)) {
591  nonIsolated.push_back(i);
592  break;
593  }
594 
595  } // for oth
596 
597  } // for (it)
598 
599  return nonIsolated;
600 } // lar::example::PointIsolationAlg::bruteRemoveIsolatedPoints()
601 
602 
603 //--------------------------------------------------------------------------
604 template <typename Coord>
605 template <typename Point>
607  (Point const& A, Point const& B) const
608 {
609  return cet::sum_of_squares(
613  ) <= config.radius2;
614 } // lar::example::PointIsolationAlg<Point>::closeEnough()
615 
616 
617 //--------------------------------------------------------------------------
618 template <typename Coord>
620  (Coord_t from, Coord_t to)
621  { return "(" + std::to_string(from) + " to " + std::to_string(to) + ")"; }
622 
623 
624 //--------------------------------------------------------------------------
625 
626 #endif // LAREXAMPLES_ALGORITHMS_REMOVEISOLATEDSPACEPOINTS_POINTISOLATIONALG_H
Algorithm to detect isolated space points.
A container of points sorted in cells.
std::ptrdiff_t CellIndexOffset_t
type of difference between indices
CellIndexOffset_t CellDimIndex_t
type of difference between indices along a dimension
static std::string rangeString(Range_t range)
Helper function. Returns a string "(<from> to <to>)"
Int_t B
Definition: plot.C:25
Coord Coord_t
Type of coordinate.
Coord_t radius2
square of isolation radius [cm^2]
size_t maxMemory
grid smaller than this number of bytes (100 MiB)
typename IndexManager_t::LinIndex_t CellIndex_t
type of index for direct access to the cell
Range_t rangeY
range in Y of the covered volume
static std::string rangeString(Coord_t from, Coord_t to)
Helper function. Returns a string "(<from> to <to>)"
bool closeEnough(Point const &A, Point const &B) const
Returns whether A and B are close enough to be considered non-isolated.
NeighAddresses_t buildNeighborhood(Indexer_t const &indexer, unsigned int neighExtent) const
Returns a list of cell offsets for the neighbourhood of given radius.
Class to organise data into a 3D grid.
Range_t rangeX
range in X of the covered volume
bool has(CellIndexOffset_t ofs) const
Returns whether there is a cell with the specified index (signed!)
Coord_t upper
upper boundary
std::array< CellDimIndex_t, dims()> CellID_t
type of cell coordinate (x, y, z)
GridContainerIndicesBase3D<> GridContainer3DIndices
Index manager for a container of data arranged on a 3D grid.
std::array< size_t, 3 > diceVolume(CoordRangeCells< Coord > const &rangeX, CoordRangeCells< Coord > const &rangeY, CoordRangeCells< Coord > const &rangeZ)
Returns the dimensions of a grid diced with the specified size.
Coord_t lower
lower boundary
auto extractPositionY(Point const &point)
std::vector< Indexer_t::CellIndexOffset_t > NeighAddresses_t
type of neighbourhood cell offsets
bool valid() const
Returns whether the range is valid (empty is also valid)
std::vector< size_t > bruteRemoveIsolatedPoints(PointIter begin, PointIter end) const
Brute-force reference algorithm.
std::tuple< double, double, const reco::ClusterHit3D * > Point
Definitions used by the VoronoiDiagram algorithm.
Definition: DCEL.h:34
decltype(*PointIter()) Point_t
PointIsolationAlg(Configuration_t const &first_config)
Constructor with configuration validation.
Type containing all configuration parameters of the algorithm.
std::vector< evd::details::RawDigitInfo_t >::const_iterator begin(RawDigitCacheDataClass const &cache)
Double_t R
Configuration_t & configuration() const
typename Grid_t::Cell_t Cell_t
type of cell
Index manager for a container of data arranged on a >=3-dim grid.
Double_t radius
CellIndexOffset_t offset(CellID_t const &origin, CellID_t const &cellID) const
Returns the difference in index of cellID respect to origin.
static void validateConfiguration(Configuration_t const &config)
std::vector< size_t > removeIsolatedPoints(Cont const &points) const
Returns the set of points that are not isolated.
std::string to_string(Flag_t< Storage > const flag)
Convert a flag into a stream (shows its index).
Definition: BitMask.h:187
LArSoft-specific namespace.
Range_t rangeZ
range in Z of the covered volume
auto extractPositionX(Point const &point)
bool isPointIsolatedFrom(Point_t< PointIter > const &point, typename Partition_t< PointIter >::Cell_t const &otherPoints) const
Returns whether a point is isolated with respect to all the others.
Configuration_t config
all configuration data
std::vector< size_t > removeIsolatedPoints(PointIter begin, PointIter end) const
Returns the set of points that are not isolated.
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
bool isPointIsolatedWithinNeighborhood(Partition_t< PointIter > const &partition, Indexer_t::CellIndex_t cellIndex, Point_t< PointIter > const &point, NeighAddresses_t const &neighList) const
Returns whether a point is isolated in the specified neighbourhood.
void reconfigure(Configuration_t const &new_config)
static Coord_t maximumOptimalCellSize(Coord_t radius)
Returns the maximum optimal cell size when using a isolation radius.
Coord_t computeCellSize() const
Computes the cell size to be used.
auto extractPositionZ(Point const &point)