Newer
Older
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidCrystal/IndexPeaks.h"
#include "MantidDataObjects/PeaksWorkspace.h"
#include "MantidGeometry/Crystal/IndexingUtils.h"
#include "MantidGeometry/Crystal/OrientedLattice.h"
#include "MantidKernel/BoundedValidator.h"
namespace Crystal {
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(IndexPeaks)
using namespace Mantid::Kernel;
using namespace Mantid::API;
using namespace Mantid::DataObjects;
using namespace Mantid::Geometry;
/** Initialize the algorithm's properties.
*/
void IndexPeaks::init() {
this->declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>(
"PeaksWorkspace", "", Direction::InOut),
"Input Peaks Workspace");
auto mustBePositive = boost::make_shared<BoundedValidator<double>>();
mustBePositive->setLower(0.0);
this->declareProperty(
std::make_unique<PropertyWithValue<double>>(
"Tolerance", 0.15, mustBePositive, Direction::Input),
"Indexing Tolerance (0.15)");
this->declareProperty(
std::make_unique<PropertyWithValue<double>>(
"ToleranceForSatellite", 0.15, mustBePositive, Direction::Input),
this->declareProperty("RoundHKLs", true,
"Round H, K and L values to integers");
this->declareProperty("CommonUBForAll", false,
"Index all orientations with a common UB");
this->declareProperty(std::make_unique<PropertyWithValue<int>>(
"NumIndexed", 0, Direction::Output),
"Gets set with the number of indexed peaks.");
this->declareProperty(std::make_unique<PropertyWithValue<double>>(
"AverageError", 0.0, Direction::Output),
"Gets set with the average HKL indexing error.");
this->declareProperty(std::make_unique<PropertyWithValue<int>>(
"TotalNumIndexed", 0, Direction::Output),
"Gets set with the number of Total indexed peaks.");
this->declareProperty(std::make_unique<PropertyWithValue<int>>(
"MainNumIndexed", 0, Direction::Output),
"Gets set with the number of indexed main peaks.");
this->declareProperty(std::make_unique<PropertyWithValue<int>>(
"SateNumIndexed", 0, Direction::Output),
"Gets set with the number of indexed main peaks.");
this->declareProperty(
std::make_unique<PropertyWithValue<double>>("MainError", 0.0,
"Gets set with the average HKL indexing error of Main Peaks.");
this->declareProperty(
std::make_unique<PropertyWithValue<double>>("SatelliteError", 0.0,
"Gets set with the average HKL indexing error of Satellite Peaks.");
}
/** Execute the algorithm.
*/
void IndexPeaks::exec() {
PeaksWorkspace_sptr ws = this->getProperty("PeaksWorkspace");
if (!ws) {
throw std::runtime_error("Could not read the peaks workspace");
}
OrientedLattice o_lattice = ws->mutableSample().getOrientedLattice();
const Matrix<double> &UB = o_lattice.getUB();
if (!IndexingUtils::CheckUB(UB)) {
throw std::runtime_error(
"ERROR: The stored UB is not a valid orientation matrix");
}
bool round_hkls = this->getProperty("RoundHKLs");
bool commonUB = this->getProperty("CommonUBForAll");
std::vector<Peak> &peaks = ws->getPeaks();
size_t n_peaks = ws->getNumberPeaks();
int total_indexed = 0;
int total_main = 0;
int total_sate = 0;
double average_error;
double average_main_error;
double average_sate_error;
double tolerance = this->getProperty("Tolerance");
if (commonUB && o_lattice.getModVec(0) == V3D(0, 0, 0)) {
std::vector<V3D> miller_indices;
std::vector<V3D> q_vectors;
q_vectors.reserve(n_peaks);
for (const auto &peak : peaks) {
q_vectors.push_back(peak.getQSampleFrame());
}
total_indexed = IndexingUtils::CalculateMillerIndices(
UB, q_vectors, tolerance, miller_indices, average_error);
for (size_t i = 0; i < n_peaks; i++) {
peaks[i].setHKL(miller_indices[i]);
peaks[i].setIntMNP(V3D(0, 0, 0));
}
} else {
double total_error = 0;
double total_main_error = 0;
double total_sate_error = 0;
double satetolerance = this->getProperty("ToleranceForSatellite");
// get list of run numbers in this peaks workspace
std::vector<int> run_numbers;
for (const auto &peak : peaks) {
bool found = false;
size_t k = 0;
while (k < run_numbers.size() && !found) {
if (run == run_numbers[k])
found = true;
else
k++;
}
if (!found)
run_numbers.push_back(run);
}
// index the peaks for each run separately, using a UB matrix optimized for
// that run
for (const int run : run_numbers) {
std::vector<V3D> miller_indices;
std::vector<V3D> q_vectors;
for (const auto &peak : peaks) {
if (peak.getRunNumber() == run)
q_vectors.push_back(peak.getQSampleFrame());
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
}
Matrix<double> tempUB(UB);
int num_indexed = 0;
int original_indexed = 0;
double original_error = 0;
original_indexed = IndexingUtils::CalculateMillerIndices(
tempUB, q_vectors, tolerance, miller_indices, original_error);
IndexingUtils::RoundHKLs(miller_indices); // HKLs must be rounded for
// Optimize_UB to work
num_indexed = original_indexed;
average_error = original_error;
bool done = false;
if (num_indexed < 3) // can't optimize without at least 3
{ // peaks
done = true;
}
int iteration = 0;
while (iteration < 4 && !done) // try repeatedly optimizing 4 times
{ // which is usually sufficient
try {
IndexingUtils::Optimize_UB(tempUB, miller_indices, q_vectors);
} catch (...) // If there is any problem, such as too few
{ // independent peaks, just use the original UB
tempUB = UB;
done = true;
num_indexed = IndexingUtils::CalculateMillerIndices(
tempUB, q_vectors, tolerance, miller_indices, average_error);
IndexingUtils::RoundHKLs(miller_indices); // HKLs must be rounded for
// Optimize_UB to work
if (num_indexed < original_indexed) // just use the original UB
{
num_indexed = original_indexed;
average_error = original_error;
done = true;
}
iteration++;
}
if (o_lattice.getMaxOrder() ==
0) // If data not modulated, recalculate fractional HKL
{
if (!round_hkls) // If user wants fractional hkls, recalculate them
{
num_indexed = IndexingUtils::CalculateMillerIndices(
tempUB, q_vectors, tolerance, miller_indices, average_error);
}
total_indexed += num_indexed;
total_error += average_error * num_indexed;
// tell the user how many were indexed in each run
if (run_numbers.size() > 1) {
g_log.notice() << "Run " << run << ": indexed " << num_indexed
<< " Peaks out of " << q_vectors.size()
<< " with tolerance of " << tolerance << '\n';
g_log.notice() << "Average error in h,k,l for indexed peaks = "
<< average_error << '\n';
}
size_t miller_index_counter = 0;
if (peak.getRunNumber() == run) {
peak.setHKL(miller_indices[miller_index_counter]);
peak.setIntHKL(miller_indices[miller_index_counter]);
peak.setIntMNP(V3D(0, 0, 0));
miller_index_counter++;
}
}
} else {
g_log.notice() << "Maximum Order: " << o_lattice.getMaxOrder() << '\n';
int ModDim = 0;
int main_indexed = 0;
int sate_indexed = 0;
double main_error = 0;
double sate_error = 0;
int maxOrder = o_lattice.getMaxOrder();
bool crossTerm = o_lattice.getCrossTerm();
V3D offsets1 = o_lattice.getModVec(0);
V3D offsets2 = o_lattice.getModVec(1);
V3D offsets3 = o_lattice.getModVec(2);
if (offsets1 == V3D(0, 0, 0))
throw std::runtime_error("Invalid Modulation Vector");
else if (offsets2 == V3D(0, 0, 0))
ModDim = 1;
else if (offsets3 == V3D(0, 0, 0))
ModDim = 2;
else
ModDim = 3;
IndexingUtils::CalculateMillerIndices(tempUB, q_vectors, 1.0,
miller_indices, average_error);
// Index satellite peaks
size_t miller_index_counter = 0;
if (peak.getRunNumber() == run) {
peak.setHKL(miller_indices[miller_index_counter]);
bool peak_main_indexed{false}, peak_sat_indexed{false};
if (IndexingUtils::ValidIndex(hkl, tolerance)) {
peak.setIntHKL(hkl);
peak.setIntMNP(V3D(0, 0, 0));
peak_main_indexed = true;
if (ModDim > 0) {
for (int order = -maxOrder; order <= maxOrder; order++) {
if (order == 0)
continue; // exclude order 0
V3D hkl1(hkl);
if (IndexingUtils::ValidIndex(hkl1, satetolerance)) {
peak.setIntHKL(hkl1);
peak.setIntMNP(V3D(order, 0, 0));
peak_sat_indexed = true;
}
if (ModDim > 1) {
for (int order = -maxOrder; order <= maxOrder; order++) {
if (order == 0)
continue; // exclude order 0
V3D hkl1(hkl);
if (IndexingUtils::ValidIndex(hkl1, satetolerance)) {
peak.setIntHKL(hkl1);
peak.setIntMNP(V3D(0, order, 0));
peak_sat_indexed = true;
}
if (ModDim > 2) {
for (int order = -maxOrder; order <= maxOrder; order++) {
if (order == 0)
continue; // exclude order 0
V3D hkl1(hkl);
if (IndexingUtils::ValidIndex(hkl1, satetolerance)) {
peak.setIntHKL(hkl1);
peak.setIntMNP(V3D(0, 0, order));
peak_sat_indexed = true;
}
} else {
if (ModDim == 1) {
for (int order = -maxOrder; order <= maxOrder; order++) {
if (order == 0)
continue; // exclude order 0
V3D hkl1(hkl);
if (IndexingUtils::ValidIndex(hkl1, satetolerance)) {
peak.setIntHKL(hkl1);
peak.setIntMNP(V3D(order, 0, 0));
peak_sat_indexed = true;
}
}
}
if (ModDim == 2) {
for (int m = -maxOrder; m <= maxOrder; m++)
if (m == 0 && n == 0)
continue; // exclude 0,0
V3D hkl1(hkl);
if (IndexingUtils::ValidIndex(hkl1, satetolerance)) {
peak.setIntHKL(hkl1);
peak.setIntMNP(V3D(m, n, 0));
peak_sat_indexed = true;
}
}
if (ModDim == 3) {
for (int m = -maxOrder; m <= maxOrder; m++)
for (int n = -maxOrder; n <= maxOrder; n++)
for (int p = -maxOrder; p <= maxOrder; p++) {
if (m == 0 && n == 0 && p == 0)
continue; // exclude 0,0,0
V3D hkl1(hkl);
hkl1 -= offsets1 * m + offsets2 * n + offsets3 * p;
if (IndexingUtils::ValidIndex(hkl1, satetolerance)) {
peak.setIntHKL(hkl1);
peak.setIntMNP(V3D(m, n, p));
peak_sat_indexed = true;
if (peak_main_indexed) {
main_indexed++;
peak.setIntHKL(V3D(0, 0, 0));
peak.setIntMNP(V3D(0, 0, 0));
} else if (peak_sat_indexed)
sate_indexed++;
else {
peak.setHKL(V3D(0, 0, 0));
peak.setIntHKL(V3D(0, 0, 0));
peak.setIntMNP(V3D(0, 0, 0));
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
num_indexed = main_indexed + sate_indexed;
total_main += main_indexed;
total_sate += sate_indexed;
total_main_error += main_error / 3;
total_sate_error += sate_error / 3;
total_indexed += main_indexed + sate_indexed;
total_error += main_error / 3 + sate_error / 3;
if (run_numbers.size() > 1) {
g_log.notice() << "Run " << run << ": indexed " << num_indexed
<< " Peaks out of " << q_vectors.size() << '\n';
g_log.notice() << "of which, " << main_indexed
<< " Main Bragg Peaks are indexed with tolerance of "
<< tolerance << ", " << sate_indexed
<< " Satellite Peaks are indexed with tolerance of "
<< satetolerance << '\n';
}
}
}
if (total_indexed > 0)
average_error = total_error / total_indexed;
else
average_error = 0;
if (total_main > 0)
average_main_error = total_main_error / total_main;
else
average_main_error = 0;
if (total_sate > 0)
average_sate_error = total_sate_error / total_sate;
else
average_sate_error = 0;
}
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
// tell the user how many were indexed overall and the overall average error
g_log.notice() << "ALL Runs: indexed " << total_indexed << " Peaks out of "
<< n_peaks << " with tolerance of " << tolerance << '\n';
g_log.notice() << "Average error in h,k,l for indexed peaks = "
<< average_error << '\n';
// Save output properties
this->setProperty("NumIndexed", total_indexed);
this->setProperty("AverageError", average_error);
// Show the lattice parameters
g_log.notice() << o_lattice << "\n";
} else {
g_log.notice() << "ALL Runs: indexed " << total_indexed << " Peaks out of "
<< n_peaks << " with tolerance of " << tolerance << '\n';
g_log.notice() << "Out of " << total_indexed << " Indexed Peaks "
<< total_main << " are Main Bragg Peaks, and " << total_sate
<< " are satellite peaks " << '\n';
g_log.notice() << "Average error in h,k,l for indexed peaks = "
<< average_error << '\n';
g_log.notice() << "Average error in h,k,l for indexed main peaks = "
<< average_main_error << '\n';
g_log.notice() << "Average error in h,k,l for indexed satellite peaks = "
<< average_sate_error << '\n';
// Save output properties
setProperty("TotalNumIndexed", total_indexed);
setProperty("MainNumIndexed", total_main);
setProperty("SateNumIndexed", total_sate);
setProperty("MainError", average_main_error);
setProperty("SatelliteError", average_sate_error);
// Show the lattice parameters
g_log.notice() << o_lattice << "\n";
}
}
} // namespace Crystal
} // namespace Mantid