Mantid
Loading...
Searching...
No Matches
WorkspaceNearestNeighbours.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
12// Nearest neighbours library
13#include "MantidKernel/ANN/ANN.h"
15#include "MantidKernel/Timer.h"
16
17namespace Mantid {
18using namespace Geometry;
19namespace API {
20using Kernel::V3D;
21using Mantid::detid_t;
22
34 std::vector<specnum_t> spectrumNumbers,
35 bool ignoreMaskedDetectors)
36 : m_spectrumInfo(spectrumInfo), m_spectrumNumbers(std::move(spectrumNumbers)), m_noNeighbours(nNeighbours),
37 m_cutoff(std::numeric_limits<double>::lowest()), m_radius(0.), m_bIgnoreMaskedDetectors(ignoreMaskedDetectors) {
38 this->build(m_noNeighbours);
39}
40
47std::map<specnum_t, V3D> WorkspaceNearestNeighbours::neighbours(const specnum_t spectrum) const {
48 return defaultNeighbours(spectrum);
49}
50
59std::map<specnum_t, V3D> WorkspaceNearestNeighbours::neighboursInRadius(const specnum_t spectrum,
60 const double radius) const {
61 // If the radius is stupid then don't let it continue as well be stuck forever
62 if (radius < 0.0 || radius > 10.0) {
63 throw std::invalid_argument("NearestNeighbours::neighbours - Invalid radius parameter.");
64 }
65
66 std::map<specnum_t, V3D> result;
67 if (radius == 0.0) {
68 const int eightNearest = 8;
69 if (m_noNeighbours != eightNearest) {
70 // Note: Should be able to do this better but time constraints for the
71 // moment mean that
72 // it is necessary.
73 // Cast is necessary as the user should see this as a const member
74 const_cast<WorkspaceNearestNeighbours *>(this)->build(eightNearest);
75 }
76 result = defaultNeighbours(spectrum);
77 } else if (radius > m_cutoff && m_radius != radius) {
78 // We might have to see how efficient this ends up being.
79 int neighbours = m_noNeighbours + 1;
80 while (true) {
81 try {
82 const_cast<WorkspaceNearestNeighbours *>(this)->build(neighbours);
83 } catch (std::invalid_argument &) {
84 break;
85 }
86 if (radius < m_cutoff)
87 break;
88 else
89 neighbours += 1;
90 }
91 }
93
94 std::map<detid_t, V3D> nearest = defaultNeighbours(spectrum);
95 for (std::map<specnum_t, V3D>::const_iterator cit = nearest.begin(); cit != nearest.end(); ++cit) {
96 if (cit->second.norm() <= radius) {
97 result[cit->first] = cit->second;
98 }
99 }
100 return result;
101}
102
103//--------------------------------------------------------------------------
104// Private member functions
105//--------------------------------------------------------------------------
111void WorkspaceNearestNeighbours::build(const int noNeighbours) {
112 const auto indices = getSpectraDetectors();
113 if (indices.empty()) {
114 throw std::runtime_error("NearestNeighbours::build - Cannot find any spectra");
115 }
116 const auto nspectra = static_cast<int>(indices.size()); // ANN only deals with integers
117 if (noNeighbours >= nspectra) {
118 throw std::invalid_argument("NearestNeighbours::build - Invalid number of neighbours");
119 }
120
121 // Clear current
122 m_graph.clear();
123 m_specToVertex.clear();
124 m_noNeighbours = noNeighbours;
125
126 BoundingBox bbox;
127 // Base the scaling on the first detector, should be adequate but we can look
128 // at this
129 const auto &firstDet = m_spectrumInfo.detector(indices.front());
130 firstDet.getBoundingBox(bbox);
131 m_scale = V3D(bbox.width());
132 ANNpointArray dataPoints = annAllocPts(nspectra, 3);
133 MapIV pointNoToVertex;
134
135 int pointNo = 0;
136 for (const auto i : indices) {
137 const specnum_t spectrum = m_spectrumNumbers[i];
139 dataPoints[pointNo][0] = pos.X();
140 dataPoints[pointNo][1] = pos.Y();
141 dataPoints[pointNo][2] = pos.Z();
142 Vertex vertex = boost::add_vertex(spectrum, m_graph);
143 pointNoToVertex[pointNo] = vertex;
144 m_specToVertex[spectrum] = vertex;
145 ++pointNo;
146 }
147
148 auto annTree = std::make_unique<ANNkd_tree>(dataPoints, nspectra, 3);
149 pointNo = 0;
150 // Run the nearest neighbour search on each detector, reusing the arrays
151 // Set size initially to avoid array index error when testing in debug mode
152 std::vector<ANNidx> nnIndexList(m_noNeighbours);
153 std::vector<ANNdist> nnDistList(m_noNeighbours);
154
155 for (const auto idx : indices) {
156 ANNpoint scaledPos = dataPoints[pointNo];
157 annTree->annkSearch(scaledPos, // Point to search nearest neighbours of
158 m_noNeighbours, // Number of neighbours to find (8)
159 nnIndexList.data(), // Index list of results
160 nnDistList.data(), // List of distances to each of these
161 0.0 // Error bound (?) is this the radius to search in?
162 );
163 // The distances that are returned are in our scaled coordinate
164 // system. We store the real space ones.
165 const V3D realPos = V3D(scaledPos[0], scaledPos[1], scaledPos[2]) * m_scale;
166 for (int i = 0; i < m_noNeighbours; i++) {
167 ANNidx index = nnIndexList[i];
168 V3D neighbour = V3D(dataPoints[index][0], dataPoints[index][1], dataPoints[index][2]) * m_scale;
169 V3D distance = neighbour - realPos;
170 double separation = distance.norm();
171 boost::add_edge(m_specToVertex[m_spectrumNumbers[idx]], // from
172 pointNoToVertex[index], // to
173 distance, m_graph);
174 if (separation > m_cutoff) {
175 m_cutoff = separation;
176 }
177 }
178 pointNo++;
179 }
180 annDeallocPts(dataPoints);
181 annClose();
182 pointNoToVertex.clear();
183
184 m_vertexID = get(boost::vertex_name, m_graph);
185 m_edgeLength = get(boost::edge_name, m_graph);
186}
187
195std::map<specnum_t, V3D> WorkspaceNearestNeighbours::defaultNeighbours(const specnum_t spectrum) const {
196 auto vertex = m_specToVertex.find(spectrum);
197
198 if (vertex != m_specToVertex.end()) {
199 std::map<specnum_t, V3D> result;
200 std::pair<Graph::adjacency_iterator, Graph::adjacency_iterator> adjacent =
201 boost::adjacent_vertices(vertex->second, m_graph);
202 Graph::adjacency_iterator adjIt;
203 for (adjIt = adjacent.first; adjIt != adjacent.second; adjIt++) {
204 Vertex nearest = (*adjIt);
205 auto nrSpec = specnum_t(m_vertexID[nearest]);
206 std::pair<Graph::edge_descriptor, bool> nrEd = boost::edge(vertex->second, nearest, m_graph);
207 result[nrSpec] = m_edgeLength[nrEd.first];
208 }
209 return result;
210 } else {
211 throw Mantid::Kernel::Exception::NotFoundError("NearestNeighbours: Unable to find spectrum in vertex map",
212 spectrum);
213 }
214}
215
218 std::vector<size_t> indices;
219 const auto nSpec = m_spectrumNumbers.size();
220 indices.reserve(nSpec);
221 for (size_t i = 0; i < nSpec; ++i) {
222 // Always ignore monitors and ignore masked detectors if requested.
223 const bool heedMasking = m_bIgnoreMaskedDetectors && m_spectrumInfo.isMasked(i);
224 if (!m_spectrumInfo.isMonitor(i) && !heedMasking) {
225 indices.emplace_back(i);
226 }
227 }
228 return indices;
229}
230} // namespace API
231} // namespace Mantid
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
double radius
Definition: Rasterize.cpp:31
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
Definition: SpectrumInfo.h:53
bool isMonitor(const size_t index) const
Returns true if the detector(s) associated with the spectrum are monitors.
Kernel::V3D position(const size_t index) const
Returns the position of the spectrum with given index.
bool isMasked(const size_t index) const
Returns true if the detector(s) associated with the spectrum are masked.
const Geometry::IDetector & detector(const size_t index) const
Return a const reference to the detector or detector group of the spectrum with given index.
This class is not intended for direct use.
bool m_bIgnoreMaskedDetectors
Flag indicating that masked detectors should be ignored.
void build(const int noNeighbours)
Construct the graph based on the given number of neighbours and the current instument and spectra-det...
boost::property_map< Graph, boost::edge_name_t >::type m_edgeLength
property map holding the edge's related Distance value.
std::map< specnum_t, Mantid::Kernel::V3D > neighbours(specnum_t spectrum) const
Returns a map of the spectrum numbers to the distances for the nearest neighbours.
int m_noNeighbours
The current number of nearest neighbours.
double m_cutoff
The largest value of the distance to a nearest neighbour.
std::map< specnum_t, Mantid::Kernel::V3D > defaultNeighbours(const specnum_t spectrum) const
Query the graph for the default number of nearest neighbours to specified detector.
WorkspaceNearestNeighbours(int nNeighbours, const SpectrumInfo &spectrumInfo, std::vector< specnum_t > spectrumNumbers, bool ignoreMaskedDetectors=false)
Constructor.
boost::property_map< Graph, boost::vertex_name_t >::type m_vertexID
property map holding the node's related DetectorID's
boost::graph_traits< Graph >::vertex_descriptor Vertex
Vertex descriptor object for Graph.
double m_radius
Cached radius value. used to avoid uncessary recalculations.
std::unordered_map< specnum_t, Vertex > MapIV
map object of int to Graph Vertex descriptor
std::vector< size_t > getSpectraDetectors()
Returns the list of valid spectrum indices.
MapIV m_specToVertex
map between the DetectorID and the Graph node descriptor
std::map< specnum_t, Mantid::Kernel::V3D > neighboursInRadius(specnum_t spectrum, double radius=0.0) const
Returns a map of the spectrum numbers to the distances for the nearest neighbours.
const SpectrumInfo & m_spectrumInfo
A reference to the SpectrumInfo.
const std::vector< specnum_t > m_spectrumNumbers
Vector of spectrum numbers.
A simple structure that defines an axis-aligned cuboid shaped bounding box for a geometrical object.
Definition: BoundingBox.h:34
Kernel::V3D width() const
Returns the width of the box.
Definition: BoundingBox.h:98
virtual void getBoundingBox(BoundingBox &boundingBox) const =0
Get the bounding box for this component and store it in the given argument.
Exception for when an item is not found in a collection.
Definition: Exception.h:145
Class for 3D vectors.
Definition: V3D.h:34
constexpr double X() const noexcept
Get x.
Definition: V3D.h:232
constexpr double Y() const noexcept
Get y.
Definition: V3D.h:233
double norm() const noexcept
Definition: V3D.h:263
constexpr double Z() const noexcept
Get z.
Definition: V3D.h:234
Helper class which provides the Collimation Length for SANS instruments.
int32_t detid_t
Typedef for a detector ID.
Definition: SpectrumInfo.h:21
int32_t specnum_t
Typedef for a spectrum Number.
Definition: IDTypes.h:16
STL namespace.