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"
24namespace Mantid::Algorithms {
25
26// Register the algorithm into the AlgorithmFactory
27DECLARE_ALGORITHM(FindCenterOfMassPosition2)
28
29using namespace Kernel;
30using namespace API;
31using namespace Geometry;
32using namespace DataObjects;
33
35 const auto wsValidator = std::make_shared<CompositeValidator>();
36 const auto positiveDouble = std::make_shared<BoundedValidator<double>>();
37
38 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, wsValidator));
39 declareProperty("Output", "",
40 "TableWorkspace will contain the center of mass position. "
41 "When empty, a CenterOfMass output parameter with two elements (x,y) is created.");
42
43 declareProperty("CenterX", 0.0, "Initial estimate for the beam center in X in meters");
44 declareProperty("CenterY", 0.0, "Initial estimate for the beam center in Y in meters");
45 declareProperty("Tolerance", 0.00125,
46 "Tolerance on the center of mass position between each iteration in meters. "
47 "Suggested value is the size of a quarter of a pixel.");
48
49 declareProperty("DirectBeam", true,
50 "When true, the calculation will include the pixels within BeamRadius from the beam center. "
51 "Since the process is iterative, the pixels masked by DirectBeam=False will move.");
52
53 positiveDouble->setLower(0);
54 declareProperty("BeamRadius", 0.0155, positiveDouble,
55 "Radius of the direct beam area, in meters, used the exclude the beam when calculating "
56 "the center of mass of the scattering pattern. "
57 "This is ignored when DirectBeam=True");
58 declareProperty("IntegrationRadius", EMPTY_DBL(), positiveDouble,
59 "Integration radius, in meters, used to include when calculating "
60 "the center of mass of the scattering pattern.");
61}
62
72 double &centerY, Progress &progress) {
73 const double tolerance = getProperty("Tolerance");
74 const bool includeDirectBeam = getProperty("DirectBeam");
75 const double beamRadius = getProperty("BeamRadius");
76 // when the integration radius isn't set, put the integration radius to 100m which is effectively infinity
77 const double integrationRadius = isDefault("IntegrationRadius") ? 100. : getProperty("IntegrationRadius");
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 in:
83 WorkspaceBoundingBox boundingBox(inputWS, integrationRadius, beamRadius, !includeDirectBeam, centerX, centerY);
84
85 // Initialize book-keeping
86 // distance between previous and current beam center
87 double distanceFromPrevious = std::numeric_limits<double>::max();
88 // previous distance between previous and current beam center
89 double distanceFromPreviousPrevious = std::numeric_limits<double>::max();
90
91 int totalLocalMinima = 0;
92 int totalIterations = 0;
93 constexpr int LOCAL_MINIMA_MAX{5};
94
95 // Find center of mass and iterate until we converge
96 // to within the tolerance specified in meters
97 while (distanceFromPrevious > tolerance) {
98 // Normalize output to find center of mass position
99 // Compute the distance to the previous iteration
100 distanceFromPrevious = boundingBox.distanceFromPrevious();
101
102 // skip out early if the center found is within the ignore region
103 if (boundingBox.centerOfMassWithinBeamCenter()) {
104 g_log.error("Center of mass falls within the beam center area: stopping here");
105 break;
106 }
107
108 // Recenter around new mass position
109 boundingBox.prepareCenterCalculation();
110
111 // Check to see if we have the same result
112 // as the previous iteration
113 if (Kernel::equals(distanceFromPrevious, distanceFromPreviousPrevious)) {
114 totalLocalMinima++;
115 } else {
116 totalLocalMinima = 0;
117 }
118
119 // Quit if we found the exact same distance five times in a row.
120 if (totalLocalMinima > LOCAL_MINIMA_MAX) {
121 g_log.warning() << "Found the same or equivalent center of mass locations more than " << LOCAL_MINIMA_MAX
122 << " times in a row: stopping here\n";
123 break;
124 }
125
126 // Quit if we haven't converged after the maximum number of iterations.
127 if (++totalIterations > m_maxIteration) {
128 g_log.warning() << "More than " << m_maxIteration << " iteration to find beam center: stopping here\n";
129 break;
130 }
131
132 distanceFromPreviousPrevious = distanceFromPrevious;
133
134 // Count histogram for normalization
135 boundingBox.findNewCenterPosition();
136
137 progress.report("Find Beam Center");
138 }
139
140 // get the final result
141 centerX = boundingBox.getCenterX();
142 centerY = boundingBox.getCenterY();
143}
144
151void FindCenterOfMassPosition2::storeOutputWorkspace(double centerX, double centerY) {
152 std::string output = getProperty("Output");
153
154 // If an output workspace name was given, create a TableWorkspace with the
155 // results,
156 // otherwise use an ArrayProperty
157 if (!output.empty()) {
158 // Store the result in a table workspace
160 std::make_unique<WorkspaceProperty<API::ITableWorkspace>>("OutputWorkspace", "", Direction::Output));
161
162 // Set the name of the new workspace
163 setPropertyValue("OutputWorkspace", output);
164
165 Mantid::API::ITableWorkspace_sptr m_result = std::make_shared<TableWorkspace>();
166 m_result->addColumn("str", "Name");
167 m_result->addColumn("double", "Value");
168
169 Mantid::API::TableRow row = m_result->appendRow();
170 row << "X (m)" << centerX;
171 row = m_result->appendRow();
172 row << "Y (m)" << centerY;
173
174 setProperty("OutputWorkspace", m_result);
175 } else {
176 // Store the results using an ArrayProperty
177 if (!existsProperty("CenterOfMass"))
178 declareProperty(std::make_unique<ArrayProperty<double>>("CenterOfMass", std::make_shared<NullValidator>(),
180 std::vector<double> center_of_mass;
181 center_of_mass.emplace_back(centerX);
182 center_of_mass.emplace_back(centerY);
183 setProperty("CenterOfMass", center_of_mass);
184 }
185
186 g_log.information() << "Center of Mass found at x=" << centerX << " y=" << centerY << '\n';
187}
188
190 const MatrixWorkspace_sptr inputWSWvl = getProperty("InputWorkspace");
191 double centerX = getProperty("CenterX");
192 double centerY = getProperty("CenterY");
193
194 MatrixWorkspace_sptr inputWS;
195
196 // Sum up all the wavelength bins
197 IAlgorithm_sptr childAlg = createChildAlgorithm("Integration", 0., 0.5); // first half is integrating
198 childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", inputWSWvl);
199 childAlg->executeAsChildAlg();
200 inputWS = childAlg->getProperty("OutputWorkspace");
201
202 // Set up the progress reporting object
203 Progress progress(this, 0.5, 1.0, m_maxIteration);
204
205 findCenterOfMass(inputWS, centerX, centerY, progress);
206 storeOutputWorkspace(centerX, centerY);
207}
208
209} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
double tolerance
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
bool existsProperty(const std::string &name) const override
Checks whether the named property is already in the list of managed property.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
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.
Kernel::Logger & g_log
Definition Algorithm.h:423
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
bool isDefault(const std::string &name) const
void setPropertyValue(const std::string &name, const std::string &value) override
Set the value of a property by string N.B.
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, 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 centerOfMassWithinBeamCenter()
This only has effect if the integral is ignoring the beam center as a whole.
void prepareCenterCalculation()
Copy the current center to the previous and update the x/y range for overall integration.
Support for a property that holds an array of values.
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:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
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
MANTID_KERNEL_DLL bool equals(T const x, T const y)
Test for equality of doubles using compiler-defined precision.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
@ Input
An input workspace.
Definition Property.h:54
@ Output
An output workspace.
Definition Property.h:55