Mantid
Loading...
Searching...
No Matches
Integrate3DEvents.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 +
11
12#include <boost/math/special_functions/round.hpp>
13#include <cmath>
14#include <fstream>
15#include <memory>
16#include <numeric>
17#include <tuple>
18
19extern "C" {
20#include <cstdio>
21#include <utility>
22
23#include <gsl/gsl_eigen.h>
24#include <gsl/gsl_matrix.h>
25#include <gsl/gsl_vector.h>
26}
27
28using namespace Mantid::DataObjects;
29namespace Mantid::MDAlgorithms {
30
31using namespace std;
34
49 const std::vector<std::pair<std::pair<double, double>, Mantid::Kernel::V3D>> &peak_q_list, Kernel::DblMatrix UBinv,
50 double radius, const bool useOnePercentBackgroundCorrection)
51 : m_UBinv(std::move(UBinv)), m_radius(radius), maxOrder(0), crossterm(false),
52 m_useOnePercentBackgroundCorrection(useOnePercentBackgroundCorrection) {
53 for (size_t it = 0; it != peak_q_list.size(); ++it) {
54 int64_t hkl_key = getHklKey(peak_q_list[it].second);
55 if (hkl_key != 0) // only save if hkl != (0,0,0)
56 m_peak_qs[hkl_key] = peak_q_list[it].second;
57 }
58}
59
83 const std::vector<std::pair<std::pair<double, double>, Mantid::Kernel::V3D>> &peak_q_list,
84 std::vector<V3D> const &hkl_list, std::vector<V3D> const &mnp_list, Kernel::DblMatrix UBinv,
85 Kernel::DblMatrix ModHKL, double radius_m, double radius_s, int MaxO, const bool CrossT,
86 const bool useOnePercentBackgroundCorrection)
87 : m_UBinv(std::move(UBinv)), m_ModHKL(std::move(ModHKL)), m_radius(radius_m), s_radius(radius_s), maxOrder(MaxO),
88 crossterm(CrossT), m_useOnePercentBackgroundCorrection(useOnePercentBackgroundCorrection) {
89 for (size_t it = 0; it != peak_q_list.size(); ++it) {
90 int64_t hklmnp_key =
91 getHklMnpKey(boost::math::iround<double>(hkl_list[it][0]), boost::math::iround<double>(hkl_list[it][1]),
92 boost::math::iround<double>(hkl_list[it][2]), boost::math::iround<double>(mnp_list[it][0]),
93 boost::math::iround<double>(mnp_list[it][1]), boost::math::iround<double>(mnp_list[it][2]));
94 if (hklmnp_key != 0) // only save if hkl != (0,0,0)
95 m_peak_qs[hklmnp_key] = peak_q_list[it].second;
96 }
97}
98
116void Integrate3DEvents::addEvents(std::vector<std::pair<std::pair<double, double>, V3D>> const &event_qs,
117 bool hkl_integ) {
118 if (!maxOrder)
119 for (const auto &event_q : event_qs)
120 addEvent(event_q, hkl_integ);
121 else
122 for (const auto &event_q : event_qs)
123 addModEvent(event_q, hkl_integ);
124}
125
126std::pair<std::shared_ptr<const Geometry::PeakShape>, std::tuple<double, double, double>>
127Integrate3DEvents::integrateStrongPeak(const IntegrationParameters &params, const V3D &peak_q, double &inti,
128 double &sigi) {
129
130 inti = 0.0; // default values, in case something
131 sigi = 0.0; // is wrong with the peak.
132 auto result = getEvents(peak_q);
133 if (!result)
134 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
135
136 const auto &events = *result;
137 if (events.empty())
138 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
139
140 DblMatrix cov_matrix(3, 3);
141 makeCovarianceMatrix(events, cov_matrix, params.regionRadius);
142
143 std::vector<V3D> eigen_vectors;
144 std::vector<double> eigen_values;
145 getEigenVectors(cov_matrix, eigen_vectors, eigen_values);
146
147 std::vector<double> sigmas(3);
148 for (int i = 0; i < 3; i++) {
149 sigmas[i] = sqrt(eigen_values[i]);
150 }
151
152 bool invalid_peak =
153 std::any_of(sigmas.cbegin(), sigmas.cend(), [](const double sigma) { return std::isnan(sigma) || sigma <= 0; });
154
155 if (invalid_peak)
156 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
157
158 const auto max_sigma = *std::max_element(sigmas.begin(), sigmas.end());
159 if (max_sigma == 0)
160 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
161
162 auto rValues = calculateRadiusFactors(params, max_sigma);
163 auto &r1 = std::get<0>(rValues), r2 = std::get<1>(rValues), r3 = std::get<2>(rValues);
164
165 std::vector<double> abcBackgroundOuterRadii, abcBackgroundInnerRadii;
166 std::vector<double> peakRadii;
167 for (int i = 0; i < 3; i++) {
168 abcBackgroundOuterRadii.emplace_back(r3 * sigmas[i]);
169 abcBackgroundInnerRadii.emplace_back(r2 * sigmas[i]);
170 peakRadii.emplace_back(r1 * sigmas[i]);
171 }
172
173 const auto isPeakOnDetector = correctForDetectorEdges(rValues, params.E1Vectors, peak_q, peakRadii,
174 abcBackgroundInnerRadii, abcBackgroundOuterRadii);
175
176 if (!isPeakOnDetector)
177 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0.0, 0.0, 0.));
178
179 const auto backgrd = numInEllipsoidBkg(events, eigen_vectors, abcBackgroundOuterRadii, abcBackgroundInnerRadii,
181 const auto core = numInEllipsoid(events, eigen_vectors, sigmas);
182 const auto peak = numInEllipsoid(events, eigen_vectors, peakRadii);
183 const auto ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
184
185 inti = peak.first - ratio * backgrd.first;
186 sigi = sqrt(peak.second + ratio * ratio * backgrd.second);
187
188 // compute the fraction of peak within the standard core
189 const auto total = (core.first + peak.first) - ratio * backgrd.first;
190 const auto frac = std::min(1.0, std::abs(inti / total));
191 // compute the uncertainty in the fraction
192 const auto df_ds_core = (1 - frac) / peak.first;
193 const auto df_ds_peak = frac / peak.first;
194 const auto fracError = sqrt(peak.first * pow(df_ds_core, 2) + core.first * pow(df_ds_peak, 2));
195
196 // create the peaks shape for the strong peak
197 const auto shape = std::make_shared<const PeakShapeEllipsoid>(eigen_vectors, peakRadii, abcBackgroundInnerRadii,
198 abcBackgroundOuterRadii, Mantid::Kernel::QLab,
199 "IntegrateEllipsoidsTwoStep");
200
201 return std::make_pair(shape, std::make_tuple(frac, fracError, max_sigma));
202}
203
204std::shared_ptr<const Geometry::PeakShape>
206 const std::tuple<double, double, double> &libPeak, const V3D &center, double &inti,
207 double &sigi) {
208
209 inti = 0.0; // default values, in case something
210 sigi = 0.0; // is wrong with the peak.
211
212 auto result = getEvents(center);
213 if (!result)
214 return std::make_shared<NoShape>();
215
216 const auto &events = *result;
217
218 const auto &directions = shape->directions();
219 auto abcBackgroundInnerRadii = shape->abcRadiiBackgroundInner();
220 auto abcBackgroundOuterRadii = shape->abcRadiiBackgroundOuter();
221 auto abcRadii = shape->abcRadii();
222
223 const auto max_sigma = std::get<2>(libPeak);
224 auto rValues = calculateRadiusFactors(params, max_sigma);
225
226 const auto isPeakOnDetector = correctForDetectorEdges(rValues, params.E1Vectors, center, abcRadii,
227 abcBackgroundInnerRadii, abcBackgroundOuterRadii);
228
229 if (!isPeakOnDetector)
230 return shape;
231
232 const double r1 = std::get<0>(rValues), r2 = std::get<1>(rValues), r3 = std::get<2>(rValues);
233
234 // integrate
235 std::pair<double, double> backgrd = numInEllipsoidBkg(events, directions, abcBackgroundOuterRadii,
236 abcBackgroundInnerRadii, m_useOnePercentBackgroundCorrection);
237 std::pair<double, double> peak_w_back = numInEllipsoid(events, directions, abcRadii);
238 double ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
239
240 const auto frac = std::get<0>(libPeak);
241 const auto fracError = std::get<1>(libPeak);
242
243 inti = peak_w_back.first - ratio * backgrd.first;
244
245 // correct for fractional intensity
246 sigi = sigi / pow(inti, 2);
247 sigi += pow((fracError / frac), 2);
248
249 inti = inti * frac;
250 sigi = sqrt(sigi) * inti;
251
252 // scale integration shape by fractional amount
253 for (size_t i = 0; i < abcRadii.size(); ++i) {
254 abcRadii[i] *= frac;
255 abcBackgroundInnerRadii[i] *= frac;
256 abcBackgroundOuterRadii[i] *= frac;
257 }
258
259 return std::make_shared<const PeakShapeEllipsoid>(shape->directions(), abcRadii, abcBackgroundInnerRadii,
260 abcBackgroundOuterRadii, Mantid::Kernel::QLab,
261 "IntegrateEllipsoidsTwoStep");
262}
263
265 bool forceSpherical, double sphericityTol) {
266
267 auto result = getEvents(center);
268 if (!result)
269 return .0;
270
271 const auto &events = *result;
272 if (events.empty())
273 return .0;
274
275 DblMatrix cov_matrix(3, 3);
276 makeCovarianceMatrix(events, cov_matrix, params.regionRadius);
277
278 std::vector<V3D> eigen_vectors;
279 std::vector<double> eigen_values;
280 getEigenVectors(cov_matrix, eigen_vectors, eigen_values);
281
282 std::vector<double> sigmas(3);
283 for (int i = 0; i < 3; i++) {
284 sigmas[i] = sqrt(eigen_values[i]);
285 }
286
287 const auto max_sigma = *std::max_element(sigmas.begin(), sigmas.end());
288 const auto min_sigma = *std::min_element(sigmas.begin(), sigmas.end());
289 if (max_sigma == 0)
290 return .0;
291
292 auto rValues = calculateRadiusFactors(params, max_sigma);
293 auto &r1 = std::get<0>(rValues), r2 = std::get<1>(rValues), r3 = std::get<2>(rValues);
294 std::vector<double> abcBackgroundOuterRadii, abcBackgroundInnerRadii;
295 std::vector<double> peakRadii;
296 if (forceSpherical) {
297 // test for spherically symmeteric peak (within tolerance)
298 if ((max_sigma - min_sigma) / max_sigma > sphericityTol)
299 return .0;
300 for (int i = 0; i < 3; i++) {
301 abcBackgroundOuterRadii.emplace_back(r3 * max_sigma);
302 abcBackgroundInnerRadii.emplace_back(r2 * max_sigma);
303 peakRadii.emplace_back(r1 * max_sigma);
304 }
305 } else {
306 for (int i = 0; i < 3; i++) {
307 abcBackgroundOuterRadii.emplace_back(r3 * sigmas[i]);
308 abcBackgroundInnerRadii.emplace_back(r2 * sigmas[i]);
309 peakRadii.emplace_back(r1 * sigmas[i]);
310 }
311 }
312
313 // Background / Peak / Background
314 std::pair<double, double> backgrd = numInEllipsoidBkg(events, eigen_vectors, abcBackgroundOuterRadii,
315 abcBackgroundInnerRadii, m_useOnePercentBackgroundCorrection);
316
317 std::pair<double, double> peak_w_back = numInEllipsoid(events, eigen_vectors, peakRadii);
318
319 double ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
320 auto inti = peak_w_back.first - ratio * backgrd.first;
321 auto sigi = sqrt(peak_w_back.second + ratio * ratio * backgrd.second);
322
323 return inti / sigi;
324}
325
326const std::vector<std::pair<std::pair<double, double>, V3D>> *Integrate3DEvents::getEvents(const V3D &peak_q) {
327 auto hkl_key = getHklKey(peak_q);
328 if (maxOrder)
329 hkl_key = getHklMnpKey(peak_q);
330
331 if (hkl_key == 0)
332 return nullptr;
333
334 const auto pos = m_event_lists.find(hkl_key);
335
336 if (m_event_lists.end() == pos)
337 return nullptr;
338
339 if (pos->second.size() < 3) // if there are not enough events
340 return nullptr;
341
342 return &(pos->second);
343}
344
345bool Integrate3DEvents::correctForDetectorEdges(std::tuple<double, double, double> &radii,
346 const std::vector<V3D> &E1Vecs, const V3D &peak_q,
347 const std::vector<double> &axesRadii,
348 const std::vector<double> &bkgInnerRadii,
349 const std::vector<double> &bkgOuterRadii) {
350
351 if (E1Vecs.empty())
352 return true;
353
354 const auto &r1 = std::get<0>(radii);
355 auto &r2 = std::get<1>(radii);
356 auto &r3 = std::get<2>(radii);
357 auto h3 = 1.0 - detectorQ(E1Vecs, peak_q, bkgOuterRadii);
358 // scaled from area of circle minus segment when r normalized to 1
359 auto m3 = std::sqrt(1.0 - (std::acos(1.0 - h3) - (1.0 - h3) * std::sqrt(2.0 * h3 - h3 * h3)) / M_PI);
360 auto h1 = 1.0 - detectorQ(E1Vecs, peak_q, axesRadii);
361 // Do not use peak if edge of detector is inside integration radius
362 if (h1 > 0.0)
363 return false;
364
365 r3 *= m3;
366 if (r2 != r1) {
367 auto h2 = 1.0 - detectorQ(E1Vecs, peak_q, bkgInnerRadii);
368 // scaled from area of circle minus segment when r normalized to 1
369 auto m2 = std::sqrt(1.0 - (std::acos(1.0 - h2) - (1.0 - h2) * std::sqrt(2.0 * h2 - h2 * h2)) / M_PI);
370 r2 *= m2;
371 }
372
373 return true;
374}
375
413Integrate3DEvents::ellipseIntegrateEvents(const std::vector<V3D> &E1Vec, V3D const &peak_q, bool specify_size,
414 double peak_radius, double back_inner_radius, double back_outer_radius,
415 std::vector<double> &axes_radii, double &inti, double &sigi) {
416 inti = 0.0; // default values, in case something
417 sigi = 0.0; // is wrong with the peak.
418
419 int64_t hkl_key = getHklKey(peak_q);
420
421 if (hkl_key == 0) {
422 return std::make_shared<NoShape>();
423 }
424
425 auto pos = m_event_lists.find(hkl_key);
426 if (m_event_lists.end() == pos)
427 return std::make_shared<NoShape>();
428 ;
429
430 const std::vector<std::pair<std::pair<double, double>, V3D>> &some_events = pos->second;
431
432 if (some_events.size() < 3) // if there are not enough events to
433 { // find covariance matrix, return
434 return std::make_shared<NoShape>();
435 }
436
437 DblMatrix cov_matrix(3, 3);
438 makeCovarianceMatrix(some_events, cov_matrix, m_radius);
439
440 std::vector<V3D> eigen_vectors;
441 std::vector<double> eigen_values;
442 getEigenVectors(cov_matrix, eigen_vectors, eigen_values);
443
444 std::vector<double> sigmas(3);
445 for (int i = 0; i < 3; i++) {
446 sigmas[i] = sqrt(eigen_values[i]);
447 }
448
449 bool invalid_peak =
450 std::any_of(sigmas.cbegin(), sigmas.cend(), [](const double sigma) { return std::isnan(sigma) || sigma <= 0; });
451
452 if (invalid_peak) // if data collapses to a line or
453 { // to a plane, the volume of the
454 return std::make_shared<NoShape>(); // ellipsoids will be zero.
455 }
456
457 return ellipseIntegrateEvents(E1Vec, peak_q, some_events, eigen_vectors, sigmas, specify_size, peak_radius,
458 back_inner_radius, back_outer_radius, axes_radii, inti, sigi);
459}
460
462Integrate3DEvents::ellipseIntegrateModEvents(const std::vector<V3D> &E1Vec, V3D const &peak_q, V3D const &hkl,
463 V3D const &mnp, bool specify_size, double peak_radius,
464 double back_inner_radius, double back_outer_radius,
465 std::vector<double> &axes_radii, double &inti, double &sigi) {
466 inti = 0.0; // default values, in case something
467 sigi = 0.0; // is wrong with the peak.
468
469 int64_t hkl_key = getHklMnpKey(boost::math::iround<double>(hkl[0]), boost::math::iround<double>(hkl[1]),
470 boost::math::iround<double>(hkl[2]), boost::math::iround<double>(mnp[0]),
471 boost::math::iround<double>(mnp[1]), boost::math::iround<double>(mnp[2]));
472
473 if (hkl_key == 0) {
474 return std::make_shared<NoShape>();
475 }
476
477 auto pos = m_event_lists.find(hkl_key);
478 if (m_event_lists.end() == pos)
479 return std::make_shared<NoShape>();
480 ;
481
482 const std::vector<std::pair<std::pair<double, double>, V3D>> &some_events = pos->second;
483
484 if (some_events.size() < 3) // if there are not enough events to
485 { // find covariance matrix, return
486 return std::make_shared<NoShape>();
487 }
488
489 DblMatrix cov_matrix(3, 3);
490 if (hkl_key % 1000 == 0)
491 makeCovarianceMatrix(some_events, cov_matrix, m_radius);
492 else
493 makeCovarianceMatrix(some_events, cov_matrix, s_radius);
494
495 std::vector<V3D> eigen_vectors;
496 std::vector<double> eigen_values;
497 getEigenVectors(cov_matrix, eigen_vectors, eigen_values);
498
499 std::vector<double> sigmas(3);
500 for (int i = 0; i < 3; i++)
501 sigmas[i] = sqrt(eigen_values[i]);
502
503 bool invalid_peak =
504 std::any_of(sigmas.cbegin(), sigmas.cend(), [](const double sigma) { return std::isnan(sigma) || sigma <= 0; });
505
506 if (invalid_peak) // if data collapses to a line or
507 { // to a plane, the volume of the
508 return std::make_shared<NoShape>(); // ellipsoids will be zero.
509 }
510
511 return ellipseIntegrateEvents(E1Vec, peak_q, some_events, eigen_vectors, sigmas, specify_size, peak_radius,
512 back_inner_radius, back_outer_radius, axes_radii, inti, sigi);
513}
527std::pair<double, double>
528Integrate3DEvents::numInEllipsoid(std::vector<std::pair<std::pair<double, double>, V3D>> const &events,
529 std::vector<V3D> const &directions, std::vector<double> const &sizes) {
530
531 std::pair<double, double> count(0, 0);
532 for (const auto &event : events) {
533 double sum = 0;
534 for (size_t k = 0; k < 3; k++) {
535 double comp = event.second.scalar_prod(directions[k]) / sizes[k];
536 sum += comp * comp;
537 }
538 if (sum <= 1) {
539 count.first += event.first.first; // count
540 count.second += event.first.second; // error squared (add in quadrature)
541 }
542 }
543
544 return count;
545}
563std::pair<double, double>
564Integrate3DEvents::numInEllipsoidBkg(std::vector<std::pair<std::pair<double, double>, V3D>> const &events,
565 std::vector<V3D> const &directions, std::vector<double> const &sizes,
566 std::vector<double> const &sizesIn, const bool useOnePercentBackgroundCorrection) {
567 std::pair<double, double> count(0, 0);
568 std::vector<std::pair<double, double>> eventVec;
569 for (const auto &event : events) {
570 double sum = 0;
571 double sumIn = 0;
572 for (size_t k = 0; k < 3; k++) {
573 double comp = event.second.scalar_prod(directions[k]) / sizes[k];
574 sum += comp * comp;
575 comp = event.second.scalar_prod(directions[k]) / sizesIn[k];
576 sumIn += comp * comp;
577 }
578 if (sum <= 1 && sumIn >= 1)
579 eventVec.emplace_back(event.first);
580 }
581
582 auto endIndex = eventVec.size();
583 if (useOnePercentBackgroundCorrection) {
584 // Remove top 1% of background
585 std::sort(eventVec.begin(), eventVec.end(),
586 [](const std::pair<double, double> &a, const std::pair<double, double> &b) { return a.first < b.first; });
587 endIndex = static_cast<size_t>(0.99 * static_cast<double>(endIndex));
588 }
589
590 for (size_t k = 0; k < endIndex; ++k) {
591 count.first += eventVec[k].first;
592 count.second += eventVec[k].second;
593 }
594
595 return count;
596}
625void Integrate3DEvents::makeCovarianceMatrix(std::vector<std::pair<std::pair<double, double>, V3D>> const &events,
626 DblMatrix &matrix, double radius) {
627 double totalCounts;
628 for (int row = 0; row < 3; row++) {
629 for (int col = 0; col < 3; col++) {
630 totalCounts = 0;
631 double sum = 0;
632 for (const auto &event : events) {
633 if (event.second.norm() <= radius) {
634 totalCounts += event.first.first;
635 sum += event.first.first * event.second[row] * event.second[col];
636 }
637 }
638 if (totalCounts > 1)
639 matrix[row][col] = sum / (totalCounts - 1);
640 else
641 matrix[row][col] = sum;
642 }
643 }
644}
645
654void Integrate3DEvents::getEigenVectors(DblMatrix const &cov_matrix, std::vector<V3D> &eigen_vectors,
655 std::vector<double> &eigen_values) {
656 unsigned int size = 3;
657
658 gsl_matrix *matrix = gsl_matrix_alloc(size, size);
659 gsl_vector *eigen_val = gsl_vector_alloc(size);
660 gsl_matrix *eigen_vec = gsl_matrix_alloc(size, size);
661 gsl_eigen_symmv_workspace *wkspace = gsl_eigen_symmv_alloc(size);
662
663 // copy the matrix data into the gsl matrix
664 for (size_t row = 0; row < size; row++)
665 for (size_t col = 0; col < size; col++) {
666 gsl_matrix_set(matrix, row, col, cov_matrix[row][col]);
667 }
668
669 gsl_eigen_symmv(matrix, eigen_val, eigen_vec, wkspace);
670
671 // copy the resulting eigen vectors to output vector
672 for (size_t col = 0; col < size; col++) {
673 eigen_vectors.emplace_back(gsl_matrix_get(eigen_vec, 0, col), gsl_matrix_get(eigen_vec, 1, col),
674 gsl_matrix_get(eigen_vec, 2, col));
675 eigen_values.emplace_back(gsl_vector_get(eigen_val, col));
676 }
677
678 gsl_matrix_free(matrix);
679 gsl_vector_free(eigen_val);
680 gsl_matrix_free(eigen_vec);
681 gsl_eigen_symmv_free(wkspace);
682}
683
692int64_t Integrate3DEvents::getHklKey(int h, int k, int l) {
693 int64_t key(0);
694
695 if (h != 0 || k != 0 || l != 0)
696 key = 1000000000000 * h + 100000000 * k + 10000 * l;
697
698 return key;
699}
711int64_t Integrate3DEvents::getHklMnpKey(int h, int k, int l, int m, int n, int p) {
712 int64_t key(0);
713
714 if (h != 0 || k != 0 || l != 0 || m != 0 || n != 0 || p != 0)
715 key = 1000000000000 * h + 100000000 * k + 10000 * l + 100 * m + 10 * n + p;
716
717 return key;
718}
727 int h = boost::math::iround<double>(hkl[0]);
728 int k = boost::math::iround<double>(hkl[1]);
729 int l = boost::math::iround<double>(hkl[2]);
730 return getHklKey(h, k, l);
731}
740 V3D modvec1 = V3D(m_ModHKL[0][0], m_ModHKL[1][0], m_ModHKL[2][0]);
741 V3D modvec2 = V3D(m_ModHKL[0][1], m_ModHKL[1][1], m_ModHKL[2][1]);
742 V3D modvec3 = V3D(m_ModHKL[0][2], m_ModHKL[1][2], m_ModHKL[2][2]);
744 int h = boost::math::iround<double>(hkl[0]);
745 int k = boost::math::iround<double>(hkl[1]);
746 int l = boost::math::iround<double>(hkl[2]);
747
748 return getHklMnpKey(h, k, l, 0, 0, 0);
749 } else if (!crossterm) {
750 if (modvec1 != V3D(0, 0, 0))
751 for (int order = -maxOrder; order <= maxOrder; order++) {
752 if (order == 0)
753 continue; // exclude order 0
754 V3D hkl1(hkl);
755 hkl1[0] -= order * modvec1[0];
756 hkl1[1] -= order * modvec1[1];
757 hkl1[2] -= order * modvec1[2];
759 int h = boost::math::iround<double>(hkl1[0]);
760 int k = boost::math::iround<double>(hkl1[1]);
761 int l = boost::math::iround<double>(hkl1[2]);
762 return getHklMnpKey(h, k, l, order, 0, 0);
763 }
764 }
765 if (modvec2 != V3D(0, 0, 0))
766 for (int order = -maxOrder; order <= maxOrder; order++) {
767 if (order == 0)
768 continue; // exclude order 0
769 V3D hkl1(hkl);
770 hkl1[0] -= order * modvec2[0];
771 hkl1[1] -= order * modvec2[1];
772 hkl1[2] -= order * modvec2[2];
774 int h = boost::math::iround<double>(hkl1[0]);
775 int k = boost::math::iround<double>(hkl1[1]);
776 int l = boost::math::iround<double>(hkl1[2]);
777 return getHklMnpKey(h, k, l, 0, order, 0);
778 }
779 }
780 if (modvec3 != V3D(0, 0, 0))
781 for (int order = -maxOrder; order <= maxOrder; order++) {
782 if (order == 0)
783 continue; // exclude order 0
784 V3D hkl1(hkl);
785 hkl1[0] -= order * modvec3[0];
786 hkl1[1] -= order * modvec3[1];
787 hkl1[2] -= order * modvec3[2];
789 int h = boost::math::iround<double>(hkl1[0]);
790 int k = boost::math::iround<double>(hkl1[1]);
791 int l = boost::math::iround<double>(hkl1[2]);
792 return getHklMnpKey(h, k, l, 0, 0, order);
793 }
794 }
795 } else {
796 int maxOrder1 = maxOrder;
797 if (modvec1 == V3D(0, 0, 0))
798 maxOrder1 = 0;
799 int maxOrder2 = maxOrder;
800 if (modvec2 == V3D(0, 0, 0))
801 maxOrder2 = 0;
802 int maxOrder3 = maxOrder;
803 if (modvec3 == V3D(0, 0, 0))
804 maxOrder3 = 0;
805 for (int m = -maxOrder1; m <= maxOrder1; m++)
806 for (int n = -maxOrder2; n <= maxOrder2; n++)
807 for (int p = -maxOrder3; p <= maxOrder3; p++) {
808 if (m == 0 && n == 0 && p == 0)
809 continue; // exclude 0,0,0
810 V3D hkl1(hkl);
811 V3D mnp = V3D(m, n, p);
812 hkl1 -= m_ModHKL * mnp;
814 int h = boost::math::iround<double>(hkl1[0]);
815 int k = boost::math::iround<double>(hkl1[1]);
816 int l = boost::math::iround<double>(hkl1[2]);
817 return getHklMnpKey(h, k, l, m, n, p);
818 }
819 }
820 }
821 return 0;
822}
830int64_t Integrate3DEvents::getHklKey(V3D const &q_vector) {
831 V3D hkl = m_UBinv * q_vector;
832 int h = boost::math::iround<double>(hkl[0]);
833 int k = boost::math::iround<double>(hkl[1]);
834 int l = boost::math::iround<double>(hkl[2]);
835 return getHklKey(h, k, l);
836}
837
846int64_t Integrate3DEvents::getHklMnpKey(V3D const &q_vector) {
847 V3D hkl = m_UBinv * q_vector;
848
849 V3D modvec1 = V3D(m_ModHKL[0][0], m_ModHKL[1][0], m_ModHKL[2][0]);
850 V3D modvec2 = V3D(m_ModHKL[0][1], m_ModHKL[1][1], m_ModHKL[2][1]);
851 V3D modvec3 = V3D(m_ModHKL[0][2], m_ModHKL[1][2], m_ModHKL[2][2]);
853 int h = boost::math::iround<double>(hkl[0]);
854 int k = boost::math::iround<double>(hkl[1]);
855 int l = boost::math::iround<double>(hkl[2]);
856
857 return getHklMnpKey(h, k, l, 0, 0, 0);
858 } else if (!crossterm) {
859 if (modvec1 != V3D(0, 0, 0))
860 for (int order = -maxOrder; order <= maxOrder; order++) {
861 if (order == 0)
862 continue; // exclude order 0
863 V3D hkl1(hkl);
864 hkl1[0] -= order * modvec1[0];
865 hkl1[1] -= order * modvec1[1];
866 hkl1[2] -= order * modvec1[2];
868 int h = boost::math::iround<double>(hkl1[0]);
869 int k = boost::math::iround<double>(hkl1[1]);
870 int l = boost::math::iround<double>(hkl1[2]);
871 return getHklMnpKey(h, k, l, order, 0, 0);
872 }
873 }
874 if (modvec2 != V3D(0, 0, 0))
875 for (int order = -maxOrder; order <= maxOrder; order++) {
876 if (order == 0)
877 continue; // exclude order 0
878 V3D hkl1(hkl);
879 hkl1[0] -= order * modvec2[0];
880 hkl1[1] -= order * modvec2[1];
881 hkl1[2] -= order * modvec2[2];
883 int h = boost::math::iround<double>(hkl1[0]);
884 int k = boost::math::iround<double>(hkl1[1]);
885 int l = boost::math::iround<double>(hkl1[2]);
886 return getHklMnpKey(h, k, l, 0, order, 0);
887 }
888 }
889 if (modvec3 != V3D(0, 0, 0))
890 for (int order = -maxOrder; order <= maxOrder; order++) {
891 if (order == 0)
892 continue; // exclude order 0
893 V3D hkl1(hkl);
894 hkl1[0] -= order * modvec3[0];
895 hkl1[1] -= order * modvec3[1];
896 hkl1[2] -= order * modvec3[2];
898 int h = boost::math::iround<double>(hkl1[0]);
899 int k = boost::math::iround<double>(hkl1[1]);
900 int l = boost::math::iround<double>(hkl1[2]);
901 return getHklMnpKey(h, k, l, 0, 0, order);
902 }
903 }
904 } else {
905 int maxOrder1 = maxOrder;
906 if (modvec1 == V3D(0, 0, 0))
907 maxOrder1 = 0;
908 int maxOrder2 = maxOrder;
909 if (modvec2 == V3D(0, 0, 0))
910 maxOrder2 = 0;
911 int maxOrder3 = maxOrder;
912 if (modvec3 == V3D(0, 0, 0))
913 maxOrder3 = 0;
914 for (int m = -maxOrder1; m <= maxOrder1; m++)
915 for (int n = -maxOrder2; n <= maxOrder2; n++)
916 for (int p = -maxOrder3; p <= maxOrder3; p++) {
917 if (m == 0 && n == 0 && p == 0)
918 continue; // exclude 0,0,0
919 V3D hkl1(hkl);
920 V3D mnp = V3D(m, n, p);
921 hkl1 -= m_ModHKL * mnp;
923 int h = boost::math::iround<double>(hkl1[0]);
924 int k = boost::math::iround<double>(hkl1[1]);
925 int l = boost::math::iround<double>(hkl1[2]);
926 return getHklMnpKey(h, k, l, m, n, p);
927 }
928 }
929 }
930 return 0;
931}
932
947void Integrate3DEvents::addEvent(std::pair<std::pair<double, double>, V3D> event_Q, bool hkl_integ) {
948 int64_t hkl_key;
949 if (hkl_integ)
950 hkl_key = getHklKey2(event_Q.second);
951 else
952 hkl_key = getHklKey(event_Q.second);
953
954 if (hkl_key == 0) // don't keep events associated with 0,0,0
955 return;
956
957 auto peak_it = m_peak_qs.find(hkl_key);
958 if (peak_it != m_peak_qs.end()) {
959 if (!peak_it->second.nullVector()) {
960 if (hkl_integ)
961 event_Q.second = event_Q.second - m_UBinv * peak_it->second;
962 else
963 event_Q.second = event_Q.second - peak_it->second;
964 if (event_Q.second.norm() < m_radius) {
965 m_event_lists[hkl_key].emplace_back(event_Q);
966 }
967 }
968 }
969}
970
985void Integrate3DEvents::addModEvent(std::pair<std::pair<double, double>, V3D> event_Q, bool hkl_integ) {
986 int64_t hklmnp_key;
987
988 if (hkl_integ)
989 hklmnp_key = getHklMnpKey2(event_Q.second);
990 else
991 hklmnp_key = getHklMnpKey(event_Q.second);
992
993 if (hklmnp_key == 0) // don't keep events associated with 0,0,0
994 return;
995
996 auto peak_it = m_peak_qs.find(hklmnp_key);
997 if (peak_it != m_peak_qs.end()) {
998 if (!peak_it->second.nullVector()) {
999 if (hkl_integ)
1000 event_Q.second = event_Q.second - m_UBinv * peak_it->second;
1001 else
1002 event_Q.second = event_Q.second - peak_it->second;
1003
1004 if (hklmnp_key % 10000 == 0) {
1005 if (event_Q.second.norm() < m_radius)
1006 m_event_lists[hklmnp_key].emplace_back(event_Q);
1007 } else if (event_Q.second.norm() < s_radius) {
1008 m_event_lists[hklmnp_key].emplace_back(event_Q);
1009 }
1010 }
1011 }
1012}
1013
1052 const std::vector<V3D> &E1Vec, V3D const &peak_q,
1053 std::vector<std::pair<std::pair<double, double>, Mantid::Kernel::V3D>> const &ev_list,
1054 std::vector<V3D> const &directions, std::vector<double> const &sigmas, bool specify_size, double peak_radius,
1055 double back_inner_radius, double back_outer_radius, std::vector<double> &axes_radii, double &inti, double &sigi) {
1056 // r1, r2 and r3 will give the sizes of the major axis of
1057 // the peak ellipsoid, and of the inner and outer surface
1058 // of the background ellipsoidal shell, respectively.
1059 // They will specify the size as the number of standard
1060 // deviations in the direction of each of the pricipal
1061 // axes that the ellipsoid will extend from the center.
1062 double r1, r2, r3;
1063
1064 double max_sigma = sigmas[0];
1065 for (int i = 1; i < 3; i++) {
1066 if (sigmas[i] > max_sigma) {
1067 max_sigma = sigmas[i];
1068 }
1069 }
1070
1071 if (specify_size) {
1072 r1 = peak_radius / max_sigma; // scale specified sizes by 1/max_sigma
1073 r2 = back_inner_radius / max_sigma; // so when multiplied by the individual
1074 r3 = back_outer_radius / max_sigma; // sigmas in different directions, the
1075 } // major axis has the specified size
1076 else {
1077 r1 = 3;
1078 r2 = 3;
1079 r3 = r2 * 1.25992105; // A factor of 2 ^ (1/3) will make the background
1080 // shell volume equal to the peak region volume.
1081
1082 // if necessary restrict the background ellipsoid
1083 // to lie within the specified sphere, and adjust
1084 // the other sizes, proportionally
1085 if (r3 * max_sigma > m_radius) {
1086 r3 = m_radius / max_sigma;
1087 r1 = r3 * 0.79370053f; // This value for r1 and r2 makes the background
1088 r2 = r1; // shell volume equal to the peak region volume.
1089 }
1090 }
1091
1092 axes_radii.clear();
1093 std::vector<double> abcBackgroundOuterRadii;
1094 std::vector<double> abcBackgroundInnerRadii;
1095 std::vector<double> abcRadii;
1096 for (int i = 0; i < 3; i++) {
1097 abcBackgroundOuterRadii.emplace_back(r3 * sigmas[i]);
1098 abcBackgroundInnerRadii.emplace_back(r2 * sigmas[i]);
1099 abcRadii.emplace_back(r1 * sigmas[i]);
1100 axes_radii.emplace_back(r1 * sigmas[i]);
1101 }
1102
1103 if (!E1Vec.empty()) {
1104 double h3 = 1.0 - detectorQ(E1Vec, peak_q, abcBackgroundOuterRadii);
1105 // scaled from area of circle minus segment when r normalized to 1
1106 double m3 = std::sqrt(1.0 - (std::acos(1.0 - h3) - (1.0 - h3) * std::sqrt(2.0 * h3 - h3 * h3)) / M_PI);
1107 double h1 = 1.0 - detectorQ(E1Vec, peak_q, axes_radii);
1108 // Do not use peak if edge of detector is inside integration radius
1109 if (h1 > 0.0)
1110 return std::make_shared<const PeakShapeEllipsoid>(directions, abcRadii, abcBackgroundInnerRadii,
1111 abcBackgroundOuterRadii, Mantid::Kernel::QLab,
1112 "IntegrateEllipsoids");
1113 r3 *= m3;
1114 if (r2 != r1) {
1115 double h2 = 1.0 - detectorQ(E1Vec, peak_q, abcBackgroundInnerRadii);
1116 // scaled from area of circle minus segment when r normalized to 1
1117 double m2 = std::sqrt(1.0 - (std::acos(1.0 - h2) - (1.0 - h2) * std::sqrt(2.0 * h2 - h2 * h2)) / M_PI);
1118 r2 *= m2;
1119 }
1120 }
1121
1122 std::pair<double, double> backgrd = numInEllipsoidBkg(ev_list, directions, abcBackgroundOuterRadii,
1123 abcBackgroundInnerRadii, m_useOnePercentBackgroundCorrection);
1124
1125 std::pair<double, double> peak_w_back = numInEllipsoid(ev_list, directions, axes_radii);
1126
1127 double ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
1128
1129 inti = peak_w_back.first - ratio * backgrd.first;
1130 sigi = sqrt(peak_w_back.second + ratio * ratio * backgrd.second);
1131
1132 // Make the shape and return it.
1133 return std::make_shared<const PeakShapeEllipsoid>(directions, abcRadii, abcBackgroundInnerRadii,
1134 abcBackgroundOuterRadii, Mantid::Kernel::QLab,
1135 "IntegrateEllipsoids");
1136}
1148double Integrate3DEvents::detectorQ(const std::vector<V3D> &E1Vec, const V3D &QLabFrame, const std::vector<double> &r) {
1149 double quot = 1.0;
1150 for (auto &E1 : E1Vec) {
1151 V3D distv = QLabFrame - E1 * (QLabFrame.scalar_prod(E1)); // distance to the trajectory as a vector
1152 double quot0 = distv.norm() / *(std::min_element(r.begin(), r.end()));
1153 if (quot0 < quot) {
1154 quot = quot0;
1155 }
1156 }
1157 return quot;
1158}
1159
1167std::tuple<double, double, double> Integrate3DEvents::calculateRadiusFactors(const IntegrationParameters &params,
1168 double max_sigma) const {
1169 double r1 = 0, r2 = 0, r3 = 0;
1170
1171 if (!params.specifySize) {
1172 r1 = 3;
1173 r2 = 3;
1174 r3 = r2 * 1.25992105; // A factor of 2 ^ (1/3) will make the background
1175 // shell volume equal to the peak region volume.
1176
1177 // if necessary restrict the background ellipsoid
1178 // to lie within the specified sphere, and adjust
1179 // the other sizes, proportionally
1180 if (r3 * max_sigma > params.regionRadius) {
1181 r3 = params.regionRadius / max_sigma;
1182 r1 = r3 * 0.79370053f; // This value for r1 and r2 makes the background
1183 r2 = r1; // shell volume equal to the peak region volume.
1184 }
1185 } else {
1186 // scale specified sizes by 1/max_sigma
1187 // so when multiplied by the individual
1188 // sigmas in different directions, the
1189 r1 = params.peakRadius / max_sigma;
1190 r2 = params.backgroundInnerRadius / max_sigma;
1191 r3 = params.backgroundOuterRadius / max_sigma;
1192 }
1193
1194 return std::make_tuple(r1, r2, r3);
1195}
1196
1197} // namespace Mantid::MDAlgorithms
double sigma
Definition: GetAllEi.cpp:156
const int maxOrder
Definition: IndexPeaks.cpp:55
int count
counter
Definition: Matrix.cpp:37
double radius
Definition: Rasterize.cpp:31
static bool ValidIndex(const Kernel::V3D &hkl, double tolerance)
Check is hkl is within tolerance of integer (h,k,l) non-zero values.
Class for 3D vectors.
Definition: V3D.h:34
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition: V3D.h:274
double norm() const noexcept
Definition: V3D.h:263
int64_t getHklKey2(Mantid::Kernel::V3D const &hkl)
Form a map key for the specified q_vector.
Integrate3DEvents(const std::vector< std::pair< std::pair< double, double >, Mantid::Kernel::V3D > > &peak_q_list, Kernel::DblMatrix UBinv, double radius, const bool useOnePercentBackgroundCorrection=true)
Construct object to store events around peaks and integrate peaks.
static int64_t getHklMnpKey(int h, int k, int l, int m, int n, int p)
Form a map key as 10^12*h + 10^6*k + l from the integers h,k,l.
std::shared_ptr< const Mantid::Geometry::PeakShape > ellipseIntegrateModEvents(const std::vector< Kernel::V3D > &E1Vec, Mantid::Kernel::V3D const &peak_q, Mantid::Kernel::V3D const &hkl, Mantid::Kernel::V3D const &mnp, bool specify_size, double peak_radius, double back_inner_radius, double back_outer_radius, std::vector< double > &axes_radii, double &inti, double &sigi)
Find the net integrated intensity of a modulated peak, using ellipsoidal volumes.
std::shared_ptr< const Mantid::Geometry::PeakShape > ellipseIntegrateEvents(const std::vector< Kernel::V3D > &E1Vec, Mantid::Kernel::V3D const &peak_q, bool specify_size, double peak_radius, double back_inner_radius, double back_outer_radius, std::vector< double > &axes_radii, double &inti, double &sigi)
Find the net integrated intensity of a peak, using ellipsoidal volumes.
void addEvent(std::pair< std::pair< double, double >, Mantid::Kernel::V3D > event_Q, bool hkl_integ)
Add an event to the vector of events for the closest h,k,l.
static std::pair< double, double > numInEllipsoidBkg(std::vector< std::pair< std::pair< double, double >, Mantid::Kernel::V3D > > const &events, std::vector< Mantid::Kernel::V3D > const &directions, std::vector< double > const &sizes, std::vector< double > const &sizesIn, const bool useOnePercentBackgroundCorrection)
Calculate the number of events in an ellipsoid centered at 0,0,0.
std::pair< std::shared_ptr< const Mantid::Geometry::PeakShape >, std::tuple< double, double, double > > integrateStrongPeak(const IntegrationParameters &params, const Kernel::V3D &peak_q, double &inti, double &sigi)
Find the net integrated intensity of a peak, using ellipsoidal volumes.
static void makeCovarianceMatrix(std::vector< std::pair< std::pair< double, double >, Mantid::Kernel::V3D > > const &events, Kernel::DblMatrix &matrix, double radius)
Calculate the 3x3 covariance matrix of a list of Q-vectors at 0,0,0.
void addModEvent(std::pair< std::pair< double, double >, Mantid::Kernel::V3D > event_Q, bool hkl_integ)
Add an event to the appropriate vector of events for the closest h,k,l, if it is within the required ...
static std::pair< double, double > numInEllipsoid(std::vector< std::pair< std::pair< double, double >, Mantid::Kernel::V3D > > const &events, std::vector< Mantid::Kernel::V3D > const &directions, std::vector< double > const &sizes)
Calculate the number of events in an ellipsoid centered at 0,0,0.
std::shared_ptr< const Geometry::PeakShape > integrateWeakPeak(const IntegrationParameters &params, Mantid::DataObjects::PeakShapeEllipsoid_const_sptr shape, const std::tuple< double, double, double > &libPeak, const Mantid::Kernel::V3D &peak_q, double &inti, double &sigi)
double detectorQ(const std::vector< Kernel::V3D > &E1Vec, const Mantid::Kernel::V3D &QLabFrame, const std::vector< double > &r)
Compute if a particular Q falls on the edge of a detector.
bool correctForDetectorEdges(std::tuple< double, double, double > &radii, const std::vector< Mantid::Kernel::V3D > &E1Vecs, const Mantid::Kernel::V3D &peak_q, const std::vector< double > &axesRadii, const std::vector< double > &bkgInnerRadii, const std::vector< double > &bkgOuterRadii)
std::tuple< double, double, double > calculateRadiusFactors(const IntegrationParameters &params, double max_sigma) const
Calculate the radius to use for each axis of the ellipsoid from the parameters provided.
double estimateSignalToNoiseRatio(const IntegrationParameters &params, const Mantid::Kernel::V3D &center, bool forceSpherical=false, double sphericityTol=0.02)
static void getEigenVectors(Kernel::DblMatrix const &cov_matrix, std::vector< Mantid::Kernel::V3D > &eigen_vectors, std::vector< double > &eigen_values)
Calculate the eigen vectors of a 3x3 real symmetric matrix.
static int64_t getHklKey(int h, int k, int l)
Form a map key as 10^12*h + 10^6*k + l from the integers h, k, l.
void addEvents(std::vector< std::pair< std::pair< double, double >, Mantid::Kernel::V3D > > const &event_qs, bool hkl_integ)
Add event Q's to lists of events near peaks.
int64_t getHklMnpKey2(Mantid::Kernel::V3D const &hkl)
Form a map key for the specified q_vector.
const std::vector< std::pair< std::pair< double, double >, Mantid::Kernel::V3D > > * getEvents(const Mantid::Kernel::V3D &peak_q)
Get a list of events for a given Q.
std::complex< double > MANTID_API_DLL E1(std::complex< double > z)
Integral for Gamma.
std::shared_ptr< const PeakShapeEllipsoid > PeakShapeEllipsoid_const_sptr
std::shared_ptr< const PeakShape > PeakShape_const_sptr
Definition: PeakShape.h:43
STL namespace.