Mantid
Loading...
Searching...
No Matches
FindClusterFaces.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
12#include "MantidAPI/TableRow.h"
18#include "MantidKernel/Utils.h"
19
21
22using namespace Mantid::Kernel;
23using namespace Mantid::Geometry;
24using namespace Mantid::API;
27
28namespace {
29using namespace Mantid::Crystal;
30// Map of label ids to peak index in peaks workspace.
31using LabelMap = std::map<int, int>;
32// Optional set of labels
33using OptionalLabelPeakIndexMap = boost::optional<LabelMap>;
34
44OptionalLabelPeakIndexMap createOptionalLabelFilter(size_t dimensionality, int emptyLabelId,
45 const IPeaksWorkspace_sptr &filterWorkspace,
46 IMDHistoWorkspace_sptr &clusterImage) {
47 OptionalLabelPeakIndexMap optionalAllowedLabels;
48
49 if (filterWorkspace) {
50 if (dimensionality < 3) {
51 throw std::invalid_argument("A FilterWorkspace has been given, but the "
52 "dimensionality of the labeled workspace is "
53 "< 3.");
54 }
55 LabelMap allowedLabels;
56 PeakClusterProjection projection(clusterImage);
57
58 for (int i = 0; i < filterWorkspace->getNumberPeaks(); ++i) {
59 Mantid::Geometry::IPeak &peak = filterWorkspace->getPeak(i);
60 const auto labelIdAtPeakCenter = static_cast<int>(projection.signalAtPeakCenter(peak));
61 if (labelIdAtPeakCenter > emptyLabelId) {
62 allowedLabels.emplace(labelIdAtPeakCenter, i);
63 }
64 }
65 optionalAllowedLabels = allowedLabels;
66 }
67 return optionalAllowedLabels;
68}
69
73struct ClusterFace {
74 int clusterId;
75 size_t workspaceIndex;
76 int faceNormalDimension;
77 bool maxEdge;
78 double radius;
79};
80
81using ClusterFaces = std::deque<ClusterFace>;
82using VecClusterFaces = std::vector<ClusterFaces>;
83
90void checkDataPoint(const size_t &linearIndex, const double signalValue) {
91 double intPart;
92
93 // Check that the signal value looks like a label id.
94 if (std::modf(signalValue, &intPart) != 0.0) {
95 std::stringstream buffer;
96 buffer << "Problem at linear index: " << linearIndex << " SignalValue is not an integer: " << signalValue
97 << " Suggests wrong input IMDHistoWorkspace passed to "
98 "FindClusterFaces.";
99
100 throw std::runtime_error(buffer.str());
101 }
102}
103
117void findFacesAtIndex(const size_t linearIndex, IMDIterator *mdIterator, IMDHistoWorkspace_sptr &clusterImage,
118 const double &radius, const int &id, const int &emptyLabelId,
119 const std::vector<size_t> &imageShape, ClusterFaces &localClusterFaces) {
120 auto indexes = Mantid::Kernel::Utils::getIndicesFromLinearIndex(linearIndex, imageShape);
121
122 const auto neighbours = mdIterator->findNeighbourIndexesFaceTouching();
123 for (auto neighbourLinearIndex : neighbours) {
124 const auto neighbourId = static_cast<int>(clusterImage->getSignalAt(neighbourLinearIndex));
125
126 if (neighbourId <= emptyLabelId) {
127 // We have an edge!
128
129 // In which dimension is the edge?
130 auto neighbourIndexes = Mantid::Kernel::Utils::getIndicesFromLinearIndex(neighbourLinearIndex, imageShape);
131 for (size_t j = 0; j < imageShape.size(); ++j) {
132 if (indexes[j] != neighbourIndexes[j]) {
133 const bool maxEdge = neighbourLinearIndex > linearIndex;
134
135 ClusterFace face;
136 face.clusterId = id;
137 face.workspaceIndex = linearIndex;
138 face.faceNormalDimension = static_cast<int>(j);
139 face.maxEdge = maxEdge;
140 face.radius = radius;
141
142 localClusterFaces.emplace_back(face);
143 }
144 }
145 }
146 }
147}
148
160void executeUnFiltered(IMDIterator *mdIterator, ClusterFaces &localClusterFaces, Progress &progress,
161 IMDHistoWorkspace_sptr &clusterImage, const std::vector<size_t> &imageShape) {
162 const int emptyLabelId = 0;
163 const double radius = -1;
164 do {
165 const Mantid::signal_t signalValue = mdIterator->getSignal();
166 const auto id = static_cast<int>(signalValue);
167
168 if (id > emptyLabelId) {
169 const size_t linearIndex = mdIterator->getLinearIndex();
170
171 // Sanity check the signal value.
172 checkDataPoint(linearIndex, signalValue);
173
174 progress.report();
175
176 // Find faces
177 findFacesAtIndex(linearIndex, mdIterator, clusterImage, radius, id, emptyLabelId, imageShape, localClusterFaces);
178 }
179 } while (mdIterator->next());
180}
181
196void executeFiltered(IMDIterator *mdIterator, ClusterFaces &localClusterFaces, Progress &progress,
197 IMDHistoWorkspace_sptr &clusterImage, const std::vector<size_t> &imageShape,
198 PeaksWorkspace_sptr &filterWorkspace, const OptionalLabelPeakIndexMap &optionalAllowedLabels) {
199 const int emptyLabelId = 0;
200 PeakClusterProjection projection(clusterImage);
201 do {
202 const Mantid::signal_t signalValue = mdIterator->getSignal();
203 const auto id = static_cast<int>(signalValue);
204
205 auto it = optionalAllowedLabels->find(id);
206 if (it != optionalAllowedLabels->end()) {
207
208 if (id > emptyLabelId) {
209
210 const size_t linearIndex = mdIterator->getLinearIndex();
211
212 // Sanity check data.
213 checkDataPoint(linearIndex, signalValue);
214
215 // Find the peak center
216 const int &peakIndex = it->second;
217 const IPeak &peak = filterWorkspace->getPeak(peakIndex);
218 V3D peakCenter = projection.peakCenter(peak);
219
220 // Calculate the radius
221 VMD positionND = clusterImage->getCenter(mdIterator->getLinearIndex());
222 V3D cellPosition(positionND[0], positionND[1], positionND[2]);
223 double radius = cellPosition.distance(peakCenter);
224
225 progress.report();
226
227 // Find faces
228 findFacesAtIndex(linearIndex, mdIterator, clusterImage, radius, id, emptyLabelId, imageShape,
229 localClusterFaces);
230 }
231 }
232
233 } while (mdIterator->next());
234}
235} // namespace
236
237namespace Mantid::Crystal {
238
239// Register the algorithm into the AlgorithmFactory
241
242//----------------------------------------------------------------------------------------------
244const std::string FindClusterFaces::name() const { return "FindClusterFaces"; }
245
247int FindClusterFaces::version() const { return 1; }
248
250const std::string FindClusterFaces::category() const { return "Crystal\\Integration"; }
251
252//----------------------------------------------------------------------------------------------
253
254//----------------------------------------------------------------------------------------------
258 declareProperty(std::make_unique<WorkspaceProperty<IMDHistoWorkspace>>("InputWorkspace", "", Direction::Input),
259 "An input image workspace consisting of cluster ids.");
260
261 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("FilterWorkspace", "", Direction::Input,
263 "Optional filtering peaks workspace. Used to restrict face finding to "
264 "clusters in image which correspond to peaks in the workspace.");
265
266 declareProperty("LimitRows", true, "Limit the report output to a maximum number of rows");
267
268 declareProperty(std::make_unique<PropertyWithValue<int>>("MaximumRows", 100000,
269 std::make_shared<BoundedValidator<int>>(), Direction::Input),
270 "The number of neighbours to utilise. Defaults to 100000.");
271 setPropertySettings("MaximumRows", std::make_unique<EnabledWhenProperty>("LimitRows", IS_DEFAULT));
272
273 declareProperty(std::make_unique<WorkspaceProperty<ITableWorkspace>>("OutputWorkspace", "", Direction::Output),
274 "An output table workspace containing cluster face information.");
275
276 declareProperty(std::make_unique<PropertyWithValue<bool>>("TruncatedOutput", false, Direction::Output),
277 "Indicates that the output results were truncated if True");
278}
279
280//----------------------------------------------------------------------------------------------
284 IMDHistoWorkspace_sptr clusterImage = getProperty("InputWorkspace");
285 const int emptyLabelId = 0;
286
287 std::vector<size_t> imageShape;
288 const size_t dimensionality = clusterImage->getNumDims();
289 for (size_t i = 0; i < dimensionality; ++i) {
290 imageShape.emplace_back(clusterImage->getDimension(i)->getNBins());
291 }
292
293 // Get the peaks workspace
294 PeaksWorkspace_sptr filterWorkspace = this->getProperty("FilterWorkspace");
295
296 // Use the peaks workspace to filter to labels of interest
297 OptionalLabelPeakIndexMap optionalAllowedLabels =
298 createOptionalLabelFilter(dimensionality, emptyLabelId, filterWorkspace, clusterImage);
299
300 const int nThreads = Mantid::API::FrameworkManager::Instance().getNumOMPThreads(); // NThreads to Request
301 auto mdIterators = clusterImage->createIterators(nThreads); // Iterators
302 const auto nIterators = static_cast<int>(mdIterators.size()); // Number of iterators yielded.
303 VecClusterFaces clusterFaces(nIterators);
304 size_t nSteps = 1000;
305 if (optionalAllowedLabels.is_initialized()) {
306 nSteps = optionalAllowedLabels->size();
307 }
308 const bool usingFiltering = optionalAllowedLabels.is_initialized();
309
310 Progress progress(this, 0.0, 1.0, nSteps);
312 for (int it = 0; it < nIterators; ++it) {
314 ClusterFaces &localClusterFaces = clusterFaces[it];
315 auto mdIterator = mdIterators[it].get();
316
317 if (usingFiltering) {
318 executeFiltered(mdIterator, localClusterFaces, progress, clusterImage, imageShape, filterWorkspace,
319 optionalAllowedLabels);
320 } else {
321 executeUnFiltered(mdIterator, localClusterFaces, progress, clusterImage, imageShape);
322 }
324 }
326
327 const bool limitRows = getProperty("LimitRows");
328 const int maxRows = getProperty("MaximumRows");
329
330 // Create an output table workspace now that all local cluster faces have been
331 // found in parallel.
332 auto out = WorkspaceFactory::Instance().createTable("TableWorkspace");
333 out->addColumn("int", "ClusterId");
334 out->addColumn("double", "MDWorkspaceIndex");
335 out->addColumn("int", "FaceNormalDimension");
336 out->addColumn("bool", "MaxEdge");
337 out->addColumn("double", "Radius");
338 size_t totalFaces = 0;
339 for (int i = 0; i < nIterators; ++i) {
340 const ClusterFaces &localClusterFaces = clusterFaces[i];
341
342 for (const auto &clusterFace : localClusterFaces) {
343 if (!limitRows || (out->rowCount() < size_t(maxRows))) {
344 TableRow row = out->appendRow();
345 row << clusterFace.clusterId << double(clusterFace.workspaceIndex) << clusterFace.faceNormalDimension
346 << clusterFace.maxEdge << clusterFace.radius;
347 }
348 ++totalFaces;
349 }
350 }
351
352 bool truncatedOutput = false;
353 if (limitRows && out->rowCount() == size_t(maxRows)) {
354 truncatedOutput = true;
355 std::stringstream buffer;
356 buffer << "More faces found than can be reported given the MaximumRows "
357 "limit. Row limit at: "
358 << maxRows << " Total faces available: " << totalFaces;
359 g_log.warning(buffer.str());
360 }
361
362 setProperty("OutputWorkspace", out);
363 setProperty("TruncatedOutput", truncatedOutput);
364}
365
366} // namespace Mantid::Crystal
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
Definition: MultiThreaded.h:94
#define PARALLEL_FOR_NO_WSP_CHECK()
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
double radius
Definition: Rasterize.cpp:31
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
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 progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
This is an interface to an iterator of an IMDWorkspace.
Definition: IMDIterator.h:39
virtual std::vector< size_t > findNeighbourIndexesFaceTouching() const =0
Find neighbouring indexes face touching.
virtual bool next()=0
Advance to the next cell.
virtual signal_t getSignal() const =0
Returns the total signal for this box.
virtual size_t getLinearIndex() const =0
Get the linear index.
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
TableRow represents a row in a TableWorkspace.
Definition: TableRow.h:39
A property class for workspaces.
FindClusterFaces : Algorithm to find faces of clusters in an MDHistoWorkspace (image)
void exec() override
Execute the algorithm.
const std::string category() const override
Algorithm's category for identification.
void init() override
Initialize the algorithm's properties.
int version() const override
Algorithm's version for identification.
PeakClusterProjection : Maps peaks onto IMDHistoWorkspaces and returns the signal value at the peak c...
The class PeaksWorkspace stores information about a set of SCD peaks.
Structure describing a single-crystal peak.
Definition: IPeak.h:26
BoundedValidator is a validator that requires the values to be between upper or lower bounds,...
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Class for 3D vectors.
Definition: V3D.h:34
std::shared_ptr< IPeaksWorkspace > IPeaksWorkspace_sptr
shared pointer to Mantid::API::IPeaksWorkspace
std::shared_ptr< IMDHistoWorkspace > IMDHistoWorkspace_sptr
shared pointer to Mantid::API::IMDHistoWorkspace
std::shared_ptr< PeaksWorkspace > PeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
void getIndicesFromLinearIndex(const size_t linear_index, size_t const *const numBins, const size_t numDims, size_t *const out_indices)
Convert an linear index in nDim workspace into vector of loop indexes of nDim depth loop Unsafe (poin...
Definition: Utils.h:228
double signal_t
Typedef for the signal recorded in a MDBox, etc.
Definition: MDTypes.h:36
STL namespace.
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54