Mantid
Loading...
Searching...
No Matches
FindCenterOfMassPosition.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 +
11#include "MantidAPI/TableRow.h"
20
21namespace Mantid::Algorithms {
22
23// Register the algorithm into the AlgorithmFactory
24DECLARE_ALGORITHM(FindCenterOfMassPosition)
25
26using namespace Kernel;
27using namespace API;
28using namespace Geometry;
29using namespace DataObjects;
30
32 auto wsValidator = std::make_shared<CompositeValidator>();
33 wsValidator->add<WorkspaceUnitValidator>("Wavelength");
34 wsValidator->add<HistogramValidator>();
35 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, wsValidator));
36 declareProperty("Output", "",
37 "If not empty, a table workspace of that "
38 "name will contain the center of mass position.");
39
40 auto positiveInt = std::make_shared<BoundedValidator<int>>();
41 positiveInt->setLower(0);
42 declareProperty("NPixelX", 192, positiveInt, "Number of detector pixels in the X direction.");
43
44 positiveInt->setLower(0);
45 declareProperty("NPixelY", 192, positiveInt, "Number of detector pixels in the Y direction.");
46
47 declareProperty("DirectBeam", true,
48 "If true, a direct beam calculation will be performed. Otherwise, the "
49 "center of mass "
50 "of the scattering data will be computed by excluding the beam area.");
51
52 auto positiveDouble = std::make_shared<BoundedValidator<double>>();
53 positiveDouble->setLower(0);
54 declareProperty("BeamRadius", 20.0, positiveDouble,
55 "Radius of the beam area, in pixels, used the exclude the "
56 "beam when calculating "
57 "the center of mass of the scattering pattern.");
58}
59
61 MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
62
63 // Option to exclude beam area
64 bool direct_beam = getProperty("DirectBeam");
65
66 // TODO: Need an input for the X bin to use, assume 0 for now
67 int specID = 0;
68 // Need input for detector dimensions
69 int n_pixel_x = getProperty("NPixelX");
70 int n_pixel_y = getProperty("NPixelY");
71 // Iteration cutoff
72 int max_iteration = 200;
73 // Radius of the beam area, in pixels
74 double beam_radius = getProperty("BeamRadius");
75
76 // Set up the progress reporting object
77 Progress progress(this, 0.0, 1.0, max_iteration);
78
79 // Define box around center of mass so that only pixels in an area
80 // _centered_ on the latest center position are considered. At each
81 // iteration we will recompute the bounding box, and we will make
82 // it as large as possible. The largest box is defined as:
83 double xmin0 = 1.0;
84 double xmax0 = n_pixel_x - 2.0;
85 double ymin0 = 1.0;
86 double ymax0 = n_pixel_y - 2.0;
87
88 // Starting values for the bounding box and the center
89 double xmin = xmin0;
90 double xmax = xmax0;
91 double ymin = ymin0;
92 double ymax = ymax0;
93 double center_x = n_pixel_x / 2.0;
94 double center_y = n_pixel_y / 2.0;
95
96 // Initialize book-keeping
97 double distance = -1;
98 double distance_check = 0;
99 int n_local_minima = 0;
100 int n_iteration = 0;
101
102 // Get the number of monitors. We assume that all monitors are stored in the
103 // first spectra
104 int n_monitors = static_cast<int>(inputWS->getInstrument()->getMonitors().size());
105 const auto numSpec = static_cast<int>(inputWS->getNumberHistograms());
106
107 // Find center of mass and iterate until we converge
108 // to within a quarter of a pixel
109 while (distance > 0.25 || distance < 0) {
110 // Count histogram for normalization
111 double total_count = 0;
112 double position_x = 0;
113 double position_y = 0;
114
115 const auto &spectrumInfo = inputWS->spectrumInfo();
116 for (int i = 0; i < numSpec; i++) {
117 if (!spectrumInfo.hasDetectors(i)) {
118 g_log.warning() << "Workspace index " << i << " has no detector assigned to it - discarding\n";
119 continue;
120 }
121 // Skip if we have a monitor or if the detector is masked.
122 if (spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i))
123 continue;
124
125 // Get the current spectrum
126 const MantidVec &YIn = inputWS->readY(i);
127 auto y = static_cast<double>((i - n_monitors) % n_pixel_x);
128 double x = floor(static_cast<double>(i - n_monitors) / n_pixel_y);
129
130 if (x >= xmin && x <= xmax && y >= ymin && y <= ymax) {
131 if (!direct_beam) {
132 double dx = x - center_x;
133 double dy = y - center_y;
134 if (dx * dx + dy * dy < beam_radius * beam_radius)
135 continue;
136 }
137 position_x += YIn[specID] * x;
138 position_y += YIn[specID] * y;
139 total_count += YIn[specID];
140 }
141 }
142
143 // Normalize output to find center of mass position
144 position_x /= total_count;
145 position_y /= total_count;
146
147 // Compute the distance to the previous iteration
148 distance =
149 sqrt((center_x - position_x) * (center_x - position_x) + (center_y - position_y) * (center_y - position_y));
150
151 // Modify the bounding box around the detector region used to
152 // compute the center of mass so that it is centered around
153 // the new center of mass position.
154 double radius_x = std::min((position_x - xmin0), (xmax0 - position_x));
155 double radius_y = std::min((position_y - ymin0), (ymax0 - position_y));
156
157 if (!direct_beam && (radius_x <= beam_radius || radius_y <= beam_radius)) {
158 g_log.error() << "Center of mass falls within the beam center area: "
159 "stopping here\n";
160 break;
161 }
162
163 center_x = position_x;
164 center_y = position_y;
165
166 xmin = center_x - radius_x;
167 xmax = center_x + radius_x;
168 ymin = center_y - radius_y;
169 ymax = center_y + radius_y;
170
171 // Sanity check to avoid getting stuck
172 if (distance == distance_check) {
173 n_local_minima++;
174 } else {
175 n_local_minima = 0;
176 }
177
178 // Quit if we found the exact same distance five times in a row.
179 if (n_local_minima > 5) {
180 g_log.warning() << "Found the same or equivalent center of mass locations "
181 "more than 5 times in a row: stopping here\n";
182 break;
183 }
184
185 // Quit if we haven't converged after the maximum number of iterations.
186 if (++n_iteration > max_iteration) {
187 g_log.warning() << "More than " << max_iteration << " iteration to find beam center: stopping here\n";
188 break;
189 }
190
191 distance_check = distance;
192
193 progress.report();
194 }
195
196 std::string output = getProperty("Output");
197
198 // If an output workspace name was given, create a TableWorkspace with the
199 // results,
200 // otherwise use an ArrayProperty
201 if (!output.empty()) {
202 // Store the result in a table workspace
204 std::make_unique<WorkspaceProperty<API::ITableWorkspace>>("OutputWorkspace", "", Direction::Output));
205
206 // Set the name of the new workspace
207 setPropertyValue("OutputWorkspace", output);
208
209 Mantid::API::ITableWorkspace_sptr m_result = std::make_shared<TableWorkspace>();
210 m_result->addColumn("str", "Name");
211 m_result->addColumn("double", "Value");
212
213 Mantid::API::TableRow row = m_result->appendRow();
214 row << "X (m)" << center_x;
215 row = m_result->appendRow();
216 row << "Y (m)" << center_y;
217
218 setProperty("OutputWorkspace", m_result);
219 } else {
220 // Store the results using an ArrayProperty
222 std::make_unique<ArrayProperty<double>>("CenterOfMass", std::make_shared<NullValidator>(), Direction::Output));
223 std::vector<double> center_of_mass;
224 center_of_mass.emplace_back(center_x);
225 center_of_mass.emplace_back(center_y);
226 setProperty("CenterOfMass", center_of_mass);
227 }
228
229 g_log.information() << "Center of Mass found at x=" << center_x << " y=" << center_y << '\n';
230}
231
232} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
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
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
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
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.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
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
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
Definition: cow_ptr.h:172
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54