Mantid
Loading...
Searching...
No Matches
FindCenterOfMassPosition2.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 +
10#include "MantidAPI/TableRow.h"
22namespace Mantid::Algorithms {
23
24// Register the algorithm into the AlgorithmFactory
25DECLARE_ALGORITHM(FindCenterOfMassPosition2)
26
27using namespace Kernel;
28using namespace API;
29using namespace Geometry;
30using namespace DataObjects;
31
32namespace {
33bool equals(const double a, const double b) { // anonymous namespace
34 return fabs(a - b) < std::numeric_limits<double>::min();
35} // anonymous namespace
36} // namespace
37
39 const auto wsValidator = std::make_shared<CompositeValidator>();
40 const auto positiveDouble = std::make_shared<BoundedValidator<double>>();
41
42 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, wsValidator));
43 declareProperty("Output", "",
44 "If not empty, a table workspace of that "
45 "name will contain the center of mass position.");
46
47 declareProperty("CenterX", 0.0, "Estimate for the beam center in X [m]. Default: 0");
48 declareProperty("CenterY", 0.0, "Estimate for the beam center in Y [m]. Default: 0");
49 declareProperty("Tolerance", 0.00125,
50 "Tolerance on the center of mass "
51 "position between each iteration [m]. "
52 "Default: 0.00125");
53
54 declareProperty("DirectBeam", true,
55 "If true, a direct beam calculation will be performed. Otherwise, the "
56 "center of mass "
57 "of the scattering data will be computed by excluding the beam area.");
58
59 positiveDouble->setLower(0);
60 declareProperty("BeamRadius", 0.0155, positiveDouble,
61 "Radius of the beam area, in meters, used the exclude the "
62 "beam when calculating "
63 "the center of mass of the scattering pattern.");
64}
65
66// find the min/max for x/y coords in set of valid spectra, update position of bounding box
67double initBoundingBox(WorkspaceBoundingBox &boundingBox, const int numSpec, const double beamRadius,
68 const bool directBeam) {
69 double totalCount = 0;
70 for (int i = 0; i < numSpec; i++) {
71 if (!boundingBox.isValidWs(i))
72 continue;
73
74 boundingBox.updateMinMax(i);
75
76 if (!boundingBox.isOutOfBoundsOfNonDirectBeam(beamRadius, i, directBeam))
77 continue;
78 else
79 totalCount += boundingBox.updatePositionAndReturnCount(i);
80 }
81 return totalCount;
82}
83
84// In subsequent iterations check if spectra fits in the normalized bounding box(generated by previous iterations)
85// If so, update position
86double updateBoundingBox(WorkspaceBoundingBox &boundingBox, WorkspaceBoundingBox &previousBoundingBox,
87 const int numSpec, const double beamRadius, const bool directBeam) {
88 double totalCount = 0;
89 const auto &spectrumInfo = boundingBox.getWorkspace()->spectrumInfo();
90 for (int i = 0; i < numSpec; i++) {
91 if (!boundingBox.isValidWs(i))
92 continue;
93
94 const V3D position = spectrumInfo.position(i);
95
96 if (previousBoundingBox.containsPoint(position.X(), position.Y())) {
97 if (!boundingBox.isOutOfBoundsOfNonDirectBeam(beamRadius, i, directBeam))
98 continue;
99 else
100 totalCount += boundingBox.updatePositionAndReturnCount(i);
101 }
102 }
103 return totalCount;
104}
105
116 double &centerY, const int numSpec, Progress &progress) {
117 const double tolerance = getProperty("Tolerance");
118 const bool directBeam = getProperty("DirectBeam");
119 const double beamRadius = getProperty("BeamRadius");
120
121 // Define box around center of mass so that only pixels in an area
122 // _centered_ on the latest center position are considered. At each
123 // iteration we will recompute the bounding box, and we will make
124 // it as large as possible. The largest box is defined in:
125 WorkspaceBoundingBox boundingBox(inputWS);
126 boundingBox.setCenter(centerX, centerY);
127
128 // Starting values for the bounding box and the center
129 WorkspaceBoundingBox previousBoundingBox;
130 previousBoundingBox.setBounds(0., 0., 0., 0.);
131
132 // Initialize book-keeping
133 double distance = -1;
134 double distanceCheck = 0;
135 double totalCount = initBoundingBox(boundingBox, numSpec, beamRadius, directBeam);
136
137 int totalLocalMinima = 0;
138 int totalIterations = 0;
139
140 // Find center of mass and iterate until we converge
141 // to within the tolerance specified in meters
142 while (distance > tolerance || distance < 0) {
143 // Normalize output to find center of mass position
144 boundingBox.normalizePosition(totalCount, totalCount);
145 // Compute the distance to the previous iteration
146 distance = boundingBox.calculateDistance();
147 // Recenter around new mass position
148 double radiusX = boundingBox.calculateRadiusX();
149 double radiusY = boundingBox.calculateRadiusY();
150
151 if (!directBeam && (radiusX <= beamRadius || radiusY <= beamRadius)) {
152 g_log.error() << "Center of mass falls within the beam center area: "
153 "stopping here\n";
154 break;
155 }
156
157 boundingBox.setCenter(boundingBox.getX(), boundingBox.getY());
158 const auto oldCenterX = boundingBox.getCenterX();
159 const auto oldCenterY = boundingBox.getCenterY();
160 previousBoundingBox.setBounds(oldCenterX - radiusX, oldCenterX + radiusX, oldCenterY - radiusY,
161 oldCenterY + radiusY);
162
163 // Check to see if we have the same result
164 // as the previous iteration
165 if (equals(distance, distanceCheck)) {
166 totalLocalMinima++;
167 } else {
168 totalLocalMinima = 0;
169 }
170
171 // Quit if we found the exact same distance five times in a row.
172 if (totalLocalMinima > 5) {
173 g_log.warning() << "Found the same or equivalent center of mass locations "
174 "more than 5 times in a row: stopping here\n";
175 break;
176 }
177
178 // Quit if we haven't converged after the maximum number of iterations.
179 if (++totalIterations > m_maxIteration) {
180 g_log.warning() << "More than " << m_maxIteration << " iteration to find beam center: stopping here\n";
181 break;
182 }
183
184 distanceCheck = distance;
185
186 // Count histogram for normalization
187 boundingBox.setPosition(0, 0);
188 totalCount = updateBoundingBox(boundingBox, previousBoundingBox, numSpec, beamRadius, directBeam);
189
190 progress.report("Find Beam Center");
191 }
192 centerX = boundingBox.getCenterX();
193 centerY = boundingBox.getCenterY();
194}
195
202void FindCenterOfMassPosition2::storeOutputWorkspace(double centerX, double centerY) {
203 std::string output = getProperty("Output");
204
205 // If an output workspace name was given, create a TableWorkspace with the
206 // results,
207 // otherwise use an ArrayProperty
208 if (!output.empty()) {
209 // Store the result in a table workspace
211 std::make_unique<WorkspaceProperty<API::ITableWorkspace>>("OutputWorkspace", "", Direction::Output));
212
213 // Set the name of the new workspace
214 setPropertyValue("OutputWorkspace", output);
215
216 Mantid::API::ITableWorkspace_sptr m_result = std::make_shared<TableWorkspace>();
217 m_result->addColumn("str", "Name");
218 m_result->addColumn("double", "Value");
219
220 Mantid::API::TableRow row = m_result->appendRow();
221 row << "X (m)" << centerX;
222 row = m_result->appendRow();
223 row << "Y (m)" << centerY;
224
225 setProperty("OutputWorkspace", m_result);
226 } else {
227 // Store the results using an ArrayProperty
228 if (!existsProperty("CenterOfMass"))
229 declareProperty(std::make_unique<ArrayProperty<double>>("CenterOfMass", std::make_shared<NullValidator>(),
231 std::vector<double> center_of_mass;
232 center_of_mass.emplace_back(centerX);
233 center_of_mass.emplace_back(centerY);
234 setProperty("CenterOfMass", center_of_mass);
235 }
236
237 g_log.information() << "Center of Mass found at x=" << centerX << " y=" << centerY << '\n';
238}
239
241 const MatrixWorkspace_sptr inputWSWvl = getProperty("InputWorkspace");
242 double centerX = getProperty("CenterX");
243 double centerY = getProperty("CenterY");
244
245 MatrixWorkspace_sptr inputWS;
246
247 // Sum up all the wavelength bins
248 IAlgorithm_sptr childAlg = createChildAlgorithm("Integration", 0., 0.5); // first half is integrating
249 childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputWSWvl);
250 childAlg->executeAsChildAlg();
251 inputWS = childAlg->getProperty("OutputWorkspace");
252
253 // Get the number of monitors. We assume that all monitors are stored in the
254 // first spectra
255 const auto numSpec = static_cast<int>(inputWSWvl->getNumberHistograms());
256
257 // Set up the progress reporting object
258 Progress progress(this, 0.5, 1.0, m_maxIteration);
259
260 findCenterOfMass(inputWS, centerX, centerY, numSpec, progress);
261 storeOutputWorkspace(centerX, centerY);
262}
263
264} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double position
Definition: GetAllEi.cpp:154
#define fabs(x)
Definition: Matrix.cpp:22
double tolerance
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
bool existsProperty(const std::string &name) const override
Checks whether the named property is already in the list of managed property.
Definition: Algorithm.cpp:2008
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Definition: Algorithm.cpp:842
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
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.
void findCenterOfMass(const API::MatrixWorkspace_sptr &inputWS, double &centerX, double &centerY, const int numSpec, API::Progress &progress)
Helper functions.
void storeOutputWorkspace(double centerX, double centerY)
Package the algorithm outputs one of two ways depending on whether or not it was given an input Event...
bool isValidWs(int index) const
Performs checks on the spectrum located at index to determine if it is acceptable to be operated on.
void updateMinMax(int index)
Compare current mins and maxs to the coordinates of the spectrum at index expnd mins and maxs to incl...
bool isOutOfBoundsOfNonDirectBeam(const double beamRadius, int index, const bool directBeam)
Checks to see if spectrum at index is within the diameter of the given beamRadius.
void setBounds(double xMin, double xMax, double yMin, double yMax)
void normalizePosition(double x, double y)
Perform normalization on x/y coords over given values.
double updatePositionAndReturnCount(int index)
Sets member variables x/y to new x/y based on spectrum info and historgram data at the given index.
bool containsPoint(double x, double y)
Checks if a given x/y coord is within the bounding box.
API::MatrixWorkspace_const_sptr getWorkspace()
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
Class for 3D vectors.
Definition: V3D.h:34
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
bool MANTID_API_DLL equals(const MatrixWorkspace_sptr &lhs, const MatrixWorkspace_sptr &rhs, double tolerance=0.0)
Performs a comparison operation on two workspaces, using the CompareWorkspaces algorithm.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
double updateBoundingBox(WorkspaceBoundingBox &boundingBox, WorkspaceBoundingBox &previousBoundingBox, const int numSpec, const double beamRadius, const bool directBeam)
double initBoundingBox(WorkspaceBoundingBox &boundingBox, const int numSpec, const double beamRadius, const bool directBeam)
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54