Mantid
Loading...
Searching...
No Matches
CrossCorrelate.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 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
16#include "MantidAPI/TextAxis.h"
24#include <boost/iterator/counting_iterator.hpp>
25#include <numeric>
26
27namespace {
28struct Variances {
29 double y;
30 double e;
31};
32
33Variances subtractMean(std::vector<double> &signal, std::vector<double> &error) {
34 double mean = std::accumulate(signal.cbegin(), signal.cend(), 0.0);
35 double errorMeanSquared =
36 std::accumulate(error.cbegin(), error.cend(), 0.0, Mantid::Kernel::VectorHelper::SumSquares<double>());
37 const auto n = signal.size();
38 mean /= static_cast<double>(n);
39 errorMeanSquared /= static_cast<double>(n * n);
40
41 double variance = 0.0, errorVariance = 0.0;
42 auto itY = signal.begin();
43 auto itE = error.begin();
44 for (; itY != signal.end(); ++itY, ++itE) {
45 (*itY) -= mean; // Now the vector is (y[i]-refMean)
46 (*itE) = (*itE) * (*itE) + errorMeanSquared; // New error squared
47 const double t = (*itY) * (*itY); //(y[i]-refMean)^2
48 variance += t; // Sum previous term
49 errorVariance += 4.0 * t * (*itE); // Error squared
50 }
51 return {variance, errorVariance};
52}
53
54} // namespace
55
56namespace Mantid::Algorithms {
57
58// Register the class into the algorithm factory
59DECLARE_ALGORITHM(CrossCorrelate)
60
61using namespace Kernel;
62using namespace API;
63using namespace DataObjects;
64using namespace HistogramData;
65
68 auto wsValidator = std::make_shared<CompositeValidator>();
69 wsValidator->add<API::WorkspaceUnitValidator>("dSpacing");
70 wsValidator->add<API::HistogramValidator>();
71 wsValidator->add<API::RawCountValidator>();
72
73 // Input and output workspaces
75 std::make_unique<WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::Input, wsValidator),
76 "A 2D workspace with X values of d-spacing");
77 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("OutputWorkspace", "", Direction::Output),
78 "The name of the output workspace");
79
80 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
81 mustBePositive->setLower(0);
82 // Reference spectra against which cross correlation is performed
83 declareProperty("ReferenceSpectra", 0, mustBePositive,
84 "The Workspace Index of the spectra to correlate all other "
85 "spectra against. ");
86 // Spectra in the range [min to max] will be cross correlated to referenceSpectra.
87 declareProperty("WorkspaceIndexMin", 0, mustBePositive,
88 "The workspace index of the first member of the range of "
89 "spectra to cross-correlate against.");
90 declareProperty("WorkspaceIndexMax", 0, mustBePositive,
91 " The workspace index of the last member of the range of "
92 "spectra to cross-correlate against.");
93 // Alternatively to min and max index, a list of indices can be supplied
94 declareProperty(std::make_unique<ArrayProperty<size_t>>("WorkspaceIndexList"),
95 "A comma-separated list of individual workspace indices of "
96 "spectra to cross-correlate against.");
97 // Only the data in the range X_min, X_max will be used
98 declareProperty("XMin", 0.0, "The starting point of the region to be cross correlated.");
99 declareProperty("XMax", 0.0, "The ending point of the region to be cross correlated.");
100 // max is .1
101 declareProperty("MaxDSpaceShift", EMPTY_DBL(), "Optional float for maximum shift to calculate (in d-spacing)");
102}
103
105std::map<std::string, std::string> CrossCorrelate::validateInputs() {
106 std::map<std::string, std::string> helpMessages;
107
108 // Unless a list was specified, check that workspace index min and max make sense
109 if (isDefault("WorkspaceIndexList")) {
110 const int wsIndexMin = getProperty("WorkspaceIndexMin");
111 const int wsIndexMax = getProperty("WorkspaceIndexMax");
112 if (wsIndexMin >= wsIndexMax) {
113 helpMessages["WorkspaceIndexMin"] = "Must specify WorkspaceIndexMin < WorkspaceIndexMax";
114 helpMessages["WorkspaceIndexMax"] = "Must specify WorkspaceIndexMin < WorkspaceIndexMax";
115 }
116 }
117
118 // Valid input is either min and max workspace index OR list but not both
119 if (!isDefault("WorkspaceIndexList") && (!isDefault("WorkspaceIndexMin") || !isDefault("WorkspaceIndexMax"))) {
120 const std::string msg = "Must specify either WorkspaceIndexMin and WorkspaceIndexMax, "
121 "or WorkspaceIndexList, but not both.";
122 helpMessages["WorkspaceIndexMin"] = msg;
123 helpMessages["WorkspaceIndexMax"] = msg;
124 helpMessages["WorkspaceIndexList"] = msg;
125 }
126
127 // Check that the data range specified makes sense
128 const double xmin = getProperty("XMin");
129 const double xmax = getProperty("XMax");
130 if (xmin >= xmax) {
131 helpMessages["XMin"] = "Must specify XMin < XMax";
132 helpMessages["XMax"] = "Must specify XMin < XMax";
133 }
134
135 return helpMessages;
136}
137
143 MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
144 const double maxDSpaceShift = getProperty("MaxDSpaceShift");
145 const int referenceSpectra = getProperty("ReferenceSpectra");
146 const double xmin = getProperty("XMin");
147 const double xmax = getProperty("XMax");
148
149 const auto index_ref = static_cast<size_t>(referenceSpectra);
150
151 // Get indices of spectra either based on min and max index or from a list
152 std::vector<size_t> indexes = getProperty("WorkspaceIndexList");
153 if (indexes.empty()) {
154 const int wsIndexMin = getProperty("WorkspaceIndexMin");
155 const int wsIndexMax = getProperty("WorkspaceIndexMax");
156 indexes.reserve(static_cast<size_t>(wsIndexMax - wsIndexMin + 1)); // validated in validateInputs
157 std::copy(boost::make_counting_iterator(wsIndexMin), boost::make_counting_iterator(wsIndexMax + 1),
158 std::back_inserter(indexes));
159 }
160 const int numSpectra = static_cast<int>(indexes.size());
161
162 // Output message information
163 g_log.information() << "There are " << numSpectra << " spectra in the range\n";
164
165 // Take a copy of the referenceSpectra spectrum
166 auto &referenceSpectraE = inputWS->e(index_ref);
167 auto &referenceSpectraX = inputWS->x(index_ref);
168 auto &referenceSpectraY = inputWS->y(index_ref);
169 // Now check if the range between x_min and x_max is valid
170 using std::placeholders::_1;
171 auto rangeStart =
172 std::find_if(referenceSpectraX.cbegin(), referenceSpectraX.cend(), std::bind(std::greater<double>(), _1, xmin));
173 if (rangeStart == referenceSpectraX.cend())
174 throw std::runtime_error("No data above XMin");
175 auto rangeEnd = std::find_if(rangeStart, referenceSpectraX.cend(), std::bind(std::greater<double>(), _1, xmax));
176 if (rangeStart == rangeEnd)
177 throw std::runtime_error("Range is not valid");
178
179 MantidVec::difference_type rangeStartCorrection = std::distance(referenceSpectraX.cbegin(), rangeStart);
180 MantidVec::difference_type rangeEndCorrection = std::distance(referenceSpectraX.cbegin(), rangeEnd);
181
182 const std::vector<double> referenceXVector(rangeStart, rangeEnd);
183 std::vector<double> referenceYVector(referenceSpectraY.cbegin() + rangeStartCorrection,
184 referenceSpectraY.cbegin() + (rangeEndCorrection - 1));
185 std::vector<double> referenceEVector(referenceSpectraE.cbegin() + rangeStartCorrection,
186 referenceSpectraE.cbegin() + (rangeEndCorrection - 1));
187
188 g_log.information() << "min max " << referenceXVector.front() << " " << referenceXVector.back() << '\n';
189
190 // Now start the real stuff
191 // Create a 2DWorkspace that will hold the result
192 const auto numReferenceY = static_cast<int>(referenceYVector.size());
193
194 // max the shift
195 int shiftCorrection = 0;
196 if (maxDSpaceShift != EMPTY_DBL()) {
197 if (xmax - xmin < maxDSpaceShift)
198 g_log.warning() << "maxDSpaceShift(" << std::to_string(maxDSpaceShift)
199 << ") is larger than specified range of xmin(" << xmin << ") to xmax(" << xmax
200 << "), please make it smaller or removed it entirely!"
201 << "\n";
202
203 // convert dspacing to bins, where maxDSpaceShift is at least 0.1
204 const auto maxBins = std::max(0.0 + maxDSpaceShift * 2, 0.1) / inputWS->getDimension(0)->getBinWidth();
205 // calc range based on max bins
206 shiftCorrection = static_cast<int>(std::max(0.0, abs((-numReferenceY + 2) - (numReferenceY - 2)) - maxBins)) / 2;
207 }
208
209 const int numPoints = 2 * (numReferenceY - shiftCorrection) - 3;
210 if (numPoints < 1)
211 throw std::runtime_error("Range is not valid");
212
213 MatrixWorkspace_sptr out = create<HistoWorkspace>(*inputWS, numSpectra, Points(static_cast<size_t>(numPoints)));
214
215 const auto referenceVariance = subtractMean(referenceYVector, referenceEVector);
216
217 const double referenceNorm = 1.0 / sqrt(referenceVariance.y);
218 double referenceNormE = 0.5 * pow(referenceNorm, 3) * sqrt(referenceVariance.e);
219
220 // Now copy the other spectra
221 const bool isDistribution = inputWS->isDistribution();
222
223 auto &outX = out->mutableX(0);
224 // this has to be signed to allow for negative values
225 for (int i = 0; i < static_cast<int>(outX.size()); ++i) {
226 outX[i] = static_cast<double>(i - (numReferenceY - shiftCorrection) + 2);
227 }
228
229 // Initialise the progress reporting object
230 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, numSpectra);
231 PARALLEL_FOR_IF(Kernel::threadSafe(*inputWS, *out))
232 for (int currentSpecIndex = 0; currentSpecIndex < numSpectra; ++currentSpecIndex) // Now loop on all spectra
233 {
235 const size_t currentSpecIndex_szt = static_cast<size_t>(currentSpecIndex);
236 const size_t wsIndex = indexes[currentSpecIndex_szt]; // Get the ws index from the table
237 // Copy spectra info from input Workspace
238 out->getSpectrum(currentSpecIndex_szt).copyInfoFrom(inputWS->getSpectrum(wsIndex));
239 out->setSharedX(currentSpecIndex_szt, out->sharedX(0));
240 // Get temp referenceSpectras
241 const auto &inputXVector = inputWS->x(wsIndex);
242 const auto &inputYVector = inputWS->y(wsIndex);
243 const auto &inputEVector = inputWS->e(wsIndex);
244 // Copy Y,E data of spec(currentSpecIndex) to temp vector
245 // Now rebin on the grid of referenceSpectra
246 std::vector<double> tempY(static_cast<size_t>(numReferenceY));
247 std::vector<double> tempE(static_cast<size_t>(numReferenceY));
248
249 VectorHelper::rebin(inputXVector.rawData(), inputYVector.rawData(), inputEVector.rawData(), referenceXVector, tempY,
250 tempE, isDistribution);
251 const auto tempVar = subtractMean(tempY, tempE);
252
253 // Calculate the normalisation constant
254 const double tempNorm = 1.0 / sqrt(tempVar.y);
255 const double tempNormE = 0.5 * pow(tempNorm, 3) * sqrt(tempVar.e);
256 const double normalisation = referenceNorm * tempNorm;
257 const double normalisationE2 = pow((referenceNorm * tempNormE), 2) + pow((tempNorm * referenceNormE), 2);
258
259 // Get referenceSpectr to the ouput spectrum
260 auto &outY = out->mutableY(currentSpecIndex_szt);
261 auto &outE = out->mutableE(currentSpecIndex_szt);
262
263 for (int dataIndex = -numReferenceY + 2 + shiftCorrection; dataIndex <= numReferenceY - 2 - shiftCorrection;
264 ++dataIndex) {
265 const auto dataIndexMagnitude = static_cast<int>(abs(dataIndex));
266 const auto dataIndexPositive = bool(dataIndex >= 0);
267 double val = 0, err2 = 0;
268 // loop over bin number
269 if (dataIndexPositive) {
270 for (int binIndex = numReferenceY - 1 - dataIndexMagnitude; binIndex >= 0; --binIndex) {
271 const auto x = referenceYVector[binIndex];
272 const auto y = tempY[binIndex + dataIndexMagnitude];
273 const auto xE = referenceEVector[binIndex];
274 const auto yE = tempE[binIndex + dataIndexMagnitude];
275 val += (x * y);
276 err2 += x * x * yE + y * y * xE;
277 }
278 } else {
279 for (int binIndex = numReferenceY - 1 - dataIndexMagnitude; binIndex >= 0; --binIndex) {
280 const auto x = tempY[binIndex];
281 const auto y = referenceYVector[binIndex + dataIndexMagnitude];
282 const auto xE = tempE[binIndex];
283 const auto yE = referenceEVector[binIndex + dataIndexMagnitude];
284 val += (x * y);
285 err2 += x * x * yE + y * y * xE;
286 }
287 }
288 const size_t dataIndex_corrected = static_cast<size_t>(dataIndex + numReferenceY - shiftCorrection - 2);
289 outY[dataIndex_corrected] = (val * normalisation);
290 outE[dataIndex_corrected] = sqrt(val * val * normalisationE2 + normalisation * normalisation * err2);
291 }
292 // Update progress information
293 m_progress->report();
295 }
297
298 out->getAxis(0)->unit() = UnitFactory::Instance().create("Label");
299 Unit_sptr unit = out->getAxis(0)->unit();
300 std::shared_ptr<Units::Label> label = std::dynamic_pointer_cast<Units::Label>(unit);
301 label->setLabel("Bins of Shift", "\\mathbb{Z}");
302
303 setProperty("OutputWorkspace", out);
304}
305
306} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double error
#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...
#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_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
bool isDefault(const std::string &name) const
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
A validator which checks that a workspace contains raw counts in its bins.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
void init() override
Initialisation code.
std::unique_ptr< API::Progress > m_progress
Progress reporting.
std::map< std::string, std::string > validateInputs() override
Input validation.
void exec() override
Execution code.
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 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< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
void MANTID_KERNEL_DLL rebin(const std::vector< double > &xold, const std::vector< double > &yold, const std::vector< double > &eold, const std::vector< double > &xnew, std::vector< double > &ynew, std::vector< double > &enew, bool distribution, bool addition=false)
Rebins data according to a new output X array.
std::shared_ptr< Unit > Unit_sptr
Shared pointer to the Unit base class.
Definition Unit.h:194
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54
Functor to accumulate a sum of squares.