Mantid
Loading...
Searching...
No Matches
IndexingUtils.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 "MantidKernel/Quat.h"
11
12#include <boost/math/special_functions/round.hpp>
13#include <boost/numeric/conversion/cast.hpp>
14
15#include <gsl/gsl_errno.h>
16#include <gsl/gsl_fft_real.h>
17#include <gsl/gsl_linalg.h>
18#include <gsl/gsl_matrix.h>
19#include <gsl/gsl_sys.h>
20#include <gsl/gsl_vector.h>
21
22#include <algorithm>
23#include <cmath>
24
25using namespace Mantid::Geometry;
30
31namespace {
32const constexpr double DEG_TO_RAD = M_PI / 180.;
33const constexpr double RAD_TO_DEG = 180. / M_PI;
34} // namespace
35
94double IndexingUtils::Find_UB(DblMatrix &UB, const std::vector<V3D> &q_vectors, OrientedLattice &lattice,
95 double required_tolerance, int base_index, size_t num_initial, double degrees_per_step,
96 bool fixAll, int iterations) {
97 if (UB.numRows() != 3 || UB.numCols() != 3) {
98 throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3");
99 }
100
101 if (q_vectors.size() < 2) {
102 throw std::invalid_argument("Find_UB(): Two or more indexed peaks needed to find UB");
103 }
104
105 if (required_tolerance <= 0) {
106 throw std::invalid_argument("Find_UB(): required_tolerance must be positive");
107 }
108
109 if (num_initial < 2) {
110 throw std::invalid_argument("Find_UB(): number of peaks for inital scan must be > 2");
111 }
112
113 if (degrees_per_step <= 0) {
114 throw std::invalid_argument("Find_UB(): degrees_per_step must be positive");
115 }
116
117 // get list of peaks, sorted on |Q|
118 std::vector<V3D> sorted_qs;
119 sorted_qs.reserve(q_vectors.size());
120
121 if (q_vectors.size() > 5) // shift to be centered on peak (we lose
122 // one peak that way, so require > 5)
123 {
124 std::vector<V3D> shifted_qs(q_vectors);
125 size_t mid_ind = q_vectors.size() / 3;
126 // either do an initial sort and use
127 // default mid index, or use the index
128 // specified by the base_peak parameter
129 if (base_index < 0 || base_index >= static_cast<int>(q_vectors.size())) {
130 std::sort(shifted_qs.begin(), shifted_qs.end(), V3D::compareMagnitude);
131 } else {
132 mid_ind = base_index;
133 }
134 V3D mid_vec(shifted_qs[mid_ind]);
135
136 for (size_t i = 0; i < shifted_qs.size(); i++) {
137 if (i != mid_ind) {
138 V3D shifted_vec(shifted_qs[i]);
139 shifted_vec -= mid_vec;
140 sorted_qs.emplace_back(shifted_vec);
141 }
142 }
143 } else {
144 std::copy(q_vectors.cbegin(), q_vectors.cend(), std::back_inserter(sorted_qs));
145 }
146
147 std::sort(sorted_qs.begin(), sorted_qs.end(), V3D::compareMagnitude);
148
149 if (num_initial > sorted_qs.size())
150 num_initial = sorted_qs.size();
151
152 std::vector<V3D> some_qs;
153 some_qs.reserve(q_vectors.size());
154
155 for (size_t i = 0; i < num_initial; i++)
156 some_qs.emplace_back(sorted_qs[i]);
157
158 ScanFor_UB(UB, some_qs, lattice, degrees_per_step, required_tolerance);
159
160 double fit_error = 0;
161 std::vector<V3D> miller_ind;
162 std::vector<V3D> indexed_qs;
163 miller_ind.reserve(q_vectors.size());
164 indexed_qs.reserve(q_vectors.size());
165
166 // now gradually bring in the remaining
167 // peaks and re-optimize the UB to index
168 // them as well
169 while (!fixAll && num_initial < sorted_qs.size()) {
170 num_initial = std::lround(1.5 * static_cast<double>(num_initial + 3));
171 // add 3, in case we started with
172 // a very small number of peaks!
173 if (num_initial >= sorted_qs.size())
174 num_initial = sorted_qs.size();
175
176 for (size_t i = some_qs.size(); i < num_initial; i++)
177 some_qs.emplace_back(sorted_qs[i]);
178 for (int counter = 0; counter < iterations; counter++) {
179 try {
180 GetIndexedPeaks(UB, some_qs, required_tolerance, miller_ind, indexed_qs, fit_error);
181 Matrix<double> temp_UB(3, 3, false);
182 fit_error = Optimize_UB(temp_UB, miller_ind, indexed_qs);
183 UB = temp_UB;
184 } catch (...) {
185 // failed to fit using these peaks, so add some more and try again
186 }
187 }
188 }
189
190 std::vector<double> sigabc(7);
191 if (!fixAll && q_vectors.size() >= 3) // try one last refinement using all peaks
192 {
193 for (int counter = 0; counter < iterations; counter++) {
194 try {
195 GetIndexedPeaks(UB, q_vectors, required_tolerance, miller_ind, indexed_qs, fit_error);
196 Matrix<double> temp_UB = UB;
197 fit_error = Optimize_UB(temp_UB, miller_ind, indexed_qs, sigabc);
198 UB = temp_UB;
199 } catch (...) {
200 // failed to improve UB using these peaks, so just return the current UB
201 }
202 }
203 }
204 // Regardless of how we got the UB, find the
205 // sum-squared errors for the indexing in
206 // HKL space.
207 GetIndexedPeaks(UB, q_vectors, required_tolerance, miller_ind, indexed_qs, fit_error);
208 // set the error on the lattice parameters
209 lattice.setUB(UB);
210 lattice.setError(sigabc[0], sigabc[1], sigabc[2], sigabc[3], sigabc[4], sigabc[5]);
211 return fit_error;
212}
213
277double IndexingUtils::Find_UB(DblMatrix &UB, const std::vector<V3D> &q_vectors, double min_d, double max_d,
278 double required_tolerance, int base_index, size_t num_initial, double degrees_per_step) {
279 if (UB.numRows() != 3 || UB.numCols() != 3) {
280 throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3");
281 }
282
283 if (q_vectors.size() < 3) {
284 throw std::invalid_argument("Find_UB(): Three or more indexed peaks needed to find UB");
285 }
286
287 if (min_d >= max_d || min_d <= 0) {
288 throw std::invalid_argument("Find_UB(): Need 0 < min_d < max_d");
289 }
290
291 if (required_tolerance <= 0) {
292 throw std::invalid_argument("Find_UB(): required_tolerance must be positive");
293 }
294
295 if (num_initial < 3) {
296 throw std::invalid_argument("Find_UB(): number of peaks for inital scan must be > 2");
297 }
298
299 if (degrees_per_step <= 0) {
300 throw std::invalid_argument("Find_UB(): degrees_per_step must be positive");
301 }
302
303 // get list of peaks, sorted on |Q|
304 std::vector<V3D> sorted_qs;
305 sorted_qs.reserve(q_vectors.size());
306
307 if (q_vectors.size() > 5) // shift to be centered on peak (we lose
308 // one peak that way, so require > 5)
309 {
310 std::vector<V3D> shifted_qs(q_vectors);
311 size_t mid_ind = q_vectors.size() / 2;
312 // either do an initial sort and use
313 // default mid index, or use the index
314 // specified by the base_peak parameter
315 if (base_index < 0 || base_index >= static_cast<int>(q_vectors.size())) {
316 std::sort(shifted_qs.begin(), shifted_qs.end(), V3D::compareMagnitude);
317 } else {
318 mid_ind = base_index;
319 }
320 V3D mid_vec(shifted_qs[mid_ind]);
321
322 for (size_t i = 0; i < shifted_qs.size(); i++) {
323 if (i != mid_ind) {
324 V3D shifted_vec(shifted_qs[i]);
325 shifted_vec -= mid_vec;
326 sorted_qs.emplace_back(shifted_vec);
327 }
328 }
329 } else {
330 std::copy(q_vectors.cbegin(), q_vectors.cend(), std::back_inserter(sorted_qs));
331 }
332
333 std::sort(sorted_qs.begin(), sorted_qs.end(), V3D::compareMagnitude);
334
335 if (num_initial > sorted_qs.size())
336 num_initial = sorted_qs.size();
337
338 std::vector<V3D> some_qs;
339 some_qs.reserve(q_vectors.size());
340
341 for (size_t i = 0; i < num_initial; i++)
342 some_qs.emplace_back(sorted_qs[i]);
343 std::vector<V3D> directions;
344 ScanFor_Directions(directions, some_qs, min_d, max_d, required_tolerance, degrees_per_step);
345
346 if (directions.size() < 3) {
347 throw std::runtime_error("Find_UB(): Could not find at least three possible lattice directions");
348 }
349
350 std::sort(directions.begin(), directions.end(), V3D::compareMagnitude);
351
352 if (!FormUB_From_abc_Vectors(UB, directions, 0, min_d, max_d)) {
353 throw std::runtime_error("Find_UB(): Could not find independent a, b, c directions");
354 }
355 // now gradually bring in the remaining
356 // peaks and re-optimize the UB to index
357 // them as well
358 std::vector<V3D> miller_ind;
359 std::vector<V3D> indexed_qs;
360 miller_ind.reserve(q_vectors.size());
361 indexed_qs.reserve(q_vectors.size());
362
363 Matrix<double> temp_UB(3, 3, false);
364 double fit_error = 0;
365 while (num_initial < sorted_qs.size()) {
366 num_initial = std::lround(1.5 * static_cast<double>(num_initial + 3));
367 // add 3, in case we started with
368 // a very small number of peaks!
369 if (num_initial >= sorted_qs.size())
370 num_initial = sorted_qs.size();
371
372 for (size_t i = some_qs.size(); i < num_initial; i++)
373 some_qs.emplace_back(sorted_qs[i]);
374
375 GetIndexedPeaks(UB, some_qs, required_tolerance, miller_ind, indexed_qs, fit_error);
376 try {
377 fit_error = Optimize_UB(temp_UB, miller_ind, indexed_qs);
378 UB = temp_UB;
379 } catch (...) {
380 // failed to improve with these peaks, so continue with more peaks
381 // if possible
382 }
383 }
384
385 if (q_vectors.size() >= 5) // try one last refinement using all peaks
386 {
387 GetIndexedPeaks(UB, q_vectors, required_tolerance, miller_ind, indexed_qs, fit_error);
388 try {
389 fit_error = Optimize_UB(temp_UB, miller_ind, indexed_qs);
390 UB = temp_UB;
391 } catch (...) {
392 // failed to improve with all peaks, so return the UB we had
393 }
394 }
395
396 if (NiggliCell::MakeNiggliUB(UB, temp_UB))
397 UB = temp_UB;
398
399 return fit_error;
400}
401
451double IndexingUtils::Find_UB(DblMatrix &UB, const std::vector<V3D> &q_vectors, double min_d, double max_d,
452 double required_tolerance, double degrees_per_step, int iterations) {
453 if (UB.numRows() != 3 || UB.numCols() != 3) {
454 throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3");
455 }
456
457 if (q_vectors.size() < 4) {
458 throw std::invalid_argument("Find_UB(): Four or more indexed peaks needed to find UB");
459 }
460
461 if (min_d >= max_d || min_d <= 0) {
462 throw std::invalid_argument("Find_UB(): Need 0 < min_d < max_d");
463 }
464
465 if (required_tolerance <= 0) {
466 throw std::invalid_argument("Find_UB(): required_tolerance must be positive");
467 }
468
469 if (degrees_per_step <= 0) {
470 throw std::invalid_argument("Find_UB(): degrees_per_step must be positive");
471 }
472
473 std::vector<V3D> directions;
474
475 // NOTE: we use a somewhat higher tolerance when
476 // finding individual directions since it is easier
477 // to index one direction individually compared to
478 // indexing three directions simultaneously.
479 size_t max_indexed =
480 FFTScanFor_Directions(directions, q_vectors, min_d, max_d, 0.75f * required_tolerance, degrees_per_step);
481
482 if (max_indexed == 0) {
483 throw std::invalid_argument("Find_UB(): Could not find any a,b,c vectors to index Qs");
484 }
485
486 if (directions.size() < 3) {
487 throw std::invalid_argument("Find_UB(): Could not find enough a,b,c vectors");
488 }
489
490 std::sort(directions.begin(), directions.end(), V3D::compareMagnitude);
491
492 double min_vol = min_d * min_d * min_d / 4.0;
493
494 if (!FormUB_From_abc_Vectors(UB, directions, q_vectors, required_tolerance, min_vol)) {
495 throw std::invalid_argument("Find_UB(): Could not form UB matrix from a,b,c vectors");
496 }
497
498 Matrix<double> temp_UB(3, 3, false);
499 double fit_error = 0;
500 if (q_vectors.size() >= 5) // repeatedly refine UB
501 {
502 std::vector<V3D> miller_ind;
503 std::vector<V3D> indexed_qs;
504 miller_ind.reserve(q_vectors.size());
505 indexed_qs.reserve(q_vectors.size());
506
507 GetIndexedPeaks(UB, q_vectors, required_tolerance, miller_ind, indexed_qs, fit_error);
508
509 for (int counter = 0; counter < iterations; counter++) {
510 try {
511 fit_error = Optimize_UB(temp_UB, miller_ind, indexed_qs);
512 UB = temp_UB;
513 GetIndexedPeaks(UB, q_vectors, required_tolerance, miller_ind, indexed_qs, fit_error);
514 } catch (std::exception &) {
515 // failed to improve with all peaks, so just keep the UB we had
516 }
517 }
518 }
519
520 if (NiggliCell::MakeNiggliUB(UB, temp_UB))
521 UB = temp_UB;
522
523 return fit_error;
524}
525
562double IndexingUtils::Optimize_UB(DblMatrix &UB, const std::vector<V3D> &hkl_vectors, const std::vector<V3D> &q_vectors,
563 std::vector<double> &sigabc) {
564 double result = 0;
565 result = Optimize_UB(UB, hkl_vectors, q_vectors);
566
567 if (sigabc.size() < 6) {
568 sigabc.clear();
569 return result;
570 } else
571 for (int i = 0; i < 6; i++)
572 sigabc[i] = 0.0;
573
574 size_t nDOF = 3 * (hkl_vectors.size() - 3);
575 DblMatrix HKLTHKL(3, 3);
576 for (int r = 0; r < 3; r++)
577 for (int c = 0; c < 3; c++)
578 for (const auto &hkl_vector : hkl_vectors) {
579 HKLTHKL[r][c] += hkl_vector[r] * hkl_vector[c]; // rounded??? to nearest integer
580 }
581
582 HKLTHKL.Invert();
583
584 double SMALL = 1.525878906E-5;
585 Matrix<double> derivs(3, 7);
586 std::vector<double> latOrig, latNew;
587 GetLatticeParameters(UB, latOrig);
588
589 for (int r = 0; r < 3; r++) {
590 for (int c = 0; c < 3; c++) {
591
592 UB[r][c] += SMALL;
593 GetLatticeParameters(UB, latNew);
594 UB[r][c] -= SMALL;
595
596 for (size_t l = 0; l < 7; l++)
597 derivs[c][l] = (latNew[l] - latOrig[l]) / SMALL;
598 }
599
600 for (size_t l = 0; l < std::min<size_t>(static_cast<size_t>(7), sigabc.size()); l++)
601 for (int m = 0; m < 3; m++)
602 for (int n = 0; n < 3; n++)
603 sigabc[l] += (derivs[m][l] * HKLTHKL[m][n] * derivs[n][l]);
604 }
605
606 double delta = result / static_cast<double>(nDOF);
607
608 for (size_t i = 0; i < std::min<size_t>(7, sigabc.size()); i++)
609 sigabc[i] = sqrt(delta * sigabc[i]);
610
611 return result;
612}
613
644double IndexingUtils::Optimize_UB(DblMatrix &UB, const std::vector<V3D> &hkl_vectors,
645 const std::vector<V3D> &q_vectors) {
646 if (UB.numRows() != 3 || UB.numCols() != 3) {
647 throw std::invalid_argument("Optimize_UB(): UB matrix NULL or not 3X3");
648 }
649
650 if (hkl_vectors.size() < 3) {
651 throw std::invalid_argument("Optimize_UB(): Three or more indexed peaks needed to find UB");
652 }
653
654 if (hkl_vectors.size() != q_vectors.size()) {
655 throw std::invalid_argument("Optimize_UB(): Number of hkl_vectors != number of q_vectors");
656 }
657
658 gsl_matrix *H_transpose = gsl_matrix_alloc(hkl_vectors.size(), 3);
659 gsl_vector *tau = gsl_vector_alloc(3);
660
661 double sum_sq_error = 0;
662 // Make the H-transpose matrix from the
663 // hkl vectors and form QR factorization
664 for (size_t row = 0; row < hkl_vectors.size(); row++) {
665 for (size_t col = 0; col < 3; col++) {
666 gsl_matrix_set(H_transpose, row, col, (hkl_vectors[row])[col]);
667 }
668 }
669
670 int returned_flag = gsl_linalg_QR_decomp(H_transpose, tau);
671
672 if (returned_flag != 0) {
673 gsl_matrix_free(H_transpose);
674 gsl_vector_free(tau);
675 throw std::runtime_error("Optimize_UB(): gsl QR_decomp failed, invalid hkl values");
676 }
677 // solve for each row of UB, using the
678 // QR factorization of and accumulate the
679 // sum of the squares of the residuals
680 gsl_vector *UB_row = gsl_vector_alloc(3);
681 gsl_vector *q = gsl_vector_alloc(q_vectors.size());
682 gsl_vector *residual = gsl_vector_alloc(q_vectors.size());
683
684 bool found_UB = true;
685
686 for (size_t row = 0; row < 3; row++) {
687 for (size_t i = 0; i < q_vectors.size(); i++) {
688 gsl_vector_set(q, i, (q_vectors[i])[row] / (2.0 * M_PI));
689 }
690
691 returned_flag = gsl_linalg_QR_lssolve(H_transpose, tau, q, UB_row, residual);
692 if (returned_flag != 0) {
693 found_UB = false;
694 }
695
696 for (size_t i = 0; i < 3; i++) {
697 double value = gsl_vector_get(UB_row, i);
698 if (!std::isfinite(value))
699 found_UB = false;
700 }
701
702 V3D row_values(gsl_vector_get(UB_row, 0), gsl_vector_get(UB_row, 1), gsl_vector_get(UB_row, 2));
703 UB.setRow(row, row_values);
704
705 for (size_t i = 0; i < q_vectors.size(); i++) {
706 sum_sq_error += gsl_vector_get(residual, i) * gsl_vector_get(residual, i);
707 }
708 }
709
710 gsl_matrix_free(H_transpose);
711 gsl_vector_free(tau);
712 gsl_vector_free(UB_row);
713 gsl_vector_free(q);
714 gsl_vector_free(residual);
715
716 if (!found_UB) {
717 throw std::runtime_error("Optimize_UB(): Failed to find UB, invalid hkl or Q values");
718 }
719
720 if (!CheckUB(UB)) {
721 throw std::runtime_error("Optimize_UB(): The optimized UB is not valid");
722 }
723
724 return sum_sq_error;
725}
726
770double IndexingUtils::Optimize_6dUB(DblMatrix &UB, DblMatrix &ModUB, const std::vector<V3D> &hkl_vectors,
771 const std::vector<V3D> &mnp_vectors, const int &ModDim,
772 const std::vector<V3D> &q_vectors, std::vector<double> &sigabc,
773 std::vector<double> &sigq) {
774
775 if (ModDim != 0 && ModDim != 1 && ModDim != 2 && ModDim != 3)
776 throw std::invalid_argument("invalid Value for Modulation Dimension");
777
778 double result = 0;
779 result = Optimize_6dUB(UB, ModUB, hkl_vectors, mnp_vectors, ModDim, q_vectors);
780
781 if (sigabc.size() < static_cast<size_t>(6)) {
782 sigabc.clear();
783 return result;
784 } else
785 for (int i = 0; i < 6; i++)
786 sigabc[i] = 0.0;
787
788 size_t nDOF = 3 * (hkl_vectors.size() - 3);
789 DblMatrix HKLTHKL(3, 3);
790 for (int r = 0; r < 3; r++)
791 for (int c = 0; c < 3; c++)
792 for (const auto &hkl_vector : hkl_vectors) {
793 HKLTHKL[r][c] += hkl_vector[r] * hkl_vector[c];
794 }
795
796 HKLTHKL.Invert();
797
798 double SMALL = 1.525878906E-5;
799 Matrix<double> derivs(3, 7);
800 std::vector<double> latOrig, latNew;
801 GetLatticeParameters(UB, latOrig);
802
803 for (int r = 0; r < 3; r++) {
804 for (int c = 0; c < 3; c++) {
805
806 UB[r][c] += SMALL;
807 GetLatticeParameters(UB, latNew);
808 UB[r][c] -= SMALL;
809
810 for (size_t l = 0; l < 7; l++)
811 derivs[c][l] = (latNew[l] - latOrig[l]) / SMALL;
812 }
813
814 for (size_t l = 0; l < std::min<size_t>(static_cast<size_t>(7), sigabc.size()); l++)
815 for (int m = 0; m < 3; m++)
816 for (int n = 0; n < 3; n++)
817 sigabc[l] += (derivs[m][l] * HKLTHKL[m][n] * derivs[n][l]);
818 }
819
820 double delta = result / static_cast<double>(nDOF);
821
822 for (size_t i = 0; i < std::min<size_t>(7, sigabc.size()); i++)
823 sigabc[i] = sqrt(delta * sigabc[i]);
824
825 DblMatrix UBinv = UB;
826 UBinv.Invert();
827
828 if (sigq.size() < static_cast<size_t>(3)) {
829 sigq.clear();
830 return result;
831 }
832
833 else {
834 sigq[0] = sqrt(delta) * latOrig[0];
835 sigq[1] = sqrt(delta) * latOrig[1];
836 sigq[2] = sqrt(delta) * latOrig[2];
837 }
838
839 return delta;
840}
841
886double IndexingUtils::Optimize_6dUB(DblMatrix &UB, DblMatrix &ModUB, const std::vector<V3D> &hkl_vectors,
887 const std::vector<V3D> &mnp_vectors, const int &ModDim,
888 const std::vector<V3D> &q_vectors) {
889
890 if (UB.numRows() != 3 || UB.numCols() != 3) {
891 throw std::invalid_argument("Optimize_6dUB(): UB matrix NULL or not 3X3");
892 }
893
894 if (ModUB.numRows() != 3 || ModUB.numCols() != 3) {
895 throw std::invalid_argument("Optimize_6dUB(): ModUB matrix NULL or not 3X3");
896 }
897
898 if (hkl_vectors.size() < 3) {
899 throw std::invalid_argument("Optimize_6dUB(): Three or more indexed peaks needed to find UB");
900 }
901
902 if (hkl_vectors.size() != q_vectors.size() || hkl_vectors.size() != mnp_vectors.size()) {
903 throw std::invalid_argument("Optimize_6dUB(): Number of hkl_vectors "
904 "doesn't match number of mnp_vectors or number "
905 "of q_vectors");
906 }
907
908 if (ModDim != 0 && ModDim != 1 && ModDim != 2 && ModDim != 3)
909 throw std::invalid_argument("invalid Value for Modulation Dimension");
910
911 gsl_matrix *H_transpose = gsl_matrix_alloc(hkl_vectors.size(), ModDim + 3);
912 gsl_vector *tau = gsl_vector_alloc(ModDim + 3);
913
914 double sum_sq_error = 0;
915 // Make the H-transpose matrix from the
916 // hkl vectors and form QR factorization
917 for (size_t row = 0; row < hkl_vectors.size(); row++) {
918 for (size_t col = 0; col < 3; col++)
919 gsl_matrix_set(H_transpose, row, col, (hkl_vectors[row])[col]);
920 for (size_t col = 0; col < static_cast<size_t>(ModDim); col++)
921 gsl_matrix_set(H_transpose, row, col + 3, (mnp_vectors[row])[col]);
922 }
923
924 int returned_flag = gsl_linalg_QR_decomp(H_transpose, tau);
925
926 if (returned_flag != 0) {
927 gsl_matrix_free(H_transpose);
928 gsl_vector_free(tau);
929 throw std::runtime_error("Optimize_UB(): gsl QR_decomp failed, invalid hkl and mnp values");
930 }
931 // solve for each row of UB, using the
932 // QR factorization of and accumulate the
933 // sum of the squares of the residuals
934 gsl_vector *UB_row = gsl_vector_alloc(ModDim + 3);
935 gsl_vector *q = gsl_vector_alloc(q_vectors.size());
936 gsl_vector *residual = gsl_vector_alloc(q_vectors.size());
937
938 bool found_UB = true;
939
940 for (size_t row = 0; row < 3; row++) {
941 for (size_t i = 0; i < q_vectors.size(); i++) {
942 gsl_vector_set(q, i, (q_vectors[i])[row] / (2.0 * M_PI));
943 }
944
945 returned_flag = gsl_linalg_QR_lssolve(H_transpose, tau, q, UB_row, residual);
946
947 if (returned_flag != 0) {
948 found_UB = false;
949 }
950
951 for (size_t i = 0; i < static_cast<size_t>(ModDim + 3); i++) {
952 double value = gsl_vector_get(UB_row, i);
953 if (!std::isfinite(value))
954 found_UB = false;
955 }
956
957 V3D hklrow_values(gsl_vector_get(UB_row, 0), gsl_vector_get(UB_row, 1), gsl_vector_get(UB_row, 2));
958 V3D mnprow_values(0, 0, 0);
959
960 if (ModDim == 1)
961 mnprow_values(gsl_vector_get(UB_row, 3), 0, 0);
962 if (ModDim == 2)
963 mnprow_values(gsl_vector_get(UB_row, 3), gsl_vector_get(UB_row, 4), 0);
964 if (ModDim == 3)
965 mnprow_values(gsl_vector_get(UB_row, 3), gsl_vector_get(UB_row, 4), gsl_vector_get(UB_row, 5));
966
967 UB.setRow(row, hklrow_values);
968 ModUB.setRow(row, mnprow_values);
969
970 for (size_t i = 0; i < q_vectors.size(); i++) {
971 sum_sq_error += gsl_vector_get(residual, i) * gsl_vector_get(residual, i);
972 }
973 }
974
975 gsl_matrix_free(H_transpose);
976 gsl_vector_free(tau);
977 gsl_vector_free(UB_row);
978 gsl_vector_free(q);
979 gsl_vector_free(residual);
980
981 if (!found_UB) {
982 throw std::runtime_error("Optimize_UB(): Failed to find UB, invalid hkl or Q values");
983 }
984
985 if (!CheckUB(UB)) {
986 throw std::runtime_error("Optimize_UB(): The optimized UB is not valid");
987 }
988
989 return sum_sq_error;
990}
991
1027double IndexingUtils::Optimize_Direction(V3D &best_vec, const std::vector<int> &index_values,
1028 const std::vector<V3D> &q_vectors) {
1029 if (index_values.size() < 3) {
1030 throw std::invalid_argument("Optimize_Direction(): Three or more indexed values needed");
1031 }
1032
1033 if (index_values.size() != q_vectors.size()) {
1034 throw std::invalid_argument("Optimize_Direction(): Number of index_values != number of q_vectors");
1035 }
1036
1037 gsl_matrix *H_transpose = gsl_matrix_alloc(q_vectors.size(), 3);
1038 gsl_vector *tau = gsl_vector_alloc(3);
1039
1040 double sum_sq_error = 0;
1041 // Make the H-transpose matrix from the
1042 // q vectors and form QR factorization
1043
1044 for (size_t row = 0; row < q_vectors.size(); row++) {
1045 for (size_t col = 0; col < 3; col++) {
1046 gsl_matrix_set(H_transpose, row, col, (q_vectors[row])[col] / (2.0 * M_PI));
1047 }
1048 }
1049 int returned_flag = gsl_linalg_QR_decomp(H_transpose, tau);
1050
1051 if (returned_flag != 0) {
1052 gsl_matrix_free(H_transpose);
1053 gsl_vector_free(tau);
1054 throw std::runtime_error("Optimize_Direction(): gsl QR_decomp failed, invalid hkl values");
1055 }
1056 // solve for the best_vec, using the
1057 // QR factorization and accumulate the
1058 // sum of the squares of the residuals
1059 gsl_vector *x = gsl_vector_alloc(3);
1060 gsl_vector *indices = gsl_vector_alloc(index_values.size());
1061 gsl_vector *residual = gsl_vector_alloc(index_values.size());
1062
1063 bool found_best_vec = true;
1064
1065 for (size_t i = 0; i < index_values.size(); i++) {
1066 gsl_vector_set(indices, i, index_values[i]);
1067 }
1068
1069 returned_flag = gsl_linalg_QR_lssolve(H_transpose, tau, indices, x, residual);
1070 if (returned_flag != 0) {
1071 found_best_vec = false;
1072 }
1073
1074 for (size_t i = 0; i < 3; i++) {
1075 double value = gsl_vector_get(x, i);
1076 if (!std::isfinite(value))
1077 found_best_vec = false;
1078 }
1079
1080 best_vec(gsl_vector_get(x, 0), gsl_vector_get(x, 1), gsl_vector_get(x, 2));
1081
1082 for (size_t i = 0; i < index_values.size(); i++) {
1083 sum_sq_error += gsl_vector_get(residual, i) * gsl_vector_get(residual, i);
1084 }
1085
1086 gsl_matrix_free(H_transpose);
1087 gsl_vector_free(tau);
1088 gsl_vector_free(x);
1089 gsl_vector_free(indices);
1090 gsl_vector_free(residual);
1091
1092 if (!found_best_vec) {
1093 throw std::runtime_error("Optimize_Direction(): Failed to find best_vec, "
1094 "invalid indexes or Q values");
1095 }
1096
1097 return sum_sq_error;
1098}
1099
1134double IndexingUtils::ScanFor_UB(DblMatrix &UB, const std::vector<V3D> &q_vectors, const UnitCell &cell,
1135 double degrees_per_step, double required_tolerance) {
1136 if (UB.numRows() != 3 || UB.numCols() != 3) {
1137 throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3");
1138 }
1139
1140 auto a = cell.a();
1141 auto b = cell.b();
1142 auto c = cell.c();
1143
1144 // Precompute required trigonometric functions
1145 const auto cosAlpha = std::cos(cell.alpha() * DEG_TO_RAD);
1146 const auto cosBeta = std::cos(cell.beta() * DEG_TO_RAD);
1147 const auto cosGamma = std::cos(cell.gamma() * DEG_TO_RAD);
1148 const auto sinGamma = std::sin(cell.gamma() * DEG_TO_RAD);
1149 const auto gamma_degrees = cell.gamma();
1150
1151 V3D a_dir;
1152 V3D b_dir;
1153 V3D c_dir;
1154
1155 double num_a_steps = std::round(90.0 / degrees_per_step);
1156 int num_b_steps = boost::math::iround(4.0 * sinGamma * num_a_steps);
1157
1158 std::vector<V3D> a_dir_list = MakeHemisphereDirections(boost::numeric_cast<int>(num_a_steps));
1159
1160 std::vector<V3D> b_dir_list;
1161
1162 V3D a_dir_temp;
1163 V3D b_dir_temp;
1164 V3D c_dir_temp;
1165
1166 double error;
1167 double dot_prod;
1168 double nearest_int;
1169 int max_indexed = 0;
1170 V3D q_vec;
1171 // first select those directions
1172 // that index the most peaks
1173 std::vector<V3D> selected_a_dirs;
1174 std::vector<V3D> selected_b_dirs;
1175 std::vector<V3D> selected_c_dirs;
1176
1177 for (auto &a_dir_num : a_dir_list) {
1178 a_dir_temp = a_dir_num;
1179 a_dir_temp = V3D(a_dir_temp);
1180 a_dir_temp *= a;
1181
1182 b_dir_list = MakeCircleDirections(boost::numeric_cast<int>(num_b_steps), a_dir_temp, gamma_degrees);
1183
1184 for (auto &b_dir_num : b_dir_list) {
1185 b_dir_temp = b_dir_num;
1186 b_dir_temp = V3D(b_dir_temp);
1187 b_dir_temp *= b;
1188 c_dir_temp = makeCDir(a_dir_temp, b_dir_temp, c, cosAlpha, cosBeta, cosGamma, sinGamma);
1189 int num_indexed = 0;
1190 for (const auto &q_vector : q_vectors) {
1191 bool indexes_peak = true;
1192 q_vec = q_vector / (2.0 * M_PI);
1193 dot_prod = a_dir_temp.scalar_prod(q_vec);
1194 nearest_int = std::round(dot_prod);
1195 error = fabs(dot_prod - nearest_int);
1196 if (error > required_tolerance)
1197 indexes_peak = false;
1198 else {
1199 dot_prod = b_dir_temp.scalar_prod(q_vec);
1200 nearest_int = std::round(dot_prod);
1201 error = fabs(dot_prod - nearest_int);
1202 if (error > required_tolerance)
1203 indexes_peak = false;
1204 else {
1205 dot_prod = c_dir_temp.scalar_prod(q_vec);
1206 nearest_int = std::round(dot_prod);
1207 error = fabs(dot_prod - nearest_int);
1208 if (error > required_tolerance)
1209 indexes_peak = false;
1210 }
1211 }
1212 if (indexes_peak)
1213 num_indexed++;
1214 }
1215
1216 if (num_indexed > max_indexed) // only keep those directions that
1217 { // index the max number of peaks
1218 selected_a_dirs.clear();
1219 selected_b_dirs.clear();
1220 selected_c_dirs.clear();
1221 max_indexed = num_indexed;
1222 }
1223 if (num_indexed == max_indexed) {
1224 selected_a_dirs.emplace_back(a_dir_temp);
1225 selected_b_dirs.emplace_back(b_dir_temp);
1226 selected_c_dirs.emplace_back(c_dir_temp);
1227 }
1228 }
1229 }
1230 // now, for each such direction, find
1231 // the one that indexes closes to
1232 // integer values
1233 double min_error = 1.0e50;
1234 for (size_t dir_num = 0; dir_num < selected_a_dirs.size(); dir_num++) {
1235 a_dir_temp = selected_a_dirs[dir_num];
1236 b_dir_temp = selected_b_dirs[dir_num];
1237 c_dir_temp = selected_c_dirs[dir_num];
1238
1239 double sum_sq_error = 0.0;
1240 for (const auto &q_vector : q_vectors) {
1241 q_vec = q_vector / (2.0 * M_PI);
1242 dot_prod = a_dir_temp.scalar_prod(q_vec);
1243 nearest_int = std::round(dot_prod);
1244 error = dot_prod - nearest_int;
1245 sum_sq_error += error * error;
1246
1247 dot_prod = b_dir_temp.scalar_prod(q_vec);
1248 nearest_int = std::round(dot_prod);
1249 error = dot_prod - nearest_int;
1250 sum_sq_error += error * error;
1251
1252 dot_prod = c_dir_temp.scalar_prod(q_vec);
1253 nearest_int = std::round(dot_prod);
1254 error = dot_prod - nearest_int;
1255 sum_sq_error += error * error;
1256 }
1257
1258 if (sum_sq_error < min_error) {
1259 min_error = sum_sq_error;
1260 a_dir = a_dir_temp;
1261 b_dir = b_dir_temp;
1262 c_dir = c_dir_temp;
1263 }
1264 }
1265
1266 if (!OrientedLattice::GetUB(UB, a_dir, b_dir, c_dir)) {
1267 throw std::runtime_error("UB could not be formed, invert matrix failed");
1268 }
1269
1270 return min_error;
1271}
1272
1297size_t IndexingUtils::ScanFor_Directions(std::vector<V3D> &directions, const std::vector<V3D> &q_vectors, double min_d,
1298 double max_d, double required_tolerance, double degrees_per_step) {
1299 double error;
1300 double fit_error;
1301 double dot_prod;
1302 double nearest_int;
1303 int max_indexed = 0;
1304 V3D q_vec;
1305 // first, make hemisphere of possible directions
1306 // with specified resolution.
1307 int num_steps = boost::math::iround(90.0 / degrees_per_step);
1308 std::vector<V3D> full_list = MakeHemisphereDirections(num_steps);
1309 // Now, look for possible real-space unit cell edges
1310 // by checking for vectors with length between
1311 // min_d and max_d that would index the most peaks,
1312 // in some direction, keeping the shortest vector
1313 // for each direction where the max peaks are indexed
1314 double delta_d = 0.1f;
1315 int n_steps = boost::math::iround(1.0 + (max_d - min_d) / delta_d);
1316
1317 std::vector<V3D> selected_dirs;
1318 V3D dir_temp;
1319
1320 for (const auto &current_dir : full_list) {
1321 for (int step = 0; step <= n_steps; step++) {
1322 dir_temp = current_dir;
1323 dir_temp *= (min_d + step * delta_d); // increasing size
1324
1325 int num_indexed = 0;
1326 for (const auto &q_vector : q_vectors) {
1327 q_vec = q_vector / (2.0 * M_PI);
1328 dot_prod = dir_temp.scalar_prod(q_vec);
1329 nearest_int = std::round(dot_prod);
1330 error = fabs(dot_prod - nearest_int);
1331 if (error <= required_tolerance)
1332 num_indexed++;
1333 }
1334
1335 if (num_indexed > max_indexed) // only keep those directions that
1336 { // index the max number of peaks
1337 selected_dirs.clear();
1338 max_indexed = num_indexed;
1339 }
1340 if (num_indexed >= max_indexed) {
1341 selected_dirs.emplace_back(dir_temp);
1342 }
1343 }
1344 }
1345 // Now, optimize each direction and discard possible
1346 // unit cell edges that are duplicates, putting the
1347 // new smaller list in the vector "directions"
1348 std::vector<int> index_vals;
1349 std::vector<V3D> indexed_qs;
1350 index_vals.reserve(q_vectors.size());
1351 indexed_qs.reserve(q_vectors.size());
1352
1353 directions.clear();
1354 V3D current_dir;
1355 V3D diff;
1356 for (auto &selected_dir : selected_dirs) {
1357 current_dir = selected_dir;
1358
1359 GetIndexedPeaks_1D(current_dir, q_vectors, required_tolerance, index_vals, indexed_qs, fit_error);
1360
1361 Optimize_Direction(current_dir, index_vals, indexed_qs);
1362
1363 double length = current_dir.norm();
1364 if (length >= min_d && length <= max_d) // only keep if within range
1365 {
1366 bool duplicate = false;
1367 for (auto &direction : directions) {
1368 dir_temp = direction;
1369 diff = current_dir - dir_temp;
1370 // discard same direction
1371 if (diff.norm() < 0.001) {
1372 duplicate = true;
1373 } else {
1374 diff = current_dir + dir_temp;
1375 // discard opposite direction
1376 if (diff.norm() < 0.001) {
1377 duplicate = true;
1378 }
1379 }
1380 }
1381 if (!duplicate) {
1382 directions.emplace_back(current_dir);
1383 }
1384 }
1385 }
1386 return max_indexed;
1387}
1388
1415size_t IndexingUtils::FFTScanFor_Directions(std::vector<V3D> &directions, const std::vector<V3D> &q_vectors,
1416 double min_d, double max_d, double required_tolerance,
1417 double degrees_per_step) {
1418 constexpr size_t N_FFT_STEPS = 512;
1419 constexpr size_t HALF_FFT_STEPS = 256;
1420
1421 double fit_error;
1422 int max_indexed = 0;
1423
1424 // first, make hemisphere of possible directions
1425 // with specified resolution.
1426 int num_steps = boost::math::iround(90.0 / degrees_per_step);
1427 std::vector<V3D> full_list = MakeHemisphereDirections(num_steps);
1428
1429 // find the maximum magnitude of Q to set range
1430 // needed for FFT
1431 double max_mag_Q = 0;
1432 for (size_t q_num = 1; q_num < q_vectors.size(); q_num++) {
1433 double mag_Q = q_vectors[q_num].norm() / (2.0 * M_PI);
1434 if (mag_Q > max_mag_Q)
1435 max_mag_Q = mag_Q;
1436 }
1437
1438 max_mag_Q *= 1.1f; // allow for a little "headroom" for FFT range
1439
1440 // apply the FFT to each of the directions, and
1441 // keep track of their maximum magnitude past DC
1442 double max_mag_fft;
1443 std::vector<double> max_fft_val;
1444 max_fft_val.resize(full_list.size());
1445
1446 double projections[N_FFT_STEPS];
1447 double magnitude_fft[HALF_FFT_STEPS];
1448
1449 double index_factor = N_FFT_STEPS / max_mag_Q; // maps |proj Q| to index
1450
1451 for (size_t dir_num = 0; dir_num < full_list.size(); dir_num++) {
1452 V3D current_dir = full_list[dir_num];
1453 max_mag_fft = GetMagFFT(q_vectors, current_dir, N_FFT_STEPS, projections, index_factor, magnitude_fft);
1454
1455 max_fft_val[dir_num] = max_mag_fft;
1456 }
1457 // find the directions with the 500 largest
1458 // fft values, and place them in temp_dirs vector
1459 int N_TO_TRY = 500;
1460
1461 std::vector<double> max_fft_copy;
1462 max_fft_copy.resize(full_list.size());
1463 for (size_t i = 0; i < max_fft_copy.size(); i++) {
1464 max_fft_copy[i] = max_fft_val[i];
1465 }
1466
1467 std::sort(max_fft_copy.begin(), max_fft_copy.end());
1468
1469 size_t index = max_fft_copy.size() - 1;
1470 max_mag_fft = max_fft_copy[index];
1471
1472 double threshold = max_mag_fft;
1473 while ((index > max_fft_copy.size() - N_TO_TRY) && threshold >= max_mag_fft / 2) {
1474 index--;
1475 threshold = max_fft_copy[index];
1476 }
1477
1478 std::vector<V3D> temp_dirs;
1479 for (size_t i = 0; i < max_fft_val.size(); i++) {
1480 if (max_fft_val[i] >= threshold) {
1481 temp_dirs.emplace_back(full_list[i]);
1482 }
1483 }
1484 // now scan through temp_dirs and use the
1485 // FFT to find the cell edge length that
1486 // corresponds to the max_mag_fft. Only keep
1487 // directions with length nearly in bounds
1488 V3D temp;
1489 std::vector<V3D> temp_dirs_2;
1490
1491 for (auto &temp_dir : temp_dirs) {
1492 GetMagFFT(q_vectors, temp_dir, N_FFT_STEPS, projections, index_factor, magnitude_fft);
1493
1494 double position = GetFirstMaxIndex(magnitude_fft, HALF_FFT_STEPS, threshold);
1495 if (position > 0) {
1496 double q_val = max_mag_Q / position;
1497 double d_val = 1 / q_val;
1498 if (d_val >= 0.8 * min_d && d_val <= 1.2 * max_d) {
1499 temp = temp_dir * d_val;
1500 temp_dirs_2.emplace_back(temp);
1501 }
1502 }
1503 }
1504 // look at how many peaks were indexed
1505 // for each of the initial directions
1506 max_indexed = 0;
1507 int num_indexed;
1508 V3D current_dir;
1509 for (auto &dir_num : temp_dirs_2) {
1510 current_dir = dir_num;
1511 num_indexed = NumberIndexed_1D(current_dir, q_vectors, required_tolerance);
1512 if (num_indexed > max_indexed)
1513 max_indexed = num_indexed;
1514 }
1515
1516 // only keep original directions that index
1517 // at least 50% of max num indexed
1518 temp_dirs.clear();
1519 for (auto &dir_num : temp_dirs_2) {
1520 current_dir = dir_num;
1521 num_indexed = NumberIndexed_1D(current_dir, q_vectors, required_tolerance);
1522 if (num_indexed >= 0.50 * max_indexed)
1523 temp_dirs.emplace_back(current_dir);
1524 }
1525 // refine directions and again find the
1526 // max number indexed, for the optimized
1527 // directions
1528 max_indexed = 0;
1529 std::vector<int> index_vals;
1530 std::vector<V3D> indexed_qs;
1531 for (auto &temp_dir : temp_dirs) {
1532 num_indexed = GetIndexedPeaks_1D(temp_dir, q_vectors, required_tolerance, index_vals, indexed_qs, fit_error);
1533 try {
1534 int count = 0;
1535 while (count < 5) // 5 iterations should be enough for
1536 { // the optimization to stabilize
1537 Optimize_Direction(temp_dir, index_vals, indexed_qs);
1538
1539 num_indexed = GetIndexedPeaks_1D(temp_dir, q_vectors, required_tolerance, index_vals, indexed_qs, fit_error);
1540 if (num_indexed > max_indexed)
1541 max_indexed = num_indexed;
1542
1543 count++;
1544 }
1545 } catch (...) {
1546 // don't continue to refine if the direction fails to optimize properly
1547 }
1548 }
1549 // discard those with length out of bounds
1550 temp_dirs_2.clear();
1551 for (auto &temp_dir : temp_dirs) {
1552 current_dir = temp_dir;
1553 double length = current_dir.norm();
1554 if (length >= 0.8 * min_d && length <= 1.2 * max_d)
1555 temp_dirs_2.emplace_back(current_dir);
1556 }
1557 // only keep directions that index at
1558 // least 75% of the max number of peaks
1559 temp_dirs.clear();
1560 for (auto &dir_num : temp_dirs_2) {
1561 current_dir = dir_num;
1562 num_indexed = NumberIndexed_1D(current_dir, q_vectors, required_tolerance);
1563 if (num_indexed > max_indexed * 0.75)
1564 temp_dirs.emplace_back(current_dir);
1565 }
1566
1567 std::sort(temp_dirs.begin(), temp_dirs.end(), V3D::compareMagnitude);
1568
1569 // discard duplicates:
1570 double len_tol = 0.1; // 10% tolerance for lengths
1571 double ang_tol = 5.0; // 5 degree tolerance for angles
1572 DiscardDuplicates(directions, temp_dirs, q_vectors, required_tolerance, len_tol, ang_tol);
1573
1574 return max_indexed;
1575}
1576
1600double IndexingUtils::GetMagFFT(const std::vector<V3D> &q_vectors, const V3D &current_dir, const size_t N,
1601 double projections[], double index_factor, double magnitude_fft[]) {
1602 for (size_t i = 0; i < N; i++) {
1603 projections[i] = 0.0;
1604 }
1605 // project onto direction
1606 V3D q_vec;
1607 for (const auto &q_vector : q_vectors) {
1608 q_vec = q_vector / (2.0 * M_PI);
1609 double dot_prod = current_dir.scalar_prod(q_vec);
1610 auto index = static_cast<size_t>(fabs(index_factor * dot_prod));
1611 if (index < N)
1612 projections[index] += 1;
1613 else
1614 projections[N - 1] += 1; // This should not happen, but trap it in
1615 } // case of rounding errors.
1616
1617 // get the |FFT|
1618 gsl_fft_real_radix2_transform(projections, 1, N);
1619 for (size_t i = 1; i < N / 2; i++) {
1620 magnitude_fft[i] = sqrt(projections[i] * projections[i] + projections[N - i] * projections[N - i]);
1621 }
1622
1623 magnitude_fft[0] = fabs(projections[0]);
1624
1625 size_t dc_end = 5; // we may need a better estimate of this
1626 double max_mag_fft = 0.0;
1627 for (size_t i = dc_end; i < N / 2; i++)
1628 if (magnitude_fft[i] > max_mag_fft)
1629 max_mag_fft = magnitude_fft[i];
1630
1631 return max_mag_fft;
1632}
1633
1646double IndexingUtils::GetFirstMaxIndex(const double magnitude_fft[], size_t N, double threshold) {
1647 // find first local min below threshold
1648 size_t i = 2;
1649 bool found_min = false;
1650 double val;
1651 while (i < N - 1 && !found_min) {
1652 val = magnitude_fft[i];
1653 if (val < threshold && val <= magnitude_fft[i - 1] && val <= magnitude_fft[i + 1])
1654 found_min = true;
1655 i++;
1656 }
1657
1658 if (!found_min)
1659 return -1;
1660 // find next local max above threshold
1661 bool found_max = false;
1662 while (i < N - 1 && !found_max) {
1663 val = magnitude_fft[i];
1664 if (val >= threshold && val >= magnitude_fft[i - 1] && val >= magnitude_fft[i + 1])
1665 found_max = true;
1666 else
1667 i++;
1668 }
1669
1670 if (found_max) {
1671 double sum = 0;
1672 double w_sum = 0;
1673 for (size_t j = i - 2; j < std::min(N, i + 3); j++) {
1674 sum += static_cast<double>(j) * magnitude_fft[j];
1675 w_sum += magnitude_fft[j];
1676 }
1677 return sum / w_sum;
1678 } else
1679 return -1;
1680}
1681
1709bool IndexingUtils::FormUB_From_abc_Vectors(DblMatrix &UB, const std::vector<V3D> &directions, size_t a_index,
1710 double min_d, double max_d) {
1711 if (UB.numRows() != 3 || UB.numCols() != 3) {
1712 throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3");
1713 }
1714
1715 size_t index = a_index;
1716 V3D a_dir = directions[index];
1717 index++;
1718 // the possible range of d-values
1719 // implies a bound on the minimum
1720 // angle between a,b, c vectors.
1721 double min_deg = (RAD_TO_DEG)*atan(2.0 * std::min(0.2, min_d / max_d));
1722
1723 double epsilon = 5; // tolerance on right angle (degrees)
1724 V3D b_dir;
1725 bool b_found = false;
1726 while (!b_found && index < directions.size()) {
1727 V3D vec = directions[index];
1728 double gamma = a_dir.angle(vec) * RAD_TO_DEG;
1729
1730 if (gamma >= min_deg && (180. - gamma) >= min_deg) {
1731 b_dir = vec;
1732 if (gamma > 90 + epsilon) // try for Nigli cell with angles <= 90
1733 b_dir *= -1.0;
1734 b_found = true;
1735 index++;
1736 } else
1737 index++;
1738 }
1739
1740 if (!b_found)
1741 return false;
1742
1743 V3D c_dir;
1744 bool c_found = false;
1745
1746 const V3D perp = normalize(a_dir.cross_prod(b_dir));
1747 double perp_ang;
1748 double alpha;
1749 double beta;
1750 while (!c_found && index < directions.size()) {
1751 V3D vec = directions[index];
1752 int factor = 1;
1753 while (!c_found && factor >= -1) // try c in + or - direction
1754 {
1755 c_dir = vec;
1756 c_dir *= factor;
1757 perp_ang = perp.angle(c_dir) * RAD_TO_DEG;
1758 alpha = b_dir.angle(c_dir) * RAD_TO_DEG;
1759 beta = a_dir.angle(c_dir) * RAD_TO_DEG;
1760 // keep a,b,c right handed by choosing
1761 // c in general directiion of a X b
1762 if (perp_ang < 90. - epsilon && alpha >= min_deg && (180. - alpha) >= min_deg && beta >= min_deg &&
1763 (180. - beta) >= min_deg) {
1764 c_found = true;
1765 }
1766 factor -= 2;
1767 }
1768 if (!c_found)
1769 index++;
1770 }
1771
1772 if (!c_found) {
1773 return false;
1774 }
1775 // now build the UB matrix from a,b,c
1776 if (!OrientedLattice::GetUB(UB, a_dir, b_dir, c_dir)) {
1777 throw std::runtime_error("UB could not be formed, invert matrix failed");
1778 }
1779
1780 return true;
1781}
1782
1809bool IndexingUtils::FormUB_From_abc_Vectors(DblMatrix &UB, const std::vector<V3D> &directions,
1810 const std::vector<V3D> &q_vectors, double req_tolerance, double min_vol) {
1811 if (UB.numRows() != 3 || UB.numCols() != 3) {
1812 throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3");
1813 }
1814
1815 int max_indexed = 0;
1816 V3D a_dir(0, 0, 0);
1817 V3D b_dir(0, 0, 0);
1818 V3D c_dir(0, 0, 0);
1819 V3D a_temp;
1820 V3D b_temp;
1821 V3D c_temp;
1822 V3D acrossb;
1823 double vol;
1824
1825 for (size_t i = 0; i < directions.size() - 2; i++) {
1826 a_temp = directions[i];
1827 for (size_t j = i + 1; j < directions.size() - 1; j++) {
1828 b_temp = directions[j];
1829 acrossb = a_temp.cross_prod(b_temp);
1830 for (size_t k = j + 1; k < directions.size(); k++) {
1831 c_temp = directions[k];
1832 vol = fabs(acrossb.scalar_prod(c_temp));
1833 if (vol > min_vol) {
1834 const auto num_indexed = NumberIndexed_3D(a_temp, b_temp, c_temp, q_vectors, req_tolerance);
1835
1836 // Requiring 20% more indexed with longer edge lengths, favors
1837 // the smaller unit cells.
1838 if (num_indexed > 1.20 * max_indexed) {
1839 max_indexed = num_indexed;
1840 a_dir = a_temp;
1841 b_dir = b_temp;
1842 c_dir = c_temp;
1843 }
1844 }
1845 }
1846 }
1847 }
1848
1849 if (max_indexed <= 0) {
1850 return false;
1851 }
1852 // force a,b,c to be right handed
1853 acrossb = a_dir.cross_prod(b_dir);
1854 if (acrossb.scalar_prod(c_dir) < 0) {
1855 c_dir = c_dir * (-1.0);
1856 }
1857 // now build the UB matrix from a,b,c
1858 if (!OrientedLattice::GetUB(UB, a_dir, b_dir, c_dir)) {
1859 throw std::runtime_error("UB could not be formed, invert matrix failed");
1860 }
1861
1862 return true;
1863}
1864
1884V3D IndexingUtils::makeCDir(const V3D &a_dir, const V3D &b_dir, const double c, const double cosAlpha,
1885 const double cosBeta, const double cosGamma, const double sinGamma) {
1886
1887 double c1 = c * cosBeta;
1888 double c2 = c * (cosAlpha - cosGamma * cosBeta) / sinGamma;
1889 double V =
1890 sqrt(1 - cosAlpha * cosAlpha - cosBeta * cosBeta - cosGamma * cosGamma + 2 * cosAlpha * cosBeta * cosGamma);
1891 double c3 = c * V / sinGamma;
1892
1893 auto basis_1 = Mantid::Kernel::toVector3d(a_dir).normalized();
1894 auto basis_3 = Mantid::Kernel::toVector3d(a_dir).cross(Mantid::Kernel::toVector3d(b_dir)).normalized();
1895 auto basis_2 = basis_3.cross(basis_1).normalized();
1896
1897 return Mantid::Kernel::toV3D(basis_1 * c1 + basis_2 * c2 + basis_3 * c3);
1898}
1899
1925void IndexingUtils::DiscardDuplicates(std::vector<V3D> &new_list, std::vector<V3D> &directions,
1926 const std::vector<V3D> &q_vectors, double required_tolerance, double len_tol,
1927 double ang_tol) {
1928 new_list.clear();
1929 std::vector<V3D> temp;
1930
1931 V3D current_dir;
1932 V3D next_dir;
1933 V3D zero_vec(0, 0, 0);
1934
1935 double next_length;
1936 double length_diff;
1937 double angle;
1938 size_t dir_num = 0;
1939 size_t check_index;
1940 bool new_dir;
1941
1942 while (dir_num < directions.size()) // put sequence of similar vectors
1943 { // in list temp
1944 current_dir = directions[dir_num];
1945 double current_length = current_dir.norm();
1946 dir_num++;
1947
1948 if (current_length > 0) // skip any zero vectors
1949 {
1950 temp.clear();
1951 temp.emplace_back(current_dir);
1952 check_index = dir_num;
1953 new_dir = false;
1954 while (check_index < directions.size() && !new_dir) {
1955 next_dir = directions[check_index];
1956 next_length = next_dir.norm();
1957 if (next_length > 0) {
1958 length_diff = fabs(next_dir.norm() - current_length);
1959 if ((length_diff / current_length) < len_tol) // continue scan
1960 {
1961 angle = current_dir.angle(next_dir) * RAD_TO_DEG;
1962 if ((std::isnan)(angle))
1963 angle = 0;
1964 if ((angle < ang_tol) || (angle > (180.0 - ang_tol))) {
1965 temp.emplace_back(next_dir);
1966 directions[check_index] = zero_vec; // mark off this direction
1967 } // since it was duplicate
1968
1969 check_index++; // keep checking all vectors with
1970 } // essentially the same length
1971 else
1972 new_dir = true; // we only know we have a new
1973 // direction if the length is
1974 // different, since list is
1975 } // sorted by length !
1976 else
1977 check_index++; // just move on
1978 }
1979 // now scan through temp list to
1980 int max_indexed = 0; // find the one that indexes most
1981
1982 long max_i = -1;
1983 for (size_t i = 0; i < temp.size(); i++) {
1984 int num_indexed = NumberIndexed_1D(temp[i], q_vectors, required_tolerance);
1985 if (num_indexed > max_indexed) {
1986 max_indexed = num_indexed;
1987 max_i = static_cast<long>(i);
1988 }
1989 }
1990
1991 if (max_indexed > 0) // don't bother to add any direction
1992 { // that doesn't index anything
1993 new_list.emplace_back(temp[max_i]);
1994 }
1995 }
1996 }
1997
1998 directions.clear();
1999}
2000
2006 for (size_t i = 0; i < 3; i++) {
2007 hkl[i] = std::round(hkl[i]);
2008 }
2009}
2010
2020void IndexingUtils::RoundHKLs(std::vector<V3D> &hkl_list) {
2021 for (auto &entry : hkl_list) {
2022 RoundHKL(entry);
2023 }
2024}
2025
2031inline bool withinTol(const double value, const double tolerance) {
2032 double myVal = fabs(value);
2033 if (myVal - floor(myVal) < tolerance)
2034 return true;
2035 if (floor(myVal + 1.) - myVal < tolerance)
2036 return true;
2037 return false;
2038}
2039
2053bool IndexingUtils::ValidIndex(const V3D &hkl, double tolerance) {
2054 if ((hkl[0] == 0.) && (hkl[1] == 0.) && (hkl[2] == 0.))
2055 return false;
2056
2057 return (withinTol(hkl[0], tolerance) && withinTol(hkl[1], tolerance) && withinTol(hkl[2], tolerance));
2058}
2059
2070int IndexingUtils::NumberOfValidIndexes(const std::vector<V3D> &hkls, double tolerance, double &average_error) {
2071
2072 double h_error;
2073 double k_error;
2074 double l_error;
2075 double total_error = 0;
2076 int count = 0;
2077 for (const auto &hkl : hkls) {
2078 if (ValidIndex(hkl, tolerance)) {
2079 count++;
2080 h_error = fabs(round(hkl[0]) - hkl[0]);
2081 k_error = fabs(round(hkl[1]) - hkl[1]);
2082 l_error = fabs(round(hkl[2]) - hkl[2]);
2083 total_error += h_error + k_error + l_error;
2084 }
2085 }
2086
2087 if (count > 0)
2088 average_error = total_error / (3.0 * static_cast<double>(count));
2089 else
2090 average_error = 0.0;
2091
2092 return count;
2093}
2094
2096double IndexingUtils::IndexingError(const DblMatrix &UB, const std::vector<V3D> &hkls,
2097 const std::vector<V3D> &q_vectors) {
2098 DblMatrix UB_inverse(UB);
2099 if (CheckUB(UB)) {
2100 UB_inverse.Invert();
2101 } else {
2102 throw std::runtime_error("The UB in IndexingError() is not valid");
2103 }
2104 if (hkls.size() != q_vectors.size()) {
2105 throw std::runtime_error("Different size hkl and q_vectors in IndexingError()");
2106 }
2107
2108 double total_error = 0;
2109 V3D hkl;
2110 for (size_t i = 0; i < hkls.size(); i++) {
2111 hkl = UB_inverse * q_vectors[i] / (2.0 * M_PI);
2112
2113 double h_error = fabs(hkl[0] - round(hkl[0]));
2114 double k_error = fabs(hkl[1] - round(hkl[1]));
2115 double l_error = fabs(hkl[2] - round(hkl[2]));
2116 total_error += h_error + k_error + l_error;
2117 }
2118
2119 if (!hkls.empty())
2120 return total_error / (3.0 * static_cast<double>(hkls.size()));
2121 else
2122 return 0;
2123}
2124
2137 if (UB.numRows() != 3 || UB.numCols() != 3)
2138 return false;
2139
2140 for (size_t row = 0; row < 3; row++)
2141 for (size_t col = 0; col < 3; col++) {
2142 if (!std::isfinite(UB[row][col])) {
2143 return false;
2144 }
2145 }
2146
2147 double det = UB.determinant();
2148
2149 double abs_det = fabs(det);
2150
2151 return !(abs_det > 10 || abs_det < 1e-12);
2152}
2153
2175int IndexingUtils::NumberIndexed(const DblMatrix &UB, const std::vector<V3D> &q_vectors, double tolerance) {
2176 int count = 0;
2177
2178 DblMatrix UB_inverse(UB);
2179 if (CheckUB(UB)) {
2180 UB_inverse.Invert();
2181 } else {
2182 throw std::runtime_error("The UB in NumberIndexed() is not valid");
2183 }
2184
2185 V3D hkl;
2186 for (const auto &q_vector : q_vectors) {
2187 hkl = UB_inverse * q_vector / (2.0 * M_PI);
2188 if (ValidIndex(hkl, tolerance)) {
2189 count++;
2190 }
2191 }
2192
2193 return count;
2194}
2195
2213int IndexingUtils::NumberIndexed_1D(const V3D &direction, const std::vector<V3D> &q_vectors, double tolerance) {
2214 if (direction.norm() == 0)
2215 return 0;
2216
2217 int count = 0;
2218
2219 for (const auto &q_vector : q_vectors) {
2220 double proj_value = direction.scalar_prod(q_vector) / (2.0 * M_PI);
2221 double error = fabs(proj_value - std::round(proj_value));
2222 if (error <= tolerance) {
2223 count++;
2224 }
2225 }
2226
2227 return count;
2228}
2229
2251int IndexingUtils::NumberIndexed_3D(const V3D &a_dir, const V3D &b_dir, const V3D &c_dir,
2252 const std::vector<V3D> &q_vectors, double tolerance) {
2253 if (a_dir.norm() == 0 || b_dir.norm() == 0 || c_dir.norm() == 0)
2254 return 0;
2255
2256 V3D hkl_vec;
2257 int count = 0;
2258
2259 for (const auto &q_vector : q_vectors) {
2260 hkl_vec[0] = a_dir.scalar_prod(q_vector) / (2.0 * M_PI);
2261 hkl_vec[1] = b_dir.scalar_prod(q_vector) / (2.0 * M_PI);
2262 hkl_vec[2] = c_dir.scalar_prod(q_vector) / (2.0 * M_PI);
2263 if (ValidIndex(hkl_vec, tolerance)) {
2264 count++;
2265 }
2266 }
2267
2268 return count;
2269}
2270
2294int IndexingUtils::CalculateMillerIndices(const DblMatrix &UB, const std::vector<V3D> &q_vectors, double tolerance,
2295 std::vector<V3D> &miller_indices, double &ave_error) {
2296 DblMatrix UB_inverse(UB);
2297
2298 if (CheckUB(UB)) {
2299 UB_inverse.Invert();
2300 } else {
2301 throw std::runtime_error("The UB in CalculateMillerIndices() is not valid");
2302 }
2303
2304 miller_indices.clear();
2305 miller_indices.reserve(q_vectors.size());
2306
2307 int count = 0;
2308 ave_error = 0.0;
2309 for (const auto &q_vector : q_vectors) {
2310 V3D hkl;
2311 if (CalculateMillerIndices(UB_inverse, q_vector, tolerance, hkl)) {
2312 count++;
2313 ave_error += hkl.hklError();
2314 }
2315 miller_indices.emplace_back(hkl);
2316 }
2317
2318 if (count > 0) {
2319 ave_error /= (3.0 * count);
2320 }
2321
2322 return count;
2323}
2324
2344bool IndexingUtils::CalculateMillerIndices(const DblMatrix &inverseUB, const V3D &q_vector, double tolerance,
2345 V3D &miller_indices) {
2346 miller_indices = CalculateMillerIndices(inverseUB, q_vector);
2347 if (ValidIndex(miller_indices, tolerance)) {
2348 return true;
2349 } else {
2350 miller_indices = V3D(0, 0, 0);
2351 return false;
2352 }
2353}
2354
2366V3D IndexingUtils::CalculateMillerIndices(const DblMatrix &inverseUB, const V3D &q_vector) {
2367 return inverseUB * q_vector / (2.0 * M_PI);
2368}
2369
2398int IndexingUtils::GetIndexedPeaks_1D(const V3D &direction, const std::vector<V3D> &q_vectors,
2399 double required_tolerance, std::vector<int> &index_vals,
2400 std::vector<V3D> &indexed_qs, double &fit_error) {
2401 int num_indexed = 0;
2402 index_vals.clear();
2403 indexed_qs.clear();
2404 index_vals.reserve(q_vectors.size());
2405 indexed_qs.reserve(q_vectors.size());
2406
2407 fit_error = 0;
2408
2409 if (direction.norm() == 0) // special case, zero vector will NOT index
2410 return 0; // any peaks, even though dot product
2411 // with Q vectors is always an integer!
2412
2413 for (const auto &q_vector : q_vectors) {
2414 double proj_value = direction.scalar_prod(q_vector) / (2.0 * M_PI);
2415 double nearest_int = std::round(proj_value);
2416 double error = fabs(proj_value - nearest_int);
2417 if (error < required_tolerance) {
2418 fit_error += error * error;
2419 indexed_qs.emplace_back(q_vector);
2420 index_vals.emplace_back(boost::numeric_cast<int>(nearest_int));
2421 num_indexed++;
2422 }
2423 }
2424
2425 return num_indexed;
2426}
2427
2464int IndexingUtils::GetIndexedPeaks_3D(const V3D &direction_1, const V3D &direction_2, const V3D &direction_3,
2465 const std::vector<V3D> &q_vectors, double required_tolerance,
2466 std::vector<V3D> &miller_indices, std::vector<V3D> &indexed_qs,
2467 double &fit_error) {
2468 V3D hkl;
2469 int num_indexed = 0;
2470
2471 miller_indices.clear();
2472 miller_indices.reserve(q_vectors.size());
2473
2474 indexed_qs.clear();
2475 indexed_qs.reserve(q_vectors.size());
2476
2477 fit_error = 0;
2478
2479 for (const auto &q_vector : q_vectors) {
2480 double projected_h = direction_1.scalar_prod(q_vector) / (2.0 * M_PI);
2481 double projected_k = direction_2.scalar_prod(q_vector) / (2.0 * M_PI);
2482 double projected_l = direction_3.scalar_prod(q_vector) / (2.0 * M_PI);
2483
2484 hkl(projected_h, projected_k, projected_l);
2485
2486 if (ValidIndex(hkl, required_tolerance)) {
2487 double h_int = std::round(projected_h);
2488 double k_int = std::round(projected_k);
2489 double l_int = std::round(projected_l);
2490
2491 double h_error = fabs(projected_h - h_int);
2492 double k_error = fabs(projected_k - k_int);
2493 double l_error = fabs(projected_l - l_int);
2494
2495 fit_error += h_error * h_error + k_error * k_error + l_error * l_error;
2496
2497 indexed_qs.emplace_back(q_vector);
2498
2499 V3D miller_ind(h_int, k_int, l_int);
2500 miller_indices.emplace_back(miller_ind);
2501
2502 num_indexed++;
2503 }
2504 }
2505
2506 return num_indexed;
2507}
2508
2534int IndexingUtils::GetIndexedPeaks(const DblMatrix &UB, const std::vector<V3D> &q_vectors, double required_tolerance,
2535 std::vector<V3D> &miller_indices, std::vector<V3D> &indexed_qs, double &fit_error) {
2536 double error;
2537 int num_indexed = 0;
2538 V3D hkl;
2539
2540 miller_indices.clear();
2541 miller_indices.reserve(q_vectors.size());
2542
2543 indexed_qs.clear();
2544 indexed_qs.reserve(q_vectors.size());
2545
2546 fit_error = 0;
2547
2548 DblMatrix UB_inverse(UB);
2549 if (CheckUB(UB)) {
2550 UB_inverse.Invert();
2551 } else {
2552 throw std::runtime_error("The UB in GetIndexedPeaks() is not valid");
2553 }
2554
2555 for (const auto &q_vector : q_vectors) {
2556 hkl = UB_inverse * q_vector / (2.0 * M_PI);
2557
2558 if (ValidIndex(hkl, required_tolerance)) {
2559 for (int i = 0; i < 3; i++) {
2560 error = hkl[i] - std::round(hkl[i]);
2561 fit_error += error * error;
2562 }
2563
2564 indexed_qs.emplace_back(q_vector);
2565
2566 V3D miller_ind(round(hkl[0]), round(hkl[1]), round(hkl[2]));
2567 miller_indices.emplace_back(miller_ind);
2568
2569 num_indexed++;
2570 }
2571 }
2572
2573 return num_indexed;
2574}
2575
2594std::vector<V3D> IndexingUtils::MakeHemisphereDirections(int n_steps) {
2595 if (n_steps <= 0) {
2596 throw std::invalid_argument("MakeHemisphereDirections(): n_steps must be greater than 0");
2597 }
2598
2599 std::vector<V3D> direction_list;
2600
2601 double angle_step = M_PI / (2 * n_steps);
2602
2603 for (int iPhi = 0; iPhi < n_steps + 1; ++iPhi) {
2604 double phi = static_cast<double>(iPhi) * angle_step;
2605 double r = sin(phi);
2606
2607 int n_theta = boost::math::iround(2. * M_PI * r / angle_step);
2608
2609 double theta_step;
2610 if (n_theta == 0) { // n = ( 0, 1, 0 ). Just
2611 theta_step = 2. * M_PI + 1.; // use one vector at the pole
2612 n_theta = 1;
2613 } else {
2614 theta_step = 2. * M_PI / static_cast<double>(n_theta);
2615 }
2616
2617 // use half the equator to avoid vectors that are the negatives of other
2618 // vectors in the list.
2619 if (fabs(phi - M_PI / 2.) < angle_step / 2.) {
2620 n_theta /= 2;
2621 }
2622
2623 for (int jTheta = 0; jTheta < n_theta; ++jTheta) {
2624 double theta = static_cast<double>(jTheta) * theta_step;
2625 direction_list.emplace_back(r * cos(theta), cos(phi), r * sin(theta));
2626 }
2627 }
2628 return direction_list;
2629}
2630
2645std::vector<V3D> IndexingUtils::MakeCircleDirections(int n_steps, const Mantid::Kernel::V3D &axis,
2646 double angle_degrees) {
2647 if (n_steps <= 0) {
2648 throw std::invalid_argument("MakeCircleDirections(): n_steps must be greater than 0");
2649 }
2650 // first get a vector perpendicular to axis
2651 double max_component = fabs(axis[0]);
2652 double min_component = max_component;
2653 size_t min_index = 0;
2654 for (size_t i = 1; i < 3; i++) {
2655 if (fabs(axis[i]) < min_component) {
2656 min_component = fabs(axis[i]);
2657 min_index = i;
2658 }
2659 if (fabs(axis[i]) > max_component) {
2660 max_component = fabs(axis[i]);
2661 }
2662 }
2663
2664 if (max_component == 0) {
2665 throw std::invalid_argument("MakeCircleDirections(): Axis vector must be non-zero!");
2666 }
2667
2668 V3D second_vec(0, 0, 0);
2669 second_vec[min_index] = 1;
2670
2671 const V3D perp_vec = normalize(second_vec.cross_prod(axis));
2672
2673 // next get a vector that is the specified
2674 // number of degrees away from the axis
2675 Quat rotation_1(angle_degrees, perp_vec);
2676 V3D vector_at_angle(axis);
2677 rotation_1.rotate(vector_at_angle);
2678 vector_at_angle.normalize();
2679
2680 // finally, form the circle of directions
2681 // consisting of vectors that are at the
2682 // specified angle from the original axis
2683 double angle_step = 360.0 / n_steps;
2684 Quat rotation_2(0, axis);
2685 std::vector<V3D> directions;
2686 for (int i = 0; i < n_steps; i++) {
2687 V3D vec(vector_at_angle);
2688 rotation_2.setAngleAxis(i * angle_step, axis);
2689 rotation_2.rotate(vec);
2690 directions.emplace_back(vec);
2691 }
2692
2693 return directions;
2694}
2695
2726int IndexingUtils::SelectDirection(V3D &best_direction, const std::vector<V3D> &q_vectors,
2727 const std::vector<V3D> &direction_list, double plane_spacing,
2728 double required_tolerance) {
2729 if (q_vectors.empty()) {
2730 throw std::invalid_argument("SelectDirection(): No Q vectors specified");
2731 }
2732
2733 if (direction_list.empty()) {
2734 throw std::invalid_argument("SelectDirection(): List of possible directions has zero length");
2735 }
2736
2737 double error;
2738 double min_sum_sq_error = 1.0e100;
2739
2740 for (auto direction : direction_list) {
2741 double sum_sq_error = 0;
2742 direction /= plane_spacing;
2743 for (const auto &q_vector : q_vectors) {
2744 double dot_product = direction.scalar_prod(q_vector) / (2.0 * M_PI);
2745 error = std::abs(dot_product - std::round(dot_product));
2746 sum_sq_error += error * error;
2747 }
2748
2749 if (sum_sq_error < min_sum_sq_error + DBL_EPSILON) {
2750 min_sum_sq_error = sum_sq_error;
2751 best_direction = direction;
2752 }
2753 }
2754
2755 int num_indexed = 0;
2756 for (const auto &q_vector : q_vectors) {
2757 double proj_value = best_direction.scalar_prod(q_vector) / (2.0 * M_PI);
2758 error = std::abs(proj_value - std::round(proj_value));
2759 if (error < required_tolerance)
2760 num_indexed++;
2761 }
2762
2763 best_direction.normalize();
2764
2765 return num_indexed;
2766}
2767
2780bool IndexingUtils::GetLatticeParameters(const DblMatrix &UB, std::vector<double> &lattice_par) {
2781 OrientedLattice o_lattice;
2782 o_lattice.setUB(UB);
2783
2784 lattice_par.clear();
2785 lattice_par.emplace_back(o_lattice.a());
2786 lattice_par.emplace_back(o_lattice.b());
2787 lattice_par.emplace_back(o_lattice.c());
2788
2789 lattice_par.emplace_back(o_lattice.alpha());
2790 lattice_par.emplace_back(o_lattice.beta());
2791 lattice_par.emplace_back(o_lattice.gamma());
2792
2793 lattice_par.emplace_back(o_lattice.volume()); // keep volume > 0 even if
2794 // cell is left handed
2795 return true;
2796}
2797
2798int IndexingUtils::GetModulationVectors(const DblMatrix &UB, const DblMatrix &ModUB, V3D &ModVec1, V3D &ModVec2,
2799 V3D &ModVec3) {
2800 OrientedLattice o_lattice;
2801 o_lattice.setUB(UB);
2802 o_lattice.setModUB(ModUB);
2803
2804 ModVec1 = o_lattice.getModVec(0);
2805 ModVec2 = o_lattice.getModVec(1);
2806 ModVec3 = o_lattice.getModVec(2);
2807
2808 int ModDim = 0;
2809 if (o_lattice.getdh(0) != 0.0 || o_lattice.getdk(0) != 0.0 || o_lattice.getdl(0) != 0.0)
2810 ModDim = 1;
2811 if (o_lattice.getdh(1) != 0.0 || o_lattice.getdk(1) != 0.0 || o_lattice.getdl(1) != 0.0)
2812 ModDim = 2;
2813 if (o_lattice.getdh(2) != 0.0 || o_lattice.getdk(2) != 0.0 || o_lattice.getdl(2) != 0.0)
2814 ModDim = 3;
2815
2816 return ModDim;
2817}
2818
2819bool IndexingUtils::GetModulationVector(const DblMatrix &UB, const DblMatrix &ModUB, V3D &ModVec, const int j) {
2820 OrientedLattice o_lattice;
2821 o_lattice.setUB(UB);
2822 o_lattice.setModUB(ModUB);
2823
2824 ModVec = o_lattice.getModVec(j);
2825
2826 if (ModVec[0] != 0.0 || ModVec[1] != 0.0 || ModVec[2] != 0.0)
2827 return true;
2828 else
2829 return false;
2830}
2831
2839 std::vector<double> lat_par;
2841
2842 char buffer[100];
2843
2844 sprintf(buffer, std::string(" %8.4f %8.4f %8.4f %8.3f %8.3f %8.3f %9.2f").c_str(), lat_par[0], lat_par[1],
2845 lat_par[2], lat_par[3], lat_par[4], lat_par[5], lat_par[6]);
2846
2847 std::string result(buffer);
2848 return result;
2849}
double value
The value of the point.
Definition: FitMW.cpp:51
double position
Definition: GetAllEi.cpp:154
double error
Definition: IndexPeaks.cpp:133
bool withinTol(const double value, const double tolerance)
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
#define fabs(x)
Definition: Matrix.cpp:22
int count
counter
Definition: Matrix.cpp:37
double tolerance
static double Optimize_6dUB(Kernel::DblMatrix &UB, Kernel::DblMatrix &ModUB, const std::vector< Kernel::V3D > &hkl_vectors, const std::vector< Kernel::V3D > &mnp_vectors, const int &ModDim, const std::vector< Kernel::V3D > &q_vectors, std::vector< double > &sigabc, std::vector< double > &sigq)
STATIC method Optimize_UB: Calculates the matrix that most nearly maps the specified hkl_vectors to t...
static int GetIndexedPeaks(const Kernel::DblMatrix &UB, const std::vector< Kernel::V3D > &q_vectors, double required_tolerance, std::vector< Kernel::V3D > &miller_indices, std::vector< Kernel::V3D > &indexed_qs, double &fit_error)
Get lists of indices and Qs for peaks indexed by the specified UB matrix.
static double IndexingError(const Kernel::DblMatrix &UB, const std::vector< Kernel::V3D > &hkls, const std::vector< Kernel::V3D > &q_vectors)
Find the average indexing error for UB with the specified q's and hkls.
static int GetModulationVectors(const Kernel::DblMatrix &UB, const Kernel::DblMatrix &ModUB, Kernel::V3D &ModVec1, Kernel::V3D &ModVec2, Kernel::V3D &ModVec3)
static void DiscardDuplicates(std::vector< Kernel::V3D > &new_list, std::vector< Kernel::V3D > &directions, const std::vector< Kernel::V3D > &q_vectors, double required_tolerance, double len_tol, double ang_tol)
Construct a sublist of the specified list of a,b,c directions, by removing all directions that seem t...
static std::string GetLatticeParameterString(const Kernel::DblMatrix &UB)
Get a formatted string listing the lattice parameters and cell volume.
static double Optimize_Direction(Kernel::V3D &best_vec, const std::vector< int > &index_values, const std::vector< Kernel::V3D > &q_vectors)
Find the vector that best corresponds to plane normal, given 1-D indices.
static void RoundHKL(Kernel::V3D &hkl)
Round all the components of a HKL objects to the nearest integer.
static std::vector< Kernel::V3D > MakeCircleDirections(int n_steps, const Kernel::V3D &axis, double angle_degrees)
Make list of the circle of direction vectors that form a fixed angle with the specified axis.
static double GetFirstMaxIndex(const double magnitude_fft[], size_t N, double threshold)
Get the location of the first maximum (beyond the DC term) in the |FFT| that exceeds the specified th...
static int GetIndexedPeaks_1D(const Kernel::V3D &direction, const std::vector< Kernel::V3D > &q_vectors, double required_tolerance, std::vector< int > &index_vals, std::vector< Kernel::V3D > &indexed_qs, double &fit_error)
Get lists of indices and Qs for peaks indexed in the specified direction.
static double Find_UB(Kernel::DblMatrix &UB, const std::vector< Kernel::V3D > &q_vectors, OrientedLattice &lattice, double required_tolerance, int base_index, size_t num_initial, double degrees_per_step, bool fixAll=false, int iterations=1)
Find the UB matrix that most nearly indexes the specified qxyz values given the lattice parameters.
static Kernel::V3D makeCDir(const Kernel::V3D &a_dir, const Kernel::V3D &b_dir, const double c, const double cosAlpha, const double cosBeta, const double cosGamma, const double sinGamma)
Get the vector in the direction of "c" given other unit cell information.
static int SelectDirection(Kernel::V3D &best_direction, const std::vector< Kernel::V3D > &q_vectors, const std::vector< Kernel::V3D > &direction_list, double plane_spacing, double required_tolerance)
Choose the direction in a list of directions, that is most nearly perpendicular to planes with the sp...
static int NumberOfValidIndexes(const std::vector< Kernel::V3D > &hkls, double tolerance, double &average_error)
Find number of valid HKLs and average error, in list of HKLs.
static int NumberIndexed_1D(const Kernel::V3D &direction, const std::vector< Kernel::V3D > &q_vectors, double tolerance)
Calculate the number of Q vectors that map to an integer index in one direction.
static bool CheckUB(const Kernel::DblMatrix &UB)
Check that the specified UB is reasonable for an orientation matrix.
static int NumberIndexed(const Kernel::DblMatrix &UB, const std::vector< Kernel::V3D > &q_vectors, double tolerance)
Calculate the number of Q vectors that are mapped to integer indices by UB.
static std::vector< Kernel::V3D > MakeHemisphereDirections(int n_steps)
Make list of direction vectors uniformly distributed over a hemisphere.
static size_t FFTScanFor_Directions(std::vector< Kernel::V3D > &directions, const std::vector< Kernel::V3D > &q_vectors, double min_d, double max_d, double required_tolerance, double degrees_per_step)
Use FFT to get list of possible directions and lengths for real space unit cell.
static int GetIndexedPeaks_3D(const Kernel::V3D &direction_1, const Kernel::V3D &direction_2, const Kernel::V3D &direction_3, const std::vector< Kernel::V3D > &q_vectors, double required_tolerance, std::vector< Kernel::V3D > &miller_indices, std::vector< Kernel::V3D > &indexed_qs, double &fit_error)
Get lists of indices and Qs for peaks indexed in three given directions.
static double GetMagFFT(const std::vector< Kernel::V3D > &q_vectors, const Kernel::V3D &current_dir, const size_t N, double projections[], double index_factor, double magnitude_fft[])
Get the magnitude of the FFT of the projections of the q_vectors on the current direction vector.
static void RoundHKLs(std::vector< Kernel::V3D > &hkl_list)
Round all the components of a list of V3D objects, to the nearest integer.
static bool GetModulationVector(const Kernel::DblMatrix &UB, const Kernel::DblMatrix &ModUB, Kernel::V3D &ModVec, const int j)
static size_t ScanFor_Directions(std::vector< Kernel::V3D > &directions, const std::vector< Kernel::V3D > &q_vectors, double min_d, double max_d, double required_tolerance, double degrees_per_step)
Get list of possible directions and lengths for real space unit cell.
static double Optimize_UB(Kernel::DblMatrix &UB, const std::vector< Kernel::V3D > &hkl_vectors, const std::vector< Kernel::V3D > &q_vectors, std::vector< double > &sigabc)
Find the UB matrix that most nearly maps hkl to qxyz for 3 or more peaks.
static bool FormUB_From_abc_Vectors(Kernel::DblMatrix &UB, const std::vector< Kernel::V3D > &directions, size_t a_index, double min_d, double max_d)
Try to form a UB matrix from three vectors chosen from list of possible a,b,c directions,...
static bool ValidIndex(const Kernel::V3D &hkl, double tolerance)
Check is hkl is within tolerance of integer (h,k,l) non-zero values.
static double ScanFor_UB(Kernel::DblMatrix &UB, const std::vector< Kernel::V3D > &q_vectors, const UnitCell &cell, double degrees_per_step, double required_tolerance)
Scan rotations to find UB that indexes peaks given lattice parameters.
static int NumberIndexed_3D(const Kernel::V3D &a_dir, const Kernel::V3D &b_dir, const Kernel::V3D &c_dir, const std::vector< Kernel::V3D > &q_vectors, double tolerance)
Calculate the number of Q vectors that map to integer indices simutlaneously in three directions.
static bool GetLatticeParameters(const Kernel::DblMatrix &UB, std::vector< double > &lattice_par)
Get the lattice parameters for the specified orientation matrix.
static int CalculateMillerIndices(const Kernel::DblMatrix &UB, const std::vector< Kernel::V3D > &q_vectors, double tolerance, std::vector< Kernel::V3D > &miller_indices, double &ave_error)
Given a UB, get list of Miller indices for specifed Qs and tolerance.
static bool MakeNiggliUB(const Kernel::DblMatrix &UB, Kernel::DblMatrix &newUB)
Construct a newUB corresponding to a Niggli cell from the given UB.
Definition: NiggliCell.cpp:183
Class to implement UB matrix.
void setModUB(const Kernel::DblMatrix &newModUB)
Sets the Modulation UB matrix.
void setUB(const Kernel::DblMatrix &newUB)
Sets the UB matrix and recalculates lattice parameters.
static bool GetUB(Kernel::DblMatrix &UB, const Kernel::V3D &a_dir, const Kernel::V3D &b_dir, const Kernel::V3D &c_dir)
Get the UB matix corresponding to the real space edge vectors a, b, c.
Class to implement unit cell of crystals.
Definition: UnitCell.h:44
double alpha() const
Get lattice parameter.
Definition: UnitCell.cpp:133
double a(int nd) const
Get lattice parameter a1-a3 as function of index (0-2)
Definition: UnitCell.cpp:94
double c() const
Get lattice parameter.
Definition: UnitCell.cpp:128
double getdh(int j) const
Get modulation vectors for satellites.
Definition: UnitCell.cpp:555
double getdk(int j) const
Get modulation vectors for satellites.
Definition: UnitCell.cpp:562
double volume() const
Volume of the direct unit-cell.
Definition: UnitCell.cpp:737
double getdl(int j) const
Get modulation vectors for satellites.
Definition: UnitCell.cpp:569
double beta() const
Get lattice parameter.
Definition: UnitCell.cpp:138
void setError(double _aerr, double _berr, double _cerr, double _alphaerr, double _betaerr, double _gammaerr, const int angleunit=angDegrees)
Set lattice parameter errors.
Definition: UnitCell.cpp:325
double b() const
Get lattice parameter.
Definition: UnitCell.cpp:123
const Kernel::V3D getModVec(int j) const
Get modulation vectors for satellites.
Definition: UnitCell.cpp:535
double gamma() const
Get lattice parameter.
Definition: UnitCell.cpp:143
T determinant() const
Calculate the determinant.
Definition: Matrix.cpp:1048
T Invert()
LU inversion routine.
Definition: Matrix.cpp:924
void setRow(const size_t nRow, const std::vector< T > &newRow)
Definition: Matrix.cpp:686
size_t numRows() const
Return the number of rows in the matrix.
Definition: Matrix.h:144
size_t numCols() const
Return the number of columns in the matrix.
Definition: Matrix.h:147
Class for quaternions.
Definition: Quat.h:39
void rotate(V3D &) const
Rotate a vector.
Definition: Quat.cpp:397
void setAngleAxis(const double _deg, const V3D &_axis)
Constructor from an angle and axis.
Definition: Quat.cpp:114
Class for 3D vectors.
Definition: V3D.h:34
static bool compareMagnitude(const Kernel::V3D &v1, const Kernel::V3D &v2)
Convenience method for sorting list of V3D objects based on magnitude.
Definition: V3D.cpp:483
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition: V3D.h:274
double normalize()
Make a normalized vector (return norm value)
Definition: V3D.cpp:130
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition: V3D.h:278
double angle(const V3D &) const
Angle between this and another vector.
Definition: V3D.cpp:165
double norm() const noexcept
Definition: V3D.h:263
double hklError() const
Calculates the error in hkl.
Definition: V3D.cpp:530
Eigen::Vector3d toVector3d(const Kernel::V3D &vec)
Converts Kernel::V3D to Eigen::Vector3d.
Kernel::V3D toV3D(const Eigen::Vector3d &vec)
This header provides conversion helpers between vector and rotation types in MantidKernel and equival...
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition: V3D.h:341