Mantid
Loading...
Searching...
No Matches
SpatialGrouping.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 +
8
11
15
16#include <map>
17
18#include <fstream>
19
20#include "MantidAPI/ISpectrum.h"
21#include <algorithm>
22
23namespace {
24/*
25 * Comparison operator for use in std::sort when dealing with a vector of
26 * std::pair<int,double> where int is DetectorID and double is distance from
27 * centre point.
28 * Needs to be outside of SpatialGrouping class because of the way STL handles
29 * passing functions as arguments.
30 * @param left :: element to compare
31 * @param right :: element to compare
32 * @return true if left should come before right in the order
33 */
34static bool compareIDPair(const std::pair<int64_t, Mantid::Kernel::V3D> &left,
35 const std::pair<int64_t, Mantid::Kernel::V3D> &right) {
36 return (left.second.norm() < right.second.norm());
37}
38} // namespace
39
40namespace Mantid::Algorithms {
41// Register the algorithm into the AlgorithmFactory
42DECLARE_ALGORITHM(SpatialGrouping)
43
44
47void SpatialGrouping::init() {
48 declareProperty(
49 std::make_unique<Mantid::API::WorkspaceProperty<>>("InputWorkspace", "", Mantid::Kernel::Direction::Input),
50 "Name of the input workspace, which is used only as a means "
51 "of retrieving the instrument geometry.");
52 declareProperty(std::make_unique<Mantid::API::FileProperty>("Filename", "", Mantid::API::FileProperty::Save, ".xml"),
53 "Name (and location) in which to save the file. Having a "
54 "suffix of ''.xml'' is recommended.");
55 declareProperty("SearchDistance", 2.5,
56 "The number of pixel widths in which "
57 "to search for neighbours of the "
58 "detector.");
59 declareProperty("GridSize", 3,
60 "The size of the grid that should be grouped. "
61 "i.e, 3 (the default) will select a group of "
62 "nine detectors centred in a 3 by 3 grid.");
63}
64
69 API::MatrixWorkspace_const_sptr inputWorkspace = getProperty("InputWorkspace");
70 double searchDist = getProperty("SearchDistance");
71 int gridSize = getProperty("GridSize");
72 size_t nNeighbours = (gridSize * gridSize) - 1;
73
74 m_positions.clear();
75 const auto &spectrumInfo = inputWorkspace->spectrumInfo();
76 for (size_t i = 0; i < inputWorkspace->getNumberHistograms(); ++i) {
77 const auto &spec = inputWorkspace->getSpectrum(i);
78 m_positions[spec.getSpectrumNo()] = spectrumInfo.position(i);
79 }
80
81 // TODO: There is a confusion in this algorithm between detector IDs and
82 // spectrum numbers!
83
84 Mantid::API::Progress prog(this, 0.0, 1.0, m_positions.size());
85
86 bool ignoreMaskedDetectors = false;
87 m_neighbourInfo = std::make_unique<API::WorkspaceNearestNeighbourInfo>(*inputWorkspace, ignoreMaskedDetectors);
88
89 for (size_t i = 0; i < inputWorkspace->getNumberHistograms(); ++i) {
90 prog.report();
91
92 const auto &spec = inputWorkspace->getSpectrum(i);
93 specnum_t specNo = spec.getSpectrumNo();
94
95 // We are not interested in Monitors and we don't want them to be included
96 // in
97 // any of the other lists
98 if (spectrumInfo.isMonitor(i)) {
99 m_included.insert(specNo);
100 continue;
101 }
102
103 // Or detectors already flagged as included in a group
104 auto inclIt = m_included.find(specNo);
105 if (inclIt != m_included.end()) {
106 continue;
107 }
108
109 std::map<detid_t, Mantid::Kernel::V3D> nearest;
110
111 const double empty = EMPTY_DBL();
112 Mantid::Geometry::BoundingBox bbox(empty, empty, empty, empty, empty, empty);
113
114 createBox(spectrumInfo.detector(i), bbox, searchDist);
115
116 bool extend = true;
117 while ((nNeighbours > nearest.size()) && extend) {
118 extend = expandNet(nearest, specNo, nNeighbours, bbox);
119 }
120
121 if (nearest.size() != nNeighbours)
122 continue;
123
124 // if we've gotten to this point, we want to go and make the group list.
125 std::vector<int> group;
126 m_included.insert(specNo);
127 // Add the central spectrum
128 group.emplace_back(specNo);
129
130 // Add all the nearest neighbors
131 std::map<specnum_t, Mantid::Kernel::V3D>::iterator nrsIt;
132 for (nrsIt = nearest.begin(); nrsIt != nearest.end(); ++nrsIt) {
133 m_included.insert(nrsIt->first);
134 group.emplace_back(nrsIt->first);
135 }
136 m_groups.emplace_back(group);
137 }
138
139 if (m_groups.empty()) {
140 g_log.warning() << "No groups generated.\n";
141 return;
142 }
143
144 // Create grouping XML file
145 g_log.information() << "Creating XML Grouping File.\n";
146 std::vector<std::vector<detid_t>>::iterator grpIt;
147 std::ofstream xml;
148 std::string fname = getPropertyValue("Filename");
149
150 // Check to see whether we need to append .xml to the name.
151 size_t fnameXMLappend = fname.find(".xml");
152 if (fnameXMLappend == std::string::npos) {
153 fnameXMLappend = fname.find(".XML"); // check both 'xml' and 'XML'
154 if (fnameXMLappend == std::string::npos)
155 fname = fname + ".xml";
156 }
157
158 // set the property again so the user can retrieve the stored result.
159 setPropertyValue("Filename", fname);
160
161 xml.open(fname.c_str());
162
163 xml << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
164 << "<!-- XML Grouping File created by SpatialGrouping Algorithm -->\n"
165 << "<detector-grouping>\n";
166
167 int grpID = 1;
168 for (grpIt = m_groups.begin(); grpIt != m_groups.end(); ++grpIt) {
169 xml << "<group name=\"group" << grpID++ << "\"><detids val=\"" << (*grpIt)[0];
170 for (size_t i = 1; i < (*grpIt).size(); i++) {
171 // The following lines replace: std::vector<detid_t> detIds =
172 // smap.getDetectors((*grpIt)[i]);
173 size_t workspaceIndex = inputWorkspace->getIndexFromSpectrumNumber((*grpIt)[i]);
174 const auto &detIds = inputWorkspace->getSpectrum(workspaceIndex).getDetectorIDs();
175 for (auto detId : detIds) {
176 xml << "," << detId;
177 }
178 }
179 xml << "\"/></group>\n";
180 }
181
182 xml << "</detector-grouping>";
183
184 xml.close();
185
186 g_log.information() << "Finished creating XML Grouping File.\n";
187}
188
201bool SpatialGrouping::expandNet(std::map<specnum_t, Mantid::Kernel::V3D> &nearest, specnum_t spec,
202 const size_t noNeighbours, const Mantid::Geometry::BoundingBox &bbox) {
203 const size_t incoming = nearest.size();
204
205 std::map<specnum_t, Mantid::Kernel::V3D> potentials;
206
207 // Special case for first run for this detector
208 if (incoming == 0) {
209 potentials = m_neighbourInfo->getNeighbours(spec, 0.0);
210 } else {
211 for (auto &nrsIt : nearest) {
212 std::map<specnum_t, Mantid::Kernel::V3D> results;
213 results = m_neighbourInfo->getNeighbours(nrsIt.first, 0.0);
214 for (auto &result : results) {
215 potentials[result.first] = result.second;
216 }
217 }
218 }
219
220 for (auto &potential : potentials) {
221 // We do not want to include the detector in it's own list of nearest
222 // neighbours
223 if (potential.first == spec) {
224 continue;
225 }
226
227 // Or detectors that are already in the nearest list passed into this
228 // function
229 auto nrsIt = nearest.find(potential.first);
230 if (nrsIt != nearest.end()) {
231 continue;
232 }
233
234 // We should not include detectors already included in a group (or monitors
235 // for that matter)
236 auto inclIt = m_included.find(potential.first);
237 if (inclIt != m_included.end()) {
238 continue;
239 }
240
241 // If we get this far, we need to determine if the detector is of a suitable
242 // distance
243 Mantid::Kernel::V3D pos = m_positions[potential.first];
244 if (!bbox.isPointInside(pos)) {
245 continue;
246 }
247
248 // Elsewise, it's all good.
249 nearest[potential.first] = potential.second;
250 }
251
252 if (nearest.size() == incoming) {
253 return false;
254 }
255
256 if (nearest.size() > noNeighbours) {
257 sortByDistance(nearest, noNeighbours);
258 }
259
260 return true;
261}
262
271void SpatialGrouping::sortByDistance(std::map<detid_t, Mantid::Kernel::V3D> &nearest, const size_t noNeighbours) {
272 std::vector<std::pair<detid_t, Mantid::Kernel::V3D>> order(nearest.begin(), nearest.end());
273
274 std::sort(order.begin(), order.end(), compareIDPair);
275
276 size_t current = order.size();
277 size_t lose = current - noNeighbours;
278 if (lose < 1)
279 return;
280
281 for (size_t i = 1; i <= lose; i++) {
282 nearest.erase(order[current - i].first);
283 }
284}
297void SpatialGrouping::createBox(const Geometry::IDetector &det, Geometry::BoundingBox &bndbox, double searchDist) {
298
299 // We may have DetectorGroups here
300 // Unfortunately, IDetector doesn't contain the
301 // std::shared_ptr<Mantid::Geometry::Detector> detector =
302 // std::dynamic_pointer_cast<Mantid::Geometry::Detector>(det);
303
305 det.getBoundingBox(bbox);
306
307 double xmax = bbox.xMax();
308 double ymax = bbox.yMax();
309 double zmax = bbox.zMax();
310 double xmin = bbox.xMin();
311 double ymin = bbox.yMin();
312 double zmin = bbox.zMin();
313
314 double factor = 2.0 * searchDist;
315
316 growBox(xmin, xmax, factor);
317 growBox(ymin, ymax, factor);
318 growBox(zmin, zmax, factor);
319
320 bndbox = Mantid::Geometry::BoundingBox(xmax, ymax, zmax, xmin, ymin, zmin);
321}
322
330void SpatialGrouping::growBox(double &min, double &max, const double factor) {
331 double rng = max - min;
332 double mid = (max + min) / 2.0;
333 double halfwid = rng / 2.0;
334 min = mid - (factor * halfwid);
335 max = mid + (factor * halfwid);
336}
337
338} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double left
Definition: LineProfile.cpp:80
double right
Definition: LineProfile.cpp:81
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
void setPropertyValue(const std::string &name, const std::string &value) override
Set the value of a property by string N.B.
Definition: Algorithm.cpp:1975
@ Save
to specify a file to write to, the file may or may not exist
Definition: FileProperty.h:49
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
A property class for workspaces.
This algorithm creates an XML Grouping File for use in the GroupDetectors (v2) or ReadGroupsFromFile ...
bool expandNet(std::map< specnum_t, Mantid::Kernel::V3D > &nearest, specnum_t spec, const size_t noNeighbours, const Mantid::Geometry::BoundingBox &bbox)
expand our search out to the next neighbours along
std::map< specnum_t, Kernel::V3D > m_positions
map of detectors in the instrument
void exec() override
Execution code.
std::vector< std::vector< int > > m_groups
first and last values for each group
void createBox(const Geometry::IDetector &det, Geometry::BoundingBox &bndbox, double searchDist)
create expanded bounding box for our purposes
std::unique_ptr< API::WorkspaceNearestNeighbourInfo > m_neighbourInfo
NearestNeighbourInfo used by expandNet()
std::set< specnum_t > m_included
flag which detectors are included in a group already
void growBox(double &min, double &max, const double factor)
grow dimensions of our bounding box to the factor
void sortByDistance(std::map< specnum_t, Mantid::Kernel::V3D > &nearest, const size_t noNeighbours)
sort by distance
A simple structure that defines an axis-aligned cuboid shaped bounding box for a geometrical object.
Definition: BoundingBox.h:34
bool isPointInside(const Kernel::V3D &point) const
Is the given point within the bounding box?
Definition: BoundingBox.cpp:28
double xMax() const
Return the maximum value of X.
Definition: BoundingBox.h:80
double zMin() const
Return the minimum value of Z.
Definition: BoundingBox.h:86
double zMax() const
Return the maximum value of Z.
Definition: BoundingBox.h:88
double yMax() const
Return the maximum value of Y.
Definition: BoundingBox.h:84
double xMin() const
Return the minimum value of X.
Definition: BoundingBox.h:78
double yMin() const
Return the minimum value of Y.
Definition: BoundingBox.h:82
virtual void getBoundingBox(BoundingBox &boundingBox) const =0
Get the bounding box for this component and store it in the given argument.
Interface class for detector objects.
Definition: IDetector.h:43
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
Class for 3D vectors.
Definition: V3D.h:34
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
int32_t specnum_t
Typedef for a spectrum Number.
Definition: IDTypes.h:16
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
@ Input
An input workspace.
Definition: Property.h:53