Mantid
Loading...
Searching...
No Matches
SortHKL.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/Sample.h"
11#include "MantidAPI/TableRow.h"
12#include "MantidAPI/TextAxis.h"
22#include "MantidKernel/Utils.h"
23
24#include <cmath>
25#include <fstream>
26#include <numeric>
27
28using namespace Mantid::Geometry;
29using namespace Mantid::DataObjects;
30using namespace Mantid::Kernel;
31using namespace Mantid::API;
33
34namespace Mantid::Crystal {
35
36// Register the algorithm into the AlgorithmFactory
38
39SortHKL::SortHKL() : m_pointGroups(getAllPointGroups()), m_refConds(getAllReflectionConditions()) {}
40
41SortHKL::~SortHKL() = default;
42
44 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("InputWorkspace", "", Direction::Input),
45 "An input PeaksWorkspace with an instrument.");
46
47 /* TODO: These two properties with string lists keep appearing -
48 * Probably there should be a dedicated Property type or validator. */
49 std::vector<std::string> pgOptions;
50 pgOptions.reserve(2 * m_pointGroups.size() + 5);
51 std::transform(m_pointGroups.cbegin(), m_pointGroups.cend(), std::back_inserter(pgOptions),
52 [](const auto &group) { return group->getSymbol(); });
53 std::transform(m_pointGroups.cbegin(), m_pointGroups.cend(), std::back_inserter(pgOptions),
54 [](const auto &group) { return group->getName(); });
55 // Scripts may have Orthorhombic misspelled from past bug in PointGroupFactory
56 pgOptions.emplace_back("222 (Orthorombic)");
57 pgOptions.emplace_back("mm2 (Orthorombic)");
58 pgOptions.emplace_back("2mm (Orthorombic)");
59 pgOptions.emplace_back("m2m (Orthorombic)");
60 pgOptions.emplace_back("mmm (Orthorombic)");
61 declareProperty("PointGroup", pgOptions[0], std::make_shared<StringListValidator>(pgOptions),
62 "Which point group applies to this crystal?");
63
64 std::vector<std::string> centeringOptions;
65 centeringOptions.reserve(2 * m_refConds.size());
66 std::transform(m_refConds.cbegin(), m_refConds.cend(), std::back_inserter(centeringOptions),
67 [](const auto &condition) { return condition->getSymbol(); });
68 std::transform(m_refConds.cbegin(), m_refConds.cend(), std::back_inserter(centeringOptions),
69 [](const auto &condition) { return condition->getName(); });
70 declareProperty("LatticeCentering", centeringOptions[0], std::make_shared<StringListValidator>(centeringOptions),
71 "Appropriate lattice centering for the peaks.");
72
73 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("OutputWorkspace", "", Direction::Output),
74 "Output PeaksWorkspace");
75 declareProperty("OutputChi2", 0.0, "Chi-square is available as output", Direction::Output);
77 std::make_unique<WorkspaceProperty<ITableWorkspace>>("StatisticsTable", "StatisticsTable", Direction::Output),
78 "An output table workspace for the statistics of the peaks.");
79 declareProperty(std::make_unique<PropertyWithValue<std::string>>("RowName", "Overall", Direction::Input),
80 "name of row");
81 declareProperty("Append", false,
82 "Append to output table workspace if true.\n"
83 "If false, new output table workspace (default).");
84 const std::vector<std::string> equivTypes{"Mean", "Median"};
85 declareProperty("EquivalentIntensities", equivTypes.front(), std::make_shared<StringListValidator>(equivTypes),
86 "Replace intensities by mean(default), "
87 "or median.");
88 declareProperty(std::make_unique<PropertyWithValue<double>>("SigmaCritical", 3.0, Direction::Input),
89 "Removes peaks whose intensity deviates more than "
90 "SigmaCritical from the mean (or median).");
91 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("EquivalentsWorkspace", "EquivalentIntensities",
93 "Output Equivalent Intensities");
94 declareProperty("WeightedZScore", false,
95 "Use weighted ZScore if true.\n"
96 "If false, standard ZScore (default).");
97}
98
100 PeaksWorkspace_sptr inputPeaksWorkspace = getProperty("InputWorkspace");
101
102 const std::vector<Peak> &inputPeaks = inputPeaksWorkspace->getPeaks();
103 std::vector<Peak> peaks = getNonZeroPeaks(inputPeaks);
104
105 if (peaks.empty()) {
106 g_log.warning() << "Number of peaks should not be 0 for SortHKL.\n";
107 return;
108 }
109
110 UnitCell cell = inputPeaksWorkspace->sample().getOrientedLattice();
111
112 UniqueReflectionCollection uniqueReflections = getUniqueReflections(peaks, cell);
113 std::string equivalentIntensities = getPropertyValue("EquivalentIntensities");
114 double sigmaCritical = getProperty("SigmaCritical");
115 bool weightedZ = getProperty("WeightedZScore");
116
118 "Workspace2D", uniqueReflections.getReflections().size(), 20, 20);
119 int counter = 0;
120 size_t maxPeaks = 0;
121 auto tAxis = std::make_unique<TextAxis>(uniqueReflections.getReflections().size());
122 UniqWksp->getAxis(0)->unit() = UnitFactory::Instance().create("Wavelength");
123 for (const auto &unique : uniqueReflections.getReflections()) {
124 /* Since all possible unique reflections are explored
125 * there may be 0 observations for some of them.
126 * In that case, nothing can be done.*/
127
128 if (unique.second.count() > 2) {
129 tAxis->setLabel(counter, " " + unique.second.getHKL().toString());
130 auto &UniqX = UniqWksp->mutableX(counter);
131 auto &UniqY = UniqWksp->mutableY(counter);
132 auto &UniqE = UniqWksp->mutableE(counter);
133 counter++;
134 auto wavelengths = unique.second.getWavelengths();
135 auto intensities = unique.second.getIntensities();
136 g_log.debug() << "HKL " << unique.second.getHKL() << "\n";
137 g_log.debug() << "Intensities ";
138 for (const auto &e : intensities)
139 g_log.debug() << e << " ";
140 g_log.debug() << "\n";
141 std::vector<double> zScores;
142 if (!weightedZ) {
143 zScores = Kernel::getZscore(intensities);
144 } else {
145 auto sigmas = unique.second.getSigmas();
146 zScores = Kernel::getWeightedZscore(intensities, sigmas);
147 }
148
149 if (zScores.size() > maxPeaks)
150 maxPeaks = zScores.size();
151 // Possibly remove outliers.
152 auto outliersRemoved = unique.second.removeOutliers(sigmaCritical, weightedZ);
153
154 auto intensityStatistics =
155 Kernel::getStatistics(outliersRemoved.getIntensities(), StatOptions::Mean | StatOptions::Median);
156
157 g_log.debug() << "Mean = " << intensityStatistics.mean << " Median = " << intensityStatistics.median << "\n";
158 // sort wavelengths & intensities
159 for (size_t i = 0; i < wavelengths.size(); i++) {
160 size_t i0 = i;
161 for (size_t j = i + 1; j < wavelengths.size(); j++) {
162 if (wavelengths[j] < wavelengths[i0]) // Change was here!
163 {
164 i0 = j;
165 }
166 }
167 double temp = wavelengths[i0];
168 wavelengths[i0] = wavelengths[i];
169 wavelengths[i] = temp;
170 temp = intensities[i0];
171 intensities[i0] = intensities[i];
172 intensities[i] = temp;
173 }
174 g_log.debug() << "Zscores ";
175 for (size_t i = 0; i < std::min(zScores.size(), static_cast<size_t>(20)); ++i) {
176 UniqX[i] = wavelengths[i];
177 UniqY[i] = intensities[i];
178 if (zScores[i] > sigmaCritical)
179 UniqE[i] = intensities[i];
180 else if (equivalentIntensities == "Mean")
181 UniqE[i] = intensityStatistics.mean - intensities[i];
182 else
183 UniqE[i] = intensityStatistics.median - intensities[i];
184 g_log.debug() << zScores[i] << " ";
185 }
186 g_log.debug() << "\n";
187 }
188 }
189
190 if (counter > 0) {
191 MatrixWorkspace_sptr UniqWksp2 =
192 Mantid::API::WorkspaceFactory::Instance().create("Workspace2D", counter, maxPeaks, maxPeaks);
193 for (int64_t i = 0; i < counter; ++i) {
194 auto &outSpec = UniqWksp2->getSpectrum(i);
195 const auto &inSpec = UniqWksp->getSpectrum(i);
196 outSpec.setHistogram(inSpec.histogram());
197 // Copy the spectrum number/detector IDs
198 outSpec.copyInfoFrom(inSpec);
199 }
200 UniqWksp2->replaceAxis(1, std::move(tAxis));
201 setProperty("EquivalentsWorkspace", UniqWksp2);
202 } else {
203 setProperty("EquivalentsWorkspace", UniqWksp);
204 }
205
206 PeaksStatistics peaksStatistics(uniqueReflections, equivalentIntensities, sigmaCritical, weightedZ);
207
208 // Store the statistics for output.
209 const std::string tableName = getProperty("StatisticsTable");
210 ITableWorkspace_sptr statisticsTable = getStatisticsTable(tableName);
211 insertStatisticsIntoTable(statisticsTable, peaksStatistics);
212
213 // Store all peaks that were used to calculate statistics.
214
215 PeaksWorkspace_sptr outputPeaksWorkspace = getOutputPeaksWorkspace(inputPeaksWorkspace);
216
217 std::vector<Peak> &originalOutputPeaks = outputPeaksWorkspace->getPeaks();
218 originalOutputPeaks.swap(peaksStatistics.m_peaks);
219
220 sortOutputPeaksByHKL(outputPeaksWorkspace);
221
222 setProperty("OutputWorkspace", outputPeaksWorkspace);
223 setProperty("OutputChi2", peaksStatistics.m_chiSquared);
224 setProperty("StatisticsTable", statisticsTable);
225 AnalysisDataService::Instance().addOrReplace(tableName, statisticsTable);
226}
227
230std::vector<Peak> SortHKL::getNonZeroPeaks(const std::vector<Peak> &inputPeaks) const {
231 std::vector<Peak> peaks;
232 peaks.reserve(inputPeaks.size());
233
234 std::remove_copy_if(inputPeaks.begin(), inputPeaks.end(), std::back_inserter(peaks), [](const Peak &peak) {
235 return peak.getIntensity() <= 0.0 || peak.getSigmaIntensity() <= 0.0 || peak.getIntMNP() != V3D(0, 0, 0) ||
236 peak.getHKL() == V3D(0, 0, 0);
237 });
238
239 return peaks;
240}
241
253UniqueReflectionCollection SortHKL::getUniqueReflections(const std::vector<Peak> &peaks, const UnitCell &cell) const {
255 PointGroup_sptr pointGroup = getPointgroup();
256
257 std::pair<double, double> dLimits = getDLimits(peaks, cell);
258
259 UniqueReflectionCollection reflections(cell, dLimits, pointGroup, centering);
260 reflections.addObservations(peaks);
261
262 return reflections;
263}
264
267 ReflectionCondition_sptr centering = std::make_shared<ReflectionConditionPrimitive>();
268
269 const std::string refCondName = getPropertyValue("LatticeCentering");
270 const auto found = std::find_if(m_refConds.crbegin(), m_refConds.crend(),
271 [refCondName](const auto &condition) { return condition->getName() == refCondName; });
272 if (found != m_refConds.crend()) {
273 centering = *found;
274 }
275
276 return centering;
277}
278
282 PointGroup_sptr pointGroup = PointGroupFactory::Instance().createPointGroup("-1");
283
284 std::string pointGroupName = getPropertyValue("PointGroup");
285 size_t pos = pointGroupName.find("Orthorombic");
286 if (pos != std::string::npos) {
287 g_log.warning() << "Orthorhomic is misspelled in your script.\n";
288 pointGroupName.replace(pos, 11, "Orthorhombic");
289 g_log.warning() << "Please correct to " << pointGroupName << ".\n";
290 }
291 const auto found = std::find_if(m_pointGroups.crbegin(), m_pointGroups.crend(),
292 [&pointGroupName](const auto &group) { return group->getName() == pointGroupName; });
293 if (found != m_pointGroups.crend()) {
294 pointGroup = *found;
295 }
296
297 return pointGroup;
298}
299
303std::pair<double, double> SortHKL::getDLimits(const std::vector<Peak> &peaks, const UnitCell &cell) const {
304 auto dLimitIterators = std::minmax_element(peaks.begin(), peaks.end(), [cell](const Peak &lhs, const Peak &rhs) {
305 return cell.d(lhs.getHKL()) < cell.d(rhs.getHKL());
306 });
307
308 return std::make_pair(cell.d((*dLimitIterators.first).getHKL()), cell.d((*dLimitIterators.second).getHKL()));
309}
310
314 TableWorkspace_sptr tablews;
315
316 // Init or append to a table workspace
317 bool append = getProperty("Append");
318 if (append && AnalysisDataService::Instance().doesExist(name)) {
319 tablews = AnalysisDataService::Instance().retrieveWS<TableWorkspace>(name);
320 } else {
321 tablews = std::make_shared<TableWorkspace>();
322 tablews->addColumn("str", "Resolution Shell");
323 tablews->addColumn("int", "No. of Unique Reflections");
324 tablews->addColumn("double", "Resolution Min");
325 tablews->addColumn("double", "Resolution Max");
326 tablews->addColumn("double", "Multiplicity");
327 tablews->addColumn("double", "Mean ((I)/sd(I))");
328 tablews->addColumn("double", "Rmerge");
329 tablews->addColumn("double", "Rpim");
330 tablews->addColumn("double", "Data Completeness");
331 }
332
333 return tablews;
334}
335
339 if (!table) {
340 throw std::runtime_error("Can't store statistics into Null-table.");
341 }
342
343 std::string rowName = getProperty("RowName");
344 double completeness = 0.0;
345 if (rowName.substr(0, 4) != "bank") {
346 completeness = static_cast<double>(statistics.m_completeness);
347 }
348
349 // append to the table workspace
350 API::TableRow newrow = table->appendRow();
351
352 newrow << rowName << statistics.m_uniqueReflections << statistics.m_dspacingMin << statistics.m_dspacingMax
353 << statistics.m_redundancy << statistics.m_meanIOverSigma << 100.0 * statistics.m_rMerge
354 << 100.0 * statistics.m_rPim << 100.0 * completeness;
355}
356
359 PeaksWorkspace_sptr outputPeaksWorkspace = getProperty("OutputWorkspace");
360 if (outputPeaksWorkspace != inputPeaksWorkspace) {
361 outputPeaksWorkspace = inputPeaksWorkspace->clone();
362 }
363
364 return outputPeaksWorkspace;
365}
366
368void SortHKL::sortOutputPeaksByHKL(const IPeaksWorkspace_sptr &outputPeaksWorkspace) {
369 // Sort by HKL
370 std::vector<std::pair<std::string, bool>> criteria{{"H", true}, {"K", true}, {"L", true}};
371 outputPeaksWorkspace->sort(criteria);
372}
373
374} // namespace Mantid::Crystal
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
const std::vector< double > & rhs
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
A property class for workspaces.
The PeaksStatistics class is a small helper class that is used in SortHKL.
double m_meanIOverSigma
Average signal to noise ratio in the reflections.
double m_redundancy
Average number of observations for a unique reflection.
double m_dspacingMin
Lower d-spacing limit in the data set, sometimes referred to as upper resolution limit.
double m_rPim
Precision indicating R-factor (R_{p.i.m}).
double m_completeness
Fraction of observed unique reflections in the resolution range defined by d_min and d_max.
double m_dspacingMax
Upper d-spacing limit in the data set.
double m_rMerge
Merging R-factor, R_merge, sometimes also called R_sym.
This class computes all possible unique reflections within the specified d-limits,...
const std::map< Kernel::V3D, UniqueReflection > & getReflections() const
Returns the internally stored reflection map.
void addObservations(const std::vector< DataObjects::Peak > &peaks)
Assigns the supplied peaks to the proper UniqueReflection.
Save a PeaksWorkspace to a Gsas-style ASCII .hkl file.
Definition SortHKL.h:32
void sortOutputPeaksByHKL(const API::IPeaksWorkspace_sptr &outputPeaksWorkspace)
Sorts the peaks in the workspace by H, K and L.
Definition SortHKL.cpp:368
std::vector< Mantid::Geometry::ReflectionCondition_sptr > m_refConds
Reflection conditions.
Definition SortHKL.h:79
API::ITableWorkspace_sptr getStatisticsTable(const std::string &name) const
Create a TableWorkspace for the statistics with appropriate columns or get one from the ADS.
Definition SortHKL.cpp:313
DataObjects::PeaksWorkspace_sptr getOutputPeaksWorkspace(const DataObjects::PeaksWorkspace_sptr &inputPeaksWorkspace) const
Returns a PeaksWorkspace which is either the input workspace or a clone.
Definition SortHKL.cpp:358
std::pair< double, double > getDLimits(const std::vector< DataObjects::Peak > &peaks, const Geometry::UnitCell &cell) const
Returns the lowest and highest d-Value in the list.
Definition SortHKL.cpp:303
void insertStatisticsIntoTable(const API::ITableWorkspace_sptr &table, const PeakStatisticsTools::PeaksStatistics &statistics) const
Inserts statistics the supplied PeaksStatistics-objects into the supplied TableWorkspace.
Definition SortHKL.cpp:338
Geometry::ReflectionCondition_sptr getCentering() const
Returns the centering extracted from the user-supplied property.
Definition SortHKL.cpp:266
Geometry::PointGroup_sptr getPointgroup() const
Returns the PointGroup-object constructed from the property supplied by the user.
Definition SortHKL.cpp:281
std::vector< DataObjects::Peak > getNonZeroPeaks(const std::vector< DataObjects::Peak > &inputPeaks) const
Returns a vector which contains only peaks with I > 0, sigma > 0 and valid HKL.
Definition SortHKL.cpp:230
const std::string name() const override
Algorithm's name for identification.
Definition SortHKL.h:38
void init() override
Virtual method - must be overridden by concrete algorithm.
Definition SortHKL.cpp:43
PeakStatisticsTools::UniqueReflectionCollection getUniqueReflections(const std::vector< DataObjects::Peak > &peaks, const Geometry::UnitCell &cell) const
SortHKL::getUniqueReflections.
Definition SortHKL.cpp:253
std::vector< Mantid::Geometry::PointGroup_sptr > m_pointGroups
Point Groups possible.
Definition SortHKL.h:76
void exec() override
Virtual method - must be overridden by concrete algorithm.
Definition SortHKL.cpp:99
Structure describing a single-crystal peak.
Definition Peak.h:34
TableWorkspace is an implementation of Workspace in which the data are organised in columns of same s...
Class to implement unit cell of crystals.
Definition UnitCell.h:44
double d(double h, double k, double l) const
Return d-spacing ( ) for a given h,k,l coordinate.
Definition UnitCell.cpp:700
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
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...
std::shared_ptr< IPeaksWorkspace > IPeaksWorkspace_sptr
shared pointer to Mantid::API::IPeaksWorkspace
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
std::shared_ptr< TableWorkspace > TableWorkspace_sptr
shared pointer to Mantid::DataObjects::TableWorkspace
std::shared_ptr< PeaksWorkspace > PeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
std::shared_ptr< ReflectionCondition > ReflectionCondition_sptr
Shared pointer to a ReflectionCondition.
std::shared_ptr< PointGroup > PointGroup_sptr
Shared pointer to a PointGroup.
Definition PointGroup.h:69
MANTID_GEOMETRY_DLL const ReflectionConditions & getAllReflectionConditions()
MANTID_GEOMETRY_DLL std::vector< PointGroup_sptr > getAllPointGroups()
Statistics getStatistics(const std::vector< TYPE > &data, const unsigned int flags=StatOptions::AllStats)
Return a statistics object for the given data set.
std::vector< double > getZscore(const std::vector< TYPE > &data)
Return the Z score values for a dataset.
std::vector< double > getWeightedZscore(const std::vector< TYPE > &data, const std::vector< TYPE > &weights)
There are enough special cases in determining the Z score where it useful to put it in a single funct...
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54