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, // cppcheck-suppress constParameterReference
1028 const std::vector<int> &index_values, 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 V3D a_dir_temp;
1161 V3D b_dir_temp;
1162 V3D c_dir_temp;
1163
1164 double error;
1165 double dot_prod;
1166 double nearest_int;
1167 int max_indexed = 0;
1168 V3D q_vec;
1169 // first select those directions
1170 // that index the most peaks
1171 std::vector<V3D> selected_a_dirs;
1172 std::vector<V3D> selected_b_dirs;
1173 std::vector<V3D> selected_c_dirs;
1174
1175 for (const auto &a_dir_num : a_dir_list) {
1176 a_dir_temp = a_dir_num;
1177 a_dir_temp = V3D(a_dir_temp);
1178 a_dir_temp *= a;
1179
1180 std::vector<V3D> b_dir_list =
1181 MakeCircleDirections(boost::numeric_cast<int>(num_b_steps), a_dir_temp, gamma_degrees);
1182
1183 for (const auto &b_dir_num : b_dir_list) {
1184 b_dir_temp = b_dir_num;
1185 b_dir_temp = V3D(b_dir_temp);
1186 b_dir_temp *= b;
1187 c_dir_temp = makeCDir(a_dir_temp, b_dir_temp, c, cosAlpha, cosBeta, cosGamma, sinGamma);
1188 int num_indexed = 0;
1189 for (const auto &q_vector : q_vectors) {
1190 bool indexes_peak = true;
1191 q_vec = q_vector / (2.0 * M_PI);
1192 dot_prod = a_dir_temp.scalar_prod(q_vec);
1193 nearest_int = std::round(dot_prod);
1194 error = fabs(dot_prod - nearest_int);
1195 if (error > required_tolerance)
1196 indexes_peak = false;
1197 else {
1198 dot_prod = b_dir_temp.scalar_prod(q_vec);
1199 nearest_int = std::round(dot_prod);
1200 error = fabs(dot_prod - nearest_int);
1201 if (error > required_tolerance)
1202 indexes_peak = false;
1203 else {
1204 dot_prod = c_dir_temp.scalar_prod(q_vec);
1205 nearest_int = std::round(dot_prod);
1206 error = fabs(dot_prod - nearest_int);
1207 if (error > required_tolerance)
1208 indexes_peak = false;
1209 }
1210 }
1211 if (indexes_peak)
1212 num_indexed++;
1213 }
1214
1215 if (num_indexed > max_indexed) // only keep those directions that
1216 { // index the max number of peaks
1217 selected_a_dirs.clear();
1218 selected_b_dirs.clear();
1219 selected_c_dirs.clear();
1220 max_indexed = num_indexed;
1221 }
1222 if (num_indexed == max_indexed) {
1223 selected_a_dirs.emplace_back(a_dir_temp);
1224 selected_b_dirs.emplace_back(b_dir_temp);
1225 selected_c_dirs.emplace_back(c_dir_temp);
1226 }
1227 }
1228 }
1229 // now, for each such direction, find
1230 // the one that indexes closes to
1231 // integer values
1232 double min_error = 1.0e50;
1233 for (size_t dir_num = 0; dir_num < selected_a_dirs.size(); dir_num++) {
1234 a_dir_temp = selected_a_dirs[dir_num];
1235 b_dir_temp = selected_b_dirs[dir_num];
1236 c_dir_temp = selected_c_dirs[dir_num];
1237
1238 double sum_sq_error = 0.0;
1239 for (const auto &q_vector : q_vectors) {
1240 q_vec = q_vector / (2.0 * M_PI);
1241 dot_prod = a_dir_temp.scalar_prod(q_vec);
1242 nearest_int = std::round(dot_prod);
1243 error = dot_prod - nearest_int;
1244 sum_sq_error += error * error;
1245
1246 dot_prod = b_dir_temp.scalar_prod(q_vec);
1247 nearest_int = std::round(dot_prod);
1248 error = dot_prod - nearest_int;
1249 sum_sq_error += error * error;
1250
1251 dot_prod = c_dir_temp.scalar_prod(q_vec);
1252 nearest_int = std::round(dot_prod);
1253 error = dot_prod - nearest_int;
1254 sum_sq_error += error * error;
1255 }
1256
1257 if (sum_sq_error < min_error) {
1258 min_error = sum_sq_error;
1259 a_dir = a_dir_temp;
1260 b_dir = b_dir_temp;
1261 c_dir = c_dir_temp;
1262 }
1263 }
1264
1265 if (!OrientedLattice::GetUB(UB, a_dir, b_dir, c_dir)) {
1266 throw std::runtime_error("UB could not be formed, invert matrix failed");
1267 }
1268
1269 return min_error;
1270}
1271
1296size_t IndexingUtils::ScanFor_Directions(std::vector<V3D> &directions, const std::vector<V3D> &q_vectors, double min_d,
1297 double max_d, double required_tolerance, double degrees_per_step) {
1298 double error;
1299 double fit_error;
1300 double dot_prod;
1301 double nearest_int;
1302 int max_indexed = 0;
1303 V3D q_vec;
1304 // first, make hemisphere of possible directions
1305 // with specified resolution.
1306 int num_steps = boost::math::iround(90.0 / degrees_per_step);
1307 std::vector<V3D> full_list = MakeHemisphereDirections(num_steps);
1308 // Now, look for possible real-space unit cell edges
1309 // by checking for vectors with length between
1310 // min_d and max_d that would index the most peaks,
1311 // in some direction, keeping the shortest vector
1312 // for each direction where the max peaks are indexed
1313 double delta_d = 0.1f;
1314 int n_steps = boost::math::iround(1.0 + (max_d - min_d) / delta_d);
1315
1316 std::vector<V3D> selected_dirs;
1317
1318 for (const auto &current_dir : full_list) {
1319 for (int step = 0; step <= n_steps; step++) {
1320 V3D dir_temp = current_dir;
1321 dir_temp *= (min_d + step * delta_d); // increasing size
1322
1323 int num_indexed = 0;
1324 for (const auto &q_vector : q_vectors) {
1325 q_vec = q_vector / (2.0 * M_PI);
1326 dot_prod = dir_temp.scalar_prod(q_vec);
1327 nearest_int = std::round(dot_prod);
1328 error = fabs(dot_prod - nearest_int);
1329 if (error <= required_tolerance)
1330 num_indexed++;
1331 }
1332
1333 if (num_indexed > max_indexed) // only keep those directions that
1334 { // index the max number of peaks
1335 selected_dirs.clear();
1336 max_indexed = num_indexed;
1337 }
1338 if (num_indexed >= max_indexed) {
1339 selected_dirs.emplace_back(dir_temp);
1340 }
1341 }
1342 }
1343 // Now, optimize each direction and discard possible
1344 // unit cell edges that are duplicates, putting the
1345 // new smaller list in the vector "directions"
1346 std::vector<int> index_vals;
1347 std::vector<V3D> indexed_qs;
1348 index_vals.reserve(q_vectors.size());
1349 indexed_qs.reserve(q_vectors.size());
1350
1351 directions.clear();
1352 for (const auto &selected_dir : selected_dirs) {
1353 V3D current_dir = selected_dir;
1354
1355 GetIndexedPeaks_1D(current_dir, q_vectors, required_tolerance, index_vals, indexed_qs, fit_error);
1356
1357 Optimize_Direction(current_dir, index_vals, indexed_qs);
1358
1359 double length = current_dir.norm();
1360 if (length >= min_d && length <= max_d) // only keep if within range
1361 {
1362 bool duplicate = false;
1363 for (const auto &direction : directions) {
1364 V3D diff = current_dir - direction;
1365 // discard same direction
1366 if (diff.norm() < 0.001) {
1367 duplicate = true;
1368 } else {
1369 diff = current_dir + direction;
1370 // discard opposite direction
1371 if (diff.norm() < 0.001) {
1372 duplicate = true;
1373 }
1374 }
1375 }
1376 if (!duplicate) {
1377 directions.emplace_back(current_dir);
1378 }
1379 }
1380 }
1381 return max_indexed;
1382}
1383
1410size_t IndexingUtils::FFTScanFor_Directions(std::vector<V3D> &directions, const std::vector<V3D> &q_vectors,
1411 double min_d, double max_d, double required_tolerance,
1412 double degrees_per_step) {
1413 constexpr size_t N_FFT_STEPS = 512;
1414 constexpr size_t HALF_FFT_STEPS = 256;
1415
1416 double fit_error;
1417 int max_indexed = 0;
1418
1419 // first, make hemisphere of possible directions
1420 // with specified resolution.
1421 int num_steps = boost::math::iround(90.0 / degrees_per_step);
1422 std::vector<V3D> full_list = MakeHemisphereDirections(num_steps);
1423
1424 // find the maximum magnitude of Q to set range
1425 // needed for FFT
1426 double max_mag_Q = 0;
1427 for (size_t q_num = 1; q_num < q_vectors.size(); q_num++) {
1428 double mag_Q = q_vectors[q_num].norm() / (2.0 * M_PI);
1429 if (mag_Q > max_mag_Q)
1430 max_mag_Q = mag_Q;
1431 }
1432
1433 max_mag_Q *= 1.1f; // allow for a little "headroom" for FFT range
1434
1435 // apply the FFT to each of the directions, and
1436 // keep track of their maximum magnitude past DC
1437 double max_mag_fft;
1438 std::vector<double> max_fft_val;
1439 max_fft_val.resize(full_list.size());
1440
1441 double projections[N_FFT_STEPS];
1442 double magnitude_fft[HALF_FFT_STEPS];
1443
1444 double index_factor = N_FFT_STEPS / max_mag_Q; // maps |proj Q| to index
1445
1446 for (size_t dir_num = 0; dir_num < full_list.size(); dir_num++) {
1447 V3D current_dir = full_list[dir_num];
1448 max_mag_fft = GetMagFFT(q_vectors, current_dir, N_FFT_STEPS, projections, index_factor, magnitude_fft);
1449
1450 max_fft_val[dir_num] = max_mag_fft;
1451 }
1452 // find the directions with the 500 largest
1453 // fft values, and place them in temp_dirs vector
1454 int N_TO_TRY = 500;
1455
1456 std::vector<double> max_fft_copy;
1457 max_fft_copy.resize(full_list.size());
1458 for (size_t i = 0; i < max_fft_copy.size(); i++) {
1459 max_fft_copy[i] = max_fft_val[i];
1460 }
1461
1462 std::sort(max_fft_copy.begin(), max_fft_copy.end());
1463
1464 size_t index = max_fft_copy.size() - 1;
1465 max_mag_fft = max_fft_copy[index];
1466
1467 double threshold = max_mag_fft;
1468 while ((index > max_fft_copy.size() - N_TO_TRY) && threshold >= max_mag_fft / 2) {
1469 index--;
1470 threshold = max_fft_copy[index];
1471 }
1472
1473 std::vector<V3D> temp_dirs;
1474 for (size_t i = 0; i < max_fft_val.size(); i++) {
1475 if (max_fft_val[i] >= threshold) {
1476 temp_dirs.emplace_back(full_list[i]);
1477 }
1478 }
1479 // now scan through temp_dirs and use the
1480 // FFT to find the cell edge length that
1481 // corresponds to the max_mag_fft. Only keep
1482 // directions with length nearly in bounds
1483 V3D temp;
1484 std::vector<V3D> temp_dirs_2;
1485
1486 for (const auto &temp_dir : temp_dirs) {
1487 GetMagFFT(q_vectors, temp_dir, N_FFT_STEPS, projections, index_factor, magnitude_fft);
1488
1489 double position = GetFirstMaxIndex(magnitude_fft, HALF_FFT_STEPS, threshold);
1490 if (position > 0) {
1491 double q_val = max_mag_Q / position;
1492 double d_val = 1 / q_val;
1493 if (d_val >= 0.8 * min_d && d_val <= 1.2 * max_d) {
1494 temp = temp_dir * d_val;
1495 temp_dirs_2.emplace_back(temp);
1496 }
1497 }
1498 }
1499 // look at how many peaks were indexed
1500 // for each of the initial directions
1501 max_indexed = 0;
1502 int num_indexed;
1503 V3D current_dir;
1504 for (const auto &dir_num : temp_dirs_2) {
1505 num_indexed = NumberIndexed_1D(dir_num, q_vectors, required_tolerance);
1506 if (num_indexed > max_indexed)
1507 max_indexed = num_indexed;
1508 }
1509
1510 // only keep original directions that index
1511 // at least 50% of max num indexed
1512 temp_dirs.clear();
1513 for (const auto &dir_num : temp_dirs_2) {
1514 current_dir = dir_num;
1515 num_indexed = NumberIndexed_1D(current_dir, q_vectors, required_tolerance);
1516 if (num_indexed >= 0.50 * max_indexed)
1517 temp_dirs.emplace_back(current_dir);
1518 }
1519 // refine directions and again find the
1520 // max number indexed, for the optimized
1521 // directions
1522 max_indexed = 0;
1523 std::vector<int> index_vals;
1524 std::vector<V3D> indexed_qs;
1525 for (auto &temp_dir : temp_dirs) {
1526 num_indexed = GetIndexedPeaks_1D(temp_dir, q_vectors, required_tolerance, index_vals, indexed_qs, fit_error);
1527 try {
1528 int count = 0;
1529 while (count < 5) // 5 iterations should be enough for
1530 { // the optimization to stabilize
1531 Optimize_Direction(temp_dir, index_vals, indexed_qs);
1532
1533 num_indexed = GetIndexedPeaks_1D(temp_dir, q_vectors, required_tolerance, index_vals, indexed_qs, fit_error);
1534 if (num_indexed > max_indexed)
1535 max_indexed = num_indexed;
1536
1537 count++;
1538 }
1539 } catch (...) {
1540 // don't continue to refine if the direction fails to optimize properly
1541 }
1542 }
1543 // discard those with length out of bounds
1544 temp_dirs_2.clear();
1545 for (const auto &temp_dir : temp_dirs) {
1546 current_dir = temp_dir;
1547 double length = current_dir.norm();
1548 if (length >= 0.8 * min_d && length <= 1.2 * max_d)
1549 temp_dirs_2.emplace_back(current_dir);
1550 }
1551 // only keep directions that index at
1552 // least 75% of the max number of peaks
1553 temp_dirs.clear();
1554 for (const auto &dir_num : temp_dirs_2) {
1555 current_dir = dir_num;
1556 num_indexed = NumberIndexed_1D(current_dir, q_vectors, required_tolerance);
1557 if (num_indexed > max_indexed * 0.75)
1558 temp_dirs.emplace_back(current_dir);
1559 }
1560
1561 std::sort(temp_dirs.begin(), temp_dirs.end(), V3D::compareMagnitude);
1562
1563 // discard duplicates:
1564 double len_tol = 0.1; // 10% tolerance for lengths
1565 double ang_tol = 5.0; // 5 degree tolerance for angles
1566 DiscardDuplicates(directions, temp_dirs, q_vectors, required_tolerance, len_tol, ang_tol);
1567
1568 return max_indexed;
1569}
1570
1594double IndexingUtils::GetMagFFT(const std::vector<V3D> &q_vectors, const V3D &current_dir, const size_t N,
1595 double projections[], double index_factor, double magnitude_fft[]) {
1596 for (size_t i = 0; i < N; i++) {
1597 projections[i] = 0.0;
1598 }
1599 // project onto direction
1600 V3D q_vec;
1601 for (const auto &q_vector : q_vectors) {
1602 q_vec = q_vector / (2.0 * M_PI);
1603 double dot_prod = current_dir.scalar_prod(q_vec);
1604 auto index = static_cast<size_t>(fabs(index_factor * dot_prod));
1605 if (index < N)
1606 projections[index] += 1;
1607 else
1608 projections[N - 1] += 1; // This should not happen, but trap it in
1609 } // case of rounding errors.
1610
1611 // get the |FFT|
1612 gsl_fft_real_radix2_transform(projections, 1, N);
1613 for (size_t i = 1; i < N / 2; i++) {
1614 magnitude_fft[i] = sqrt(projections[i] * projections[i] + projections[N - i] * projections[N - i]);
1615 }
1616
1617 magnitude_fft[0] = fabs(projections[0]);
1618
1619 size_t dc_end = 5; // we may need a better estimate of this
1620 double max_mag_fft = 0.0;
1621 for (size_t i = dc_end; i < N / 2; i++)
1622 if (magnitude_fft[i] > max_mag_fft)
1623 max_mag_fft = magnitude_fft[i];
1624
1625 return max_mag_fft;
1626}
1627
1640double IndexingUtils::GetFirstMaxIndex(const double magnitude_fft[], size_t N, double threshold) {
1641 // find first local min below threshold
1642 size_t i = 2;
1643 bool found_min = false;
1644 double val;
1645 while (i < N - 1 && !found_min) {
1646 val = magnitude_fft[i];
1647 if (val < threshold && val <= magnitude_fft[i - 1] && val <= magnitude_fft[i + 1])
1648 found_min = true;
1649 i++;
1650 }
1651
1652 if (!found_min)
1653 return -1;
1654 // find next local max above threshold
1655 bool found_max = false;
1656 while (i < N - 1 && !found_max) {
1657 val = magnitude_fft[i];
1658 if (val >= threshold && val >= magnitude_fft[i - 1] && val >= magnitude_fft[i + 1])
1659 found_max = true;
1660 else
1661 i++;
1662 }
1663
1664 if (found_max) {
1665 double sum = 0;
1666 double w_sum = 0;
1667 for (size_t j = i - 2; j < std::min(N, i + 3); j++) {
1668 sum += static_cast<double>(j) * magnitude_fft[j];
1669 w_sum += magnitude_fft[j];
1670 }
1671 return sum / w_sum;
1672 } else
1673 return -1;
1674}
1675
1703bool IndexingUtils::FormUB_From_abc_Vectors(DblMatrix &UB, const std::vector<V3D> &directions, size_t a_index,
1704 double min_d, double max_d) {
1705 if (UB.numRows() != 3 || UB.numCols() != 3) {
1706 throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3");
1707 }
1708
1709 size_t index = a_index;
1710 V3D a_dir = directions[index];
1711 index++;
1712 // the possible range of d-values
1713 // implies a bound on the minimum
1714 // angle between a,b, c vectors.
1715 double min_deg = (RAD_TO_DEG)*atan(2.0 * std::min(0.2, min_d / max_d));
1716
1717 double epsilon = 5; // tolerance on right angle (degrees)
1718 V3D b_dir;
1719 bool b_found = false;
1720 while (!b_found && index < directions.size()) {
1721 V3D vec = directions[index];
1722 double gamma = a_dir.angle(vec) * RAD_TO_DEG;
1723
1724 if (gamma >= min_deg && (180. - gamma) >= min_deg) {
1725 b_dir = vec;
1726 if (gamma > 90 + epsilon) // try for Nigli cell with angles <= 90
1727 b_dir *= -1.0;
1728 b_found = true;
1729 index++;
1730 } else
1731 index++;
1732 }
1733
1734 if (!b_found)
1735 return false;
1736
1737 V3D c_dir;
1738 bool c_found = false;
1739
1740 const V3D perp = normalize(a_dir.cross_prod(b_dir));
1741 double perp_ang;
1742 double alpha;
1743 double beta;
1744 while (!c_found && index < directions.size()) {
1745 V3D vec = directions[index];
1746 int factor = 1;
1747 while (!c_found && factor >= -1) // try c in + or - direction
1748 {
1749 c_dir = vec;
1750 c_dir *= factor;
1751 perp_ang = perp.angle(c_dir) * RAD_TO_DEG;
1752 alpha = b_dir.angle(c_dir) * RAD_TO_DEG;
1753 beta = a_dir.angle(c_dir) * RAD_TO_DEG;
1754 // keep a,b,c right handed by choosing
1755 // c in general directiion of a X b
1756 if (perp_ang < 90. - epsilon && alpha >= min_deg && (180. - alpha) >= min_deg && beta >= min_deg &&
1757 (180. - beta) >= min_deg) {
1758 c_found = true;
1759 }
1760 factor -= 2;
1761 }
1762 if (!c_found)
1763 index++;
1764 }
1765
1766 if (!c_found) {
1767 return false;
1768 }
1769 // now build the UB matrix from a,b,c
1770 if (!OrientedLattice::GetUB(UB, a_dir, b_dir, c_dir)) {
1771 throw std::runtime_error("UB could not be formed, invert matrix failed");
1772 }
1773
1774 return true;
1775}
1776
1803bool IndexingUtils::FormUB_From_abc_Vectors(DblMatrix &UB, const std::vector<V3D> &directions,
1804 const std::vector<V3D> &q_vectors, double req_tolerance, double min_vol) {
1805 if (UB.numRows() != 3 || UB.numCols() != 3) {
1806 throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3");
1807 }
1808
1809 int max_indexed = 0;
1810 V3D a_dir(0, 0, 0);
1811 V3D b_dir(0, 0, 0);
1812 V3D c_dir(0, 0, 0);
1813 V3D a_temp;
1814 V3D b_temp;
1815 V3D c_temp;
1816 V3D acrossb;
1817 double vol;
1818
1819 for (size_t i = 0; i < directions.size() - 2; i++) {
1820 a_temp = directions[i];
1821 for (size_t j = i + 1; j < directions.size() - 1; j++) {
1822 b_temp = directions[j];
1823 acrossb = a_temp.cross_prod(b_temp);
1824 for (size_t k = j + 1; k < directions.size(); k++) {
1825 c_temp = directions[k];
1826 vol = fabs(acrossb.scalar_prod(c_temp));
1827 if (vol > min_vol) {
1828 const auto num_indexed = NumberIndexed_3D(a_temp, b_temp, c_temp, q_vectors, req_tolerance);
1829
1830 // Requiring 20% more indexed with longer edge lengths, favors
1831 // the smaller unit cells.
1832 if (num_indexed > 1.20 * max_indexed) {
1833 max_indexed = num_indexed;
1834 a_dir = a_temp;
1835 b_dir = b_temp;
1836 c_dir = c_temp;
1837 }
1838 }
1839 }
1840 }
1841 }
1842
1843 if (max_indexed <= 0) {
1844 return false;
1845 }
1846 // force a,b,c to be right handed
1847 acrossb = a_dir.cross_prod(b_dir);
1848 if (acrossb.scalar_prod(c_dir) < 0) {
1849 c_dir = c_dir * (-1.0);
1850 }
1851 // now build the UB matrix from a,b,c
1852 if (!OrientedLattice::GetUB(UB, a_dir, b_dir, c_dir)) {
1853 throw std::runtime_error("UB could not be formed, invert matrix failed");
1854 }
1855
1856 return true;
1857}
1858
1878V3D IndexingUtils::makeCDir(const V3D &a_dir, const V3D &b_dir, const double c, const double cosAlpha,
1879 const double cosBeta, const double cosGamma, const double sinGamma) {
1880
1881 double c1 = c * cosBeta;
1882 double c2 = c * (cosAlpha - cosGamma * cosBeta) / sinGamma;
1883 double V =
1884 sqrt(1 - cosAlpha * cosAlpha - cosBeta * cosBeta - cosGamma * cosGamma + 2 * cosAlpha * cosBeta * cosGamma);
1885 double c3 = c * V / sinGamma;
1886
1887 auto basis_1 = Mantid::Kernel::toVector3d(a_dir).normalized();
1888 auto basis_3 = Mantid::Kernel::toVector3d(a_dir).cross(Mantid::Kernel::toVector3d(b_dir)).normalized();
1889 auto basis_2 = basis_3.cross(basis_1).normalized();
1890
1891 return Mantid::Kernel::toV3D(basis_1 * c1 + basis_2 * c2 + basis_3 * c3);
1892}
1893
1919void IndexingUtils::DiscardDuplicates(std::vector<V3D> &new_list, std::vector<V3D> &directions,
1920 const std::vector<V3D> &q_vectors, double required_tolerance, double len_tol,
1921 double ang_tol) {
1922 new_list.clear();
1923 std::vector<V3D> temp;
1924
1925 V3D current_dir;
1926 V3D next_dir;
1927 V3D zero_vec(0, 0, 0);
1928
1929 double next_length;
1930 double length_diff;
1931 double angle;
1932 size_t dir_num = 0;
1933 size_t check_index;
1934 bool new_dir;
1935
1936 while (dir_num < directions.size()) // put sequence of similar vectors
1937 { // in list temp
1938 current_dir = directions[dir_num];
1939 double current_length = current_dir.norm();
1940 dir_num++;
1941
1942 if (current_length > 0) // skip any zero vectors
1943 {
1944 temp.clear();
1945 temp.emplace_back(current_dir);
1946 check_index = dir_num;
1947 new_dir = false;
1948 while (check_index < directions.size() && !new_dir) {
1949 next_dir = directions[check_index];
1950 next_length = next_dir.norm();
1951 if (next_length > 0) {
1952 length_diff = fabs(next_dir.norm() - current_length);
1953 if ((length_diff / current_length) < len_tol) // continue scan
1954 {
1955 angle = current_dir.angle(next_dir) * RAD_TO_DEG;
1956 if ((std::isnan)(angle))
1957 angle = 0;
1958 if ((angle < ang_tol) || (angle > (180.0 - ang_tol))) {
1959 temp.emplace_back(next_dir);
1960 directions[check_index] = zero_vec; // mark off this direction
1961 } // since it was duplicate
1962
1963 check_index++; // keep checking all vectors with
1964 } // essentially the same length
1965 else
1966 new_dir = true; // we only know we have a new
1967 // direction if the length is
1968 // different, since list is
1969 } // sorted by length !
1970 else
1971 check_index++; // just move on
1972 }
1973 // now scan through temp list to
1974 int max_indexed = 0; // find the one that indexes most
1975
1976 long max_i = -1;
1977 for (size_t i = 0; i < temp.size(); i++) {
1978 int num_indexed = NumberIndexed_1D(temp[i], q_vectors, required_tolerance);
1979 if (num_indexed > max_indexed) {
1980 max_indexed = num_indexed;
1981 max_i = static_cast<long>(i);
1982 }
1983 }
1984
1985 if (max_indexed > 0) // don't bother to add any direction
1986 { // that doesn't index anything
1987 new_list.emplace_back(temp[max_i]);
1988 }
1989 }
1990 }
1991
1992 directions.clear();
1993}
1994
2000 for (size_t i = 0; i < 3; i++) {
2001 hkl[i] = std::round(hkl[i]);
2002 }
2003}
2004
2014void IndexingUtils::RoundHKLs(std::vector<V3D> &hkl_list) {
2015 for (auto &entry : hkl_list) {
2016 RoundHKL(entry);
2017 }
2018}
2019
2025inline bool withinTol(const double value, const double tolerance) {
2026 double myVal = fabs(value);
2027 if (myVal - floor(myVal) < tolerance)
2028 return true;
2029 if (floor(myVal + 1.) - myVal < tolerance)
2030 return true;
2031 return false;
2032}
2033
2047bool IndexingUtils::ValidIndex(const V3D &hkl, double tolerance) {
2048 if ((hkl[0] == 0.) && (hkl[1] == 0.) && (hkl[2] == 0.))
2049 return false;
2050
2051 return (withinTol(hkl[0], tolerance) && withinTol(hkl[1], tolerance) && withinTol(hkl[2], tolerance));
2052}
2053
2064int IndexingUtils::NumberOfValidIndexes(const std::vector<V3D> &hkls, double tolerance, double &average_error) {
2065
2066 double h_error;
2067 double k_error;
2068 double l_error;
2069 double total_error = 0;
2070 int count = 0;
2071 for (const auto &hkl : hkls) {
2072 if (ValidIndex(hkl, tolerance)) {
2073 count++;
2074 h_error = fabs(round(hkl[0]) - hkl[0]);
2075 k_error = fabs(round(hkl[1]) - hkl[1]);
2076 l_error = fabs(round(hkl[2]) - hkl[2]);
2077 total_error += h_error + k_error + l_error;
2078 }
2079 }
2080
2081 if (count > 0)
2082 average_error = total_error / (3.0 * static_cast<double>(count));
2083 else
2084 average_error = 0.0;
2085
2086 return count;
2087}
2088
2090double IndexingUtils::IndexingError(const DblMatrix &UB, const std::vector<V3D> &hkls,
2091 const std::vector<V3D> &q_vectors) {
2092 DblMatrix UB_inverse(UB);
2093 if (CheckUB(UB)) {
2094 UB_inverse.Invert();
2095 } else {
2096 throw std::runtime_error("The UB in IndexingError() is not valid");
2097 }
2098 if (hkls.size() != q_vectors.size()) {
2099 throw std::runtime_error("Different size hkl and q_vectors in IndexingError()");
2100 }
2101
2102 double total_error = 0;
2103 V3D hkl;
2104 for (size_t i = 0; i < hkls.size(); i++) {
2105 hkl = UB_inverse * q_vectors[i] / (2.0 * M_PI);
2106
2107 double h_error = fabs(hkl[0] - round(hkl[0]));
2108 double k_error = fabs(hkl[1] - round(hkl[1]));
2109 double l_error = fabs(hkl[2] - round(hkl[2]));
2110 total_error += h_error + k_error + l_error;
2111 }
2112
2113 if (!hkls.empty())
2114 return total_error / (3.0 * static_cast<double>(hkls.size()));
2115 else
2116 return 0;
2117}
2118
2131 if (UB.numRows() != 3 || UB.numCols() != 3)
2132 return false;
2133
2134 for (size_t row = 0; row < 3; row++)
2135 for (size_t col = 0; col < 3; col++) {
2136 if (!std::isfinite(UB[row][col])) {
2137 return false;
2138 }
2139 }
2140
2141 double det = UB.determinant();
2142
2143 double abs_det = fabs(det);
2144
2145 return !(abs_det > 10 || abs_det < 1e-12);
2146}
2147
2169int IndexingUtils::NumberIndexed(const DblMatrix &UB, const std::vector<V3D> &q_vectors, double tolerance) {
2170 int count = 0;
2171
2172 DblMatrix UB_inverse(UB);
2173 if (CheckUB(UB)) {
2174 UB_inverse.Invert();
2175 } else {
2176 throw std::runtime_error("The UB in NumberIndexed() is not valid");
2177 }
2178
2179 V3D hkl;
2180 for (const auto &q_vector : q_vectors) {
2181 hkl = UB_inverse * q_vector / (2.0 * M_PI);
2182 if (ValidIndex(hkl, tolerance)) {
2183 count++;
2184 }
2185 }
2186
2187 return count;
2188}
2189
2207int IndexingUtils::NumberIndexed_1D(const V3D &direction, const std::vector<V3D> &q_vectors, double tolerance) {
2208 if (direction.norm() == 0)
2209 return 0;
2210
2211 int count = 0;
2212
2213 for (const auto &q_vector : q_vectors) {
2214 double proj_value = direction.scalar_prod(q_vector) / (2.0 * M_PI);
2215 double error = fabs(proj_value - std::round(proj_value));
2216 if (error <= tolerance) {
2217 count++;
2218 }
2219 }
2220
2221 return count;
2222}
2223
2245int IndexingUtils::NumberIndexed_3D(const V3D &a_dir, const V3D &b_dir, const V3D &c_dir,
2246 const std::vector<V3D> &q_vectors, double tolerance) {
2247 if (a_dir.norm() == 0 || b_dir.norm() == 0 || c_dir.norm() == 0)
2248 return 0;
2249
2250 V3D hkl_vec;
2251 int count = 0;
2252
2253 for (const auto &q_vector : q_vectors) {
2254 hkl_vec[0] = a_dir.scalar_prod(q_vector) / (2.0 * M_PI);
2255 hkl_vec[1] = b_dir.scalar_prod(q_vector) / (2.0 * M_PI);
2256 hkl_vec[2] = c_dir.scalar_prod(q_vector) / (2.0 * M_PI);
2257 if (ValidIndex(hkl_vec, tolerance)) {
2258 count++;
2259 }
2260 }
2261
2262 return count;
2263}
2264
2288int IndexingUtils::CalculateMillerIndices(const DblMatrix &UB, const std::vector<V3D> &q_vectors, double tolerance,
2289 std::vector<V3D> &miller_indices, double &ave_error) {
2290 DblMatrix UB_inverse(UB);
2291
2292 if (CheckUB(UB)) {
2293 UB_inverse.Invert();
2294 } else {
2295 throw std::runtime_error("The UB in CalculateMillerIndices() is not valid");
2296 }
2297
2298 miller_indices.clear();
2299 miller_indices.reserve(q_vectors.size());
2300
2301 int count = 0;
2302 ave_error = 0.0;
2303 for (const auto &q_vector : q_vectors) {
2304 V3D hkl;
2305 if (CalculateMillerIndices(UB_inverse, q_vector, tolerance, hkl)) {
2306 count++;
2307 ave_error += hkl.hklError();
2308 }
2309 miller_indices.emplace_back(hkl);
2310 }
2311
2312 if (count > 0) {
2313 ave_error /= (3.0 * count);
2314 }
2315
2316 return count;
2317}
2318
2338bool IndexingUtils::CalculateMillerIndices(const DblMatrix &inverseUB, const V3D &q_vector, double tolerance,
2339 V3D &miller_indices) {
2340 miller_indices = CalculateMillerIndices(inverseUB, q_vector);
2341 if (ValidIndex(miller_indices, tolerance)) {
2342 return true;
2343 } else {
2344 miller_indices = V3D(0, 0, 0);
2345 return false;
2346 }
2347}
2348
2360V3D IndexingUtils::CalculateMillerIndices(const DblMatrix &inverseUB, const V3D &q_vector) {
2361 return inverseUB * q_vector / (2.0 * M_PI);
2362}
2363
2392int IndexingUtils::GetIndexedPeaks_1D(const V3D &direction, const std::vector<V3D> &q_vectors,
2393 double required_tolerance, std::vector<int> &index_vals,
2394 std::vector<V3D> &indexed_qs, double &fit_error) {
2395 int num_indexed = 0;
2396 index_vals.clear();
2397 indexed_qs.clear();
2398 index_vals.reserve(q_vectors.size());
2399 indexed_qs.reserve(q_vectors.size());
2400
2401 fit_error = 0;
2402
2403 if (direction.norm() == 0) // special case, zero vector will NOT index
2404 return 0; // any peaks, even though dot product
2405 // with Q vectors is always an integer!
2406
2407 for (const auto &q_vector : q_vectors) {
2408 double proj_value = direction.scalar_prod(q_vector) / (2.0 * M_PI);
2409 double nearest_int = std::round(proj_value);
2410 double error = fabs(proj_value - nearest_int);
2411 if (error < required_tolerance) {
2412 fit_error += error * error;
2413 indexed_qs.emplace_back(q_vector);
2414 index_vals.emplace_back(boost::numeric_cast<int>(nearest_int));
2415 num_indexed++;
2416 }
2417 }
2418
2419 return num_indexed;
2420}
2421
2458int IndexingUtils::GetIndexedPeaks_3D(const V3D &direction_1, const V3D &direction_2, const V3D &direction_3,
2459 const std::vector<V3D> &q_vectors, double required_tolerance,
2460 std::vector<V3D> &miller_indices, std::vector<V3D> &indexed_qs,
2461 double &fit_error) {
2462 V3D hkl;
2463 int num_indexed = 0;
2464
2465 miller_indices.clear();
2466 miller_indices.reserve(q_vectors.size());
2467
2468 indexed_qs.clear();
2469 indexed_qs.reserve(q_vectors.size());
2470
2471 fit_error = 0;
2472
2473 for (const auto &q_vector : q_vectors) {
2474 double projected_h = direction_1.scalar_prod(q_vector) / (2.0 * M_PI);
2475 double projected_k = direction_2.scalar_prod(q_vector) / (2.0 * M_PI);
2476 double projected_l = direction_3.scalar_prod(q_vector) / (2.0 * M_PI);
2477
2478 hkl(projected_h, projected_k, projected_l);
2479
2480 if (ValidIndex(hkl, required_tolerance)) {
2481 double h_int = std::round(projected_h);
2482 double k_int = std::round(projected_k);
2483 double l_int = std::round(projected_l);
2484
2485 double h_error = fabs(projected_h - h_int);
2486 double k_error = fabs(projected_k - k_int);
2487 double l_error = fabs(projected_l - l_int);
2488
2489 fit_error += h_error * h_error + k_error * k_error + l_error * l_error;
2490
2491 indexed_qs.emplace_back(q_vector);
2492
2493 V3D miller_ind(h_int, k_int, l_int);
2494 miller_indices.emplace_back(miller_ind);
2495
2496 num_indexed++;
2497 }
2498 }
2499
2500 return num_indexed;
2501}
2502
2528int IndexingUtils::GetIndexedPeaks(const DblMatrix &UB, const std::vector<V3D> &q_vectors, double required_tolerance,
2529 std::vector<V3D> &miller_indices, std::vector<V3D> &indexed_qs, double &fit_error) {
2530 double error;
2531 int num_indexed = 0;
2532 V3D hkl;
2533
2534 miller_indices.clear();
2535 miller_indices.reserve(q_vectors.size());
2536
2537 indexed_qs.clear();
2538 indexed_qs.reserve(q_vectors.size());
2539
2540 fit_error = 0;
2541
2542 DblMatrix UB_inverse(UB);
2543 if (CheckUB(UB)) {
2544 UB_inverse.Invert();
2545 } else {
2546 throw std::runtime_error("The UB in GetIndexedPeaks() is not valid");
2547 }
2548
2549 for (const auto &q_vector : q_vectors) {
2550 hkl = UB_inverse * q_vector / (2.0 * M_PI);
2551
2552 if (ValidIndex(hkl, required_tolerance)) {
2553 for (int i = 0; i < 3; i++) {
2554 error = hkl[i] - std::round(hkl[i]);
2555 fit_error += error * error;
2556 }
2557
2558 indexed_qs.emplace_back(q_vector);
2559
2560 V3D miller_ind(round(hkl[0]), round(hkl[1]), round(hkl[2]));
2561 miller_indices.emplace_back(miller_ind);
2562
2563 num_indexed++;
2564 }
2565 }
2566
2567 return num_indexed;
2568}
2569
2588std::vector<V3D> IndexingUtils::MakeHemisphereDirections(int n_steps) {
2589 if (n_steps <= 0) {
2590 throw std::invalid_argument("MakeHemisphereDirections(): n_steps must be greater than 0");
2591 }
2592
2593 std::vector<V3D> direction_list;
2594
2595 double angle_step = M_PI / (2 * n_steps);
2596
2597 for (int iPhi = 0; iPhi < n_steps + 1; ++iPhi) {
2598 double phi = static_cast<double>(iPhi) * angle_step;
2599 double r = sin(phi);
2600
2601 int n_theta = boost::math::iround(2. * M_PI * r / angle_step);
2602
2603 double theta_step;
2604 if (n_theta == 0) { // n = ( 0, 1, 0 ). Just
2605 theta_step = 2. * M_PI + 1.; // use one vector at the pole
2606 n_theta = 1;
2607 } else {
2608 theta_step = 2. * M_PI / static_cast<double>(n_theta);
2609 }
2610
2611 // use half the equator to avoid vectors that are the negatives of other
2612 // vectors in the list.
2613 if (fabs(phi - M_PI / 2.) < angle_step / 2.) {
2614 n_theta /= 2;
2615 }
2616
2617 for (int jTheta = 0; jTheta < n_theta; ++jTheta) {
2618 double theta = static_cast<double>(jTheta) * theta_step;
2619 direction_list.emplace_back(r * cos(theta), cos(phi), r * sin(theta));
2620 }
2621 }
2622 return direction_list;
2623}
2624
2639std::vector<V3D> IndexingUtils::MakeCircleDirections(int n_steps, const Mantid::Kernel::V3D &axis,
2640 double angle_degrees) {
2641 if (n_steps <= 0) {
2642 throw std::invalid_argument("MakeCircleDirections(): n_steps must be greater than 0");
2643 }
2644 // first get a vector perpendicular to axis
2645 double max_component = fabs(axis[0]);
2646 double min_component = max_component;
2647 size_t min_index = 0;
2648 for (size_t i = 1; i < 3; i++) {
2649 if (fabs(axis[i]) < min_component) {
2650 min_component = fabs(axis[i]);
2651 min_index = i;
2652 }
2653 if (fabs(axis[i]) > max_component) {
2654 max_component = fabs(axis[i]);
2655 }
2656 }
2657
2658 if (max_component == 0) {
2659 throw std::invalid_argument("MakeCircleDirections(): Axis vector must be non-zero!");
2660 }
2661
2662 V3D second_vec(0, 0, 0);
2663 second_vec[min_index] = 1;
2664
2665 const V3D perp_vec = normalize(second_vec.cross_prod(axis));
2666
2667 // next get a vector that is the specified
2668 // number of degrees away from the axis
2669 Quat rotation_1(angle_degrees, perp_vec);
2670 V3D vector_at_angle(axis);
2671 rotation_1.rotate(vector_at_angle);
2672 vector_at_angle.normalize();
2673
2674 // finally, form the circle of directions
2675 // consisting of vectors that are at the
2676 // specified angle from the original axis
2677 double angle_step = 360.0 / n_steps;
2678 Quat rotation_2(0, axis);
2679 std::vector<V3D> directions;
2680 for (int i = 0; i < n_steps; i++) {
2681 V3D vec(vector_at_angle);
2682 rotation_2.setAngleAxis(i * angle_step, axis);
2683 rotation_2.rotate(vec);
2684 directions.emplace_back(vec);
2685 }
2686
2687 return directions;
2688}
2689
2720int IndexingUtils::SelectDirection(V3D &best_direction, const std::vector<V3D> &q_vectors,
2721 const std::vector<V3D> &direction_list, double plane_spacing,
2722 double required_tolerance) {
2723 if (q_vectors.empty()) {
2724 throw std::invalid_argument("SelectDirection(): No Q vectors specified");
2725 }
2726
2727 if (direction_list.empty()) {
2728 throw std::invalid_argument("SelectDirection(): List of possible directions has zero length");
2729 }
2730
2731 double error;
2732 double min_sum_sq_error = 1.0e100;
2733
2734 for (auto direction : direction_list) {
2735 double sum_sq_error = 0;
2736 direction /= plane_spacing;
2737 for (const auto &q_vector : q_vectors) {
2738 double dot_product = direction.scalar_prod(q_vector) / (2.0 * M_PI);
2739 error = std::abs(dot_product - std::round(dot_product));
2740 sum_sq_error += error * error;
2741 }
2742
2743 if (sum_sq_error < min_sum_sq_error + DBL_EPSILON) {
2744 min_sum_sq_error = sum_sq_error;
2745 best_direction = direction;
2746 }
2747 }
2748
2749 int num_indexed = 0;
2750 for (const auto &q_vector : q_vectors) {
2751 double proj_value = best_direction.scalar_prod(q_vector) / (2.0 * M_PI);
2752 error = std::abs(proj_value - std::round(proj_value));
2753 if (error < required_tolerance)
2754 num_indexed++;
2755 }
2756
2757 best_direction.normalize();
2758
2759 return num_indexed;
2760}
2761
2774bool IndexingUtils::GetLatticeParameters(const DblMatrix &UB, std::vector<double> &lattice_par) {
2775 OrientedLattice o_lattice;
2776 o_lattice.setUB(UB);
2777
2778 lattice_par.clear();
2779 lattice_par.emplace_back(o_lattice.a());
2780 lattice_par.emplace_back(o_lattice.b());
2781 lattice_par.emplace_back(o_lattice.c());
2782
2783 lattice_par.emplace_back(o_lattice.alpha());
2784 lattice_par.emplace_back(o_lattice.beta());
2785 lattice_par.emplace_back(o_lattice.gamma());
2786
2787 lattice_par.emplace_back(o_lattice.volume()); // keep volume > 0 even if
2788 // cell is left handed
2789 return true;
2790}
2791
2792int IndexingUtils::GetModulationVectors(const DblMatrix &UB, const DblMatrix &ModUB, V3D &ModVec1, V3D &ModVec2,
2793 V3D &ModVec3) {
2794 OrientedLattice o_lattice;
2795 o_lattice.setUB(UB);
2796 o_lattice.setModUB(ModUB);
2797
2798 ModVec1 = o_lattice.getModVec(0);
2799 ModVec2 = o_lattice.getModVec(1);
2800 ModVec3 = o_lattice.getModVec(2);
2801
2802 int ModDim = 0;
2803 if (o_lattice.getdh(0) != 0.0 || o_lattice.getdk(0) != 0.0 || o_lattice.getdl(0) != 0.0)
2804 ModDim = 1;
2805 if (o_lattice.getdh(1) != 0.0 || o_lattice.getdk(1) != 0.0 || o_lattice.getdl(1) != 0.0)
2806 ModDim = 2;
2807 if (o_lattice.getdh(2) != 0.0 || o_lattice.getdk(2) != 0.0 || o_lattice.getdl(2) != 0.0)
2808 ModDim = 3;
2809
2810 return ModDim;
2811}
2812
2813bool IndexingUtils::GetModulationVector(const DblMatrix &UB, const DblMatrix &ModUB, V3D &ModVec, const int j) {
2814 OrientedLattice o_lattice;
2815 o_lattice.setUB(UB);
2816 o_lattice.setModUB(ModUB);
2817
2818 ModVec = o_lattice.getModVec(j);
2819
2820 if (ModVec[0] != 0.0 || ModVec[1] != 0.0 || ModVec[2] != 0.0)
2821 return true;
2822 else
2823 return false;
2824}
2825
2833 std::vector<double> lat_par;
2835
2836 char buffer[100];
2837
2838 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],
2839 lat_par[2], lat_par[3], lat_par[4], lat_par[5], lat_par[6]);
2840
2841 std::string result(buffer);
2842 return result;
2843}
double value
The value of the point.
Definition FitMW.cpp:51
double position
Definition GetAllEi.cpp:154
double error
bool withinTol(const double value, const double tolerance)
std::map< DeltaEMode::Type, std::string > index
#define fabs(x)
Definition Matrix.cpp:22
int count
counter
Definition Matrix.cpp:37
std::vector< T > const * vec
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.
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 matrix 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:475
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition V3D.h:280
double normalize()
Make a normalized vector (return norm value)
Definition V3D.cpp:129
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition V3D.h:284
double angle(const V3D &) const
Angle between this and another vector.
Definition V3D.cpp:162
double norm() const noexcept
Definition V3D.h:269
double hklError() const
Calculates the error in hkl.
Definition V3D.cpp:522
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:352