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 <algorithm>
13#include <boost/math/special_functions/round.hpp>
14#include <cmath>
15#include <fstream>
16#include <limits>
17#include <memory>
18#include <numeric>
19#include <tuple>
20
21extern "C" {
22#include <cstdio>
23#include <utility>
24
25#include <gsl/gsl_eigen.h>
26#include <gsl/gsl_matrix.h>
27#include <gsl/gsl_vector.h>
28}
29
30using namespace Mantid::DataObjects;
31namespace Mantid::MDAlgorithms {
32
33using namespace std;
36
51 const std::vector<std::pair<std::pair<double, double>, Mantid::Kernel::V3D>> &peak_q_list, Kernel::DblMatrix UBinv,
52 double radius, const bool useOnePercentBackgroundCorrection)
53 : m_UBinv(std::move(UBinv)), m_radius(radius), maxOrder(0), crossterm(false),
54 m_useOnePercentBackgroundCorrection(useOnePercentBackgroundCorrection) {
55 for (size_t it = 0; it != peak_q_list.size(); ++it) {
56 int64_t hkl_key = getHklKey(peak_q_list[it].second);
57 if (hkl_key != 0) // only save if hkl != (0,0,0)
58 m_peak_qs[hkl_key] = peak_q_list[it].second;
59 }
60}
61
85 const std::vector<std::pair<std::pair<double, double>, Mantid::Kernel::V3D>> &peak_q_list,
86 std::vector<V3D> const &hkl_list, std::vector<V3D> const &mnp_list, Kernel::DblMatrix UBinv,
87 Kernel::DblMatrix ModHKL, double radius_m, double radius_s, int MaxO, const bool CrossT,
88 const bool useOnePercentBackgroundCorrection)
89 : m_UBinv(std::move(UBinv)), m_ModHKL(std::move(ModHKL)), m_radius(radius_m), s_radius(radius_s), maxOrder(MaxO),
90 crossterm(CrossT), m_useOnePercentBackgroundCorrection(useOnePercentBackgroundCorrection) {
91 for (size_t it = 0; it != peak_q_list.size(); ++it) {
92 int64_t hklmnp_key =
93 getHklMnpKey(boost::math::iround<double>(hkl_list[it][0]), boost::math::iround<double>(hkl_list[it][1]),
94 boost::math::iround<double>(hkl_list[it][2]), boost::math::iround<double>(mnp_list[it][0]),
95 boost::math::iround<double>(mnp_list[it][1]), boost::math::iround<double>(mnp_list[it][2]));
96 if (hklmnp_key != 0) // only save if hkl != (0,0,0)
97 m_peak_qs[hklmnp_key] = peak_q_list[it].second;
98 }
99}
100
118void Integrate3DEvents::addEvents(std::vector<std::pair<std::pair<double, double>, V3D>> const &event_qs,
119 bool hkl_integ) {
120 if (!maxOrder)
121 for (const auto &event_q : event_qs)
122 addEvent(event_q, hkl_integ);
123 else
124 for (const auto &event_q : event_qs)
125 addModEvent(event_q, hkl_integ);
126}
127
128std::pair<std::shared_ptr<const Geometry::PeakShape>, std::tuple<double, double, double>>
129Integrate3DEvents::integrateStrongPeak(const IntegrationParameters &params, const V3D &peak_q, double &inti,
130 double &sigi) {
131
132 inti = 0.0; // default values, in case something
133 sigi = 0.0; // is wrong with the peak.
134 auto result = getEvents(peak_q);
135 if (!result)
136 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
137
138 const auto &events = *result;
139 if (events.empty())
140 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
141
142 DblMatrix cov_matrix(3, 3);
143 makeCovarianceMatrix(events, cov_matrix, params.regionRadius);
144
145 std::array<V3D, 3> eigen_vectors;
146 std::array<double, 3> eigen_values;
147 getEigenVectors(cov_matrix, eigen_vectors, eigen_values);
148
149 std::array<double, 3> sigmas;
150 for (int i = 0; i < 3; i++) {
151 sigmas[i] = sqrt(eigen_values[i]);
152 }
153
154 bool invalid_peak =
155 std::any_of(sigmas.cbegin(), sigmas.cend(), [](const double sigma) { return std::isnan(sigma) || sigma <= 0; });
156
157 if (invalid_peak)
158 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
159
160 const auto max_sigma = *std::max_element(sigmas.begin(), sigmas.end());
161 if (max_sigma == 0)
162 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
163
164 auto rValues = calculateRadiusFactors(params, max_sigma);
165 auto const &r1 = std::get<0>(rValues), r2 = std::get<1>(rValues), r3 = std::get<2>(rValues);
166
167 DataObjects::PeakEllipsoidExtent abcBackgroundOuterRadii, abcBackgroundInnerRadii;
169 for (int i = 0; i < 3; i++) {
170 abcBackgroundOuterRadii[i] = r3 * sigmas[i];
171 abcBackgroundInnerRadii[i] = r2 * sigmas[i];
172 peakRadii[i] = r1 * sigmas[i];
173 }
174
175 const auto isPeakOnDetector = correctForDetectorEdges(rValues, params.E1Vectors, peak_q, peakRadii,
176 abcBackgroundInnerRadii, abcBackgroundOuterRadii);
177
178 if (!isPeakOnDetector)
179 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0.0, 0.0, 0.));
180
181 const auto backgrd = numInEllipsoidBkg(events, eigen_vectors, abcBackgroundOuterRadii, abcBackgroundInnerRadii,
183 const auto core = numInEllipsoid(events, eigen_vectors, sigmas);
184 const auto peak = numInEllipsoid(events, eigen_vectors, peakRadii);
185 const auto ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
186
187 inti = peak.first - ratio * backgrd.first;
188 sigi = sqrt(peak.second + ratio * ratio * backgrd.second);
189
190 // compute the fraction of peak within the standard core
191 const auto total = (core.first + peak.first) - ratio * backgrd.first;
192 const auto frac = std::min(1.0, std::abs(inti / total));
193 // compute the uncertainty in the fraction
194 const auto df_ds_core = (1 - frac) / peak.first;
195 const auto df_ds_peak = frac / peak.first;
196 const auto fracError = sqrt(peak.first * pow(df_ds_core, 2) + core.first * pow(df_ds_peak, 2));
197
198 // create the peaks shape for the strong peak
199 const auto shape = std::make_shared<const PeakShapeEllipsoid>(eigen_vectors, peakRadii, abcBackgroundInnerRadii,
200 abcBackgroundOuterRadii, Mantid::Kernel::QLab,
201 "IntegrateEllipsoidsTwoStep");
202
203 return std::make_pair(shape, std::make_tuple(frac, fracError, max_sigma));
204}
205
220std::shared_ptr<const Geometry::PeakShape>
222 const std::tuple<double, double, double> &libPeak, const V3D &peak_q, double &inti,
223 double &sigi) {
224
225 inti = 0.0; // default values, in case something
226 sigi = 0.0; // is wrong with the peak.
227
228 auto result = getEvents(peak_q);
229 if (!result)
230 return std::make_shared<NoShape>();
231
232 const auto &events = *result;
233
234 const auto &directions = shape->directions();
235 auto abcBackgroundInnerRadii = shape->abcRadiiBackgroundInner();
236 auto abcBackgroundOuterRadii = shape->abcRadiiBackgroundOuter();
237 auto abcRadii = shape->abcRadii();
238
239 const auto max_sigma = std::get<2>(libPeak);
240 auto rValues = calculateRadiusFactors(params, max_sigma);
241
242 const auto isPeakOnDetector = correctForDetectorEdges(rValues, params.E1Vectors, peak_q, abcRadii,
243 abcBackgroundInnerRadii, abcBackgroundOuterRadii);
244
245 if (!isPeakOnDetector)
246 return shape;
247
248 const double r1 = std::get<0>(rValues), r2 = std::get<1>(rValues), r3 = std::get<2>(rValues);
249
250 // integrate
251 std::pair<double, double> backgrd = numInEllipsoidBkg(events, directions, abcBackgroundOuterRadii,
252 abcBackgroundInnerRadii, m_useOnePercentBackgroundCorrection);
253 std::pair<double, double> peak_w_back = numInEllipsoid(events, directions, abcRadii);
254 double ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
255
256 const auto frac = std::get<0>(libPeak);
257 const auto fracError = std::get<1>(libPeak);
258
259 inti = peak_w_back.first - ratio * backgrd.first;
260
261 // correct for fractional intensity
262 sigi = sigi / pow(inti, 2);
263 sigi += pow((fracError / frac), 2);
264
265 inti = inti * frac;
266 sigi = sqrt(sigi) * inti;
267
268 // scale integration shape by fractional amount
269 for (size_t i = 0; i < abcRadii.size(); ++i) {
270 abcRadii[i] *= frac;
271 abcBackgroundInnerRadii[i] *= frac;
272 abcBackgroundOuterRadii[i] *= frac;
273 }
274
275 return std::make_shared<const PeakShapeEllipsoid>(shape->directions(), abcRadii, abcBackgroundInnerRadii,
276 abcBackgroundOuterRadii, Mantid::Kernel::QLab,
277 "IntegrateEllipsoidsTwoStep");
278}
279
280namespace {
287
298void solvePoissonMatchedFilter(const std::vector<double> &g, const std::vector<double> &w, double normG, double volume,
299 double &A, double &b, double &sigA) {
300 const auto n = g.size();
301
302 auto logLikelihood = [&](double a, double bb) {
303 double ll = -(a * normG + bb * volume);
304 for (size_t i = 0; i < n; ++i)
305 ll += w[i] * std::log(std::max(a * g[i] + bb, 1e-300));
306 return ll;
307 };
308
309 auto logLikelihoodGradHess = [&](double a, double bb, double &dA, double &dB, double &dAA, double &dBB, double &dAB) {
310 dA = -normG;
311 dB = -volume;
312 dAA = 0.0;
313 dBB = 0.0;
314 dAB = 0.0;
315 for (size_t i = 0; i < n; ++i) {
316 const auto mu = std::max(a * g[i] + bb, 1e-12);
317 const auto wi = w[i];
318 dA += wi * g[i] / mu;
319 dB += wi / mu;
320 dAA -= wi * g[i] * g[i] / (mu * mu);
321 dBB -= wi / (mu * mu);
322 dAB -= wi * g[i] / (mu * mu);
323 }
324 };
325
326 // seed from crude estimates: split total weight evenly between a peak
327 // (using the mean kernel value over events with above-average signal) and
328 // a flat background over the search volume, deliberately conservative so
329 // the backtracking Newton solve below has a well-behaved starting point
330 // regardless of the absolute scale of normG/volume/event counts
331 const double sumW = std::accumulate(w.begin(), w.end(), 0.0);
332 b = volume > 0 ? std::max(0.5 * sumW / volume, 1e-10) : 1e-10;
333 A = normG > 0 ? std::max(0.5 * sumW / normG, 1e-10) : 1e-10;
334
335 for (int iter = 0; iter < 100; ++iter) {
336 double dA, dB, dAA, dBB, dAB;
337 logLikelihoodGradHess(A, b, dA, dB, dAA, dBB, dAB);
338
339 const auto det = dAA * dBB - dAB * dAB;
340 if (std::abs(det) < 1e-300)
341 break;
342
343 const auto deltaA = -(dBB * dA - dAB * dB) / det;
344 const auto deltaB = -(-dAB * dA + dAA * dB) / det;
345
346 // backtrack until both parameters stay non-negative AND the
347 // log-likelihood actually improves -- plain Newton without this can
348 // overshoot and diverge for a poorly-scaled seed
349 const auto llOld = logLikelihood(A, b);
350 double step = 1.0;
351 double newA = A, newB = b;
352 double llNew = llOld;
353 for (int halving = 0; halving < 60; ++halving) {
354 newA = A + step * deltaA;
355 newB = b + step * deltaB;
356 if (newA >= 0.0 && newB >= 0.0) {
357 llNew = logLikelihood(newA, newB);
358 if (llNew >= llOld)
359 break;
360 }
361 step *= 0.5;
362 }
363 if (llNew < llOld) // no improving step found within the halving budget
364 break;
365
366 const auto converged = std::abs(newA - A) < 1e-12 * (1.0 + A) && std::abs(newB - b) < 1e-12 * (1.0 + b);
367 A = newA;
368 b = newB;
369 if (converged)
370 break;
371 }
372
373 double dA, dB, dAA, dBB, dAB;
374 logLikelihoodGradHess(A, b, dA, dB, dAA, dBB, dAB);
375 const auto det = dAA * dBB - dAB * dAB;
376 sigA = std::numeric_limits<double>::infinity();
377 if (det > 0.0 && std::isfinite(det)) {
378 const auto varianceA = -dBB / det;
379 if (varianceA > 0.0 && std::isfinite(varianceA))
380 sigA = std::sqrt(varianceA);
381 }
382}
383
393bool solve3x3(const double H[3][3], const double g[3], double x[3]) {
394 const auto det = H[0][0] * (H[1][1] * H[2][2] - H[1][2] * H[2][1]) -
395 H[0][1] * (H[1][0] * H[2][2] - H[1][2] * H[2][0]) +
396 H[0][2] * (H[1][0] * H[2][1] - H[1][1] * H[2][0]);
397 if (std::abs(det) < 1e-300)
398 return false;
399
400 for (int col = 0; col < 3; ++col) {
401 double M[3][3];
402 for (int r = 0; r < 3; ++r)
403 for (int c = 0; c < 3; ++c)
404 M[r][c] = (c == col) ? -g[r] : H[r][c];
405 const auto colDet = M[0][0] * (M[1][1] * M[2][2] - M[1][2] * M[2][1]) -
406 M[0][1] * (M[1][0] * M[2][2] - M[1][2] * M[2][0]) +
407 M[0][2] * (M[1][0] * M[2][1] - M[1][1] * M[2][0]);
408 x[col] = colDet / det;
409 }
410 return true;
411}
412} // namespace
413
425void Integrate3DEvents::integrateUsingShape(const PeakShapeEllipsoid &shape, const V3D &peak_q, double &inti,
426 double &sigi) {
427 inti = 0.0; // default values, in case something
428 sigi = 0.0; // is wrong with the peak.
429
430 auto result = getEvents(peak_q);
431 if (!result)
432 return;
433
434 const auto &events = *result;
435
436 const auto &directions = shape.directions();
437 const auto &abcRadii = shape.abcRadii();
438 const auto &abcBackgroundInnerRadii = shape.abcRadiiBackgroundInner();
439 const auto &abcBackgroundOuterRadii = shape.abcRadiiBackgroundOuter();
440
441 const std::pair<double, double> backgrd = numInEllipsoidBkg(
442 events, directions, abcBackgroundOuterRadii, abcBackgroundInnerRadii, m_useOnePercentBackgroundCorrection);
443 const std::pair<double, double> peak = numInEllipsoid(events, directions, abcRadii);
444
445 // volume ratio between the peak ellipsoid and the background shell, used to
446 // scale the background counts to the peak's volume before subtracting
447 const auto peakVolume = abcRadii[0] * abcRadii[1] * abcRadii[2];
448 const auto backgroundVolume = abcBackgroundOuterRadii[0] * abcBackgroundOuterRadii[1] * abcBackgroundOuterRadii[2] -
449 abcBackgroundInnerRadii[0] * abcBackgroundInnerRadii[1] * abcBackgroundInnerRadii[2];
450 const auto ratio = backgroundVolume > 0 ? peakVolume / backgroundVolume : 0.0;
451
452 inti = peak.first - ratio * backgrd.first;
453 sigi = sqrt(peak.second + ratio * ratio * backgrd.second);
454}
455
474 bool adjustCenter, V3D &center, double &inti, double &sigi) {
475 inti = 0.0;
476 sigi = 0.0;
477 center = shape.translation();
478
479 auto result = getEvents(peak_q);
480 if (!result || result->empty())
481 return;
482
483 const auto &events = *result;
484
485 const auto &directions = shape.directions();
486 const auto &sigmas = shape.abcRadii();
487
488 // apply the ellipsoid's inverse-covariance to v, via its spectral (axis) form
489 auto applyInvS = [&](const V3D &v) {
490 V3D out;
491 for (size_t k = 0; k < 3; ++k)
492 out += directions[k] * (v.scalar_prod(directions[k]) / (sigmas[k] * sigmas[k]));
493 return out;
494 };
495
496 // kernel value and inv_S*(event-c) (needed for the center gradient) for
497 // every event, at center offset c from peak_q
498 auto computeKernel = [&](const V3D &c, std::vector<double> &g, std::vector<double> &w, std::vector<V3D> &invSq) {
499 g.clear();
500 w.clear();
501 invSq.clear();
502 g.reserve(events.size());
503 w.reserve(events.size());
504 invSq.reserve(events.size());
505 for (const auto &event : events) {
506 const V3D qOff = event.second - c;
507 const auto Sq = applyInvS(qOff);
508 g.push_back(exp(-0.5 * qOff.scalar_prod(Sq)));
509 w.push_back(event.first.first);
510 invSq.push_back(Sq);
511 }
512 };
513
514 // sum(g) over all space is translation-invariant, so normG does not
515 // depend on the center offset c
516 const auto normG = pow(2.0 * M_PI, 1.5) * sigmas[0] * sigmas[1] * sigmas[2];
517 const auto volume = (4.0 / 3.0) * M_PI * m_radius * m_radius * m_radius;
518
519 auto logLikelihood = [&](const V3D &c, double A, double b) {
520 double ll = -(A * normG + b * volume);
521 for (const auto &event : events) {
522 const V3D qOff = event.second - c;
523 const auto gi = exp(-0.5 * qOff.scalar_prod(applyInvS(qOff)));
524 ll += event.first.first * log(std::max(A * gi + b, 1e-12));
525 }
526 return ll;
527 };
528
529 std::vector<double> g, w;
530 std::vector<V3D> invSq;
531 computeKernel(center, g, w, invSq);
532
533 double A = 0.0, b = 0.0, sigA = 0.0;
534 solvePoissonMatchedFilter(g, w, normG, volume, A, b, sigA);
535
536 if (adjustCenter) {
537 const auto maxShift = *std::min_element(sigmas.begin(), sigmas.end());
538 // seedCenter is where this call started (shape.translation()); maxShift
539 // bounds the *incremental* correction made in this call, not the total
540 // distance from peak_q, so an already-substantial prior translation
541 // isn't clamped away before any new refinement even starts.
542 const V3D seedCenter = center;
543
544 for (int outer = 0; outer < 20; ++outer) {
545 // Gauss-Newton step in the center, holding A, b fixed
546 double grad[3] = {0.0, 0.0, 0.0};
547 double hess[3][3] = {{0.0}};
548 for (size_t i = 0; i < g.size(); ++i) {
549 const auto mu = std::max(A * g[i] + b, 1e-12);
550 const auto factor = w[i] * g[i] / mu;
551 const auto factor2 = w[i] * g[i] * g[i] / (mu * mu);
552 const double Sq[3] = {invSq[i].X(), invSq[i].Y(), invSq[i].Z()};
553 for (int r = 0; r < 3; ++r) {
554 grad[r] += A * factor * Sq[r];
555 for (int c = 0; c < 3; ++c)
556 hess[r][c] -= A * A * factor2 * Sq[r] * Sq[c];
557 }
558 }
559
560 double deltaC[3];
561 if (!solve3x3(hess, grad, deltaC))
562 break;
563
564 V3D step(deltaC[0], deltaC[1], deltaC[2]);
565 if (step.norm() > 0.25 * maxShift)
566 step *= (0.25 * maxShift / step.norm());
567
568 auto clampToMaxShift = [&](V3D c) {
569 const V3D offsetFromSeed = c - seedCenter;
570 if (offsetFromSeed.norm() > maxShift)
571 return seedCenter + offsetFromSeed * (maxShift / offsetFromSeed.norm());
572 return c;
573 };
574
575 const auto llOld = logLikelihood(center, A, b);
576 V3D newCenter = clampToMaxShift(center + step);
577 auto llNew = logLikelihood(newCenter, A, b);
578
579 double localStep = 1.0;
580 while (llNew < llOld && localStep > 1e-4) {
581 localStep *= 0.5;
582 newCenter = clampToMaxShift(center + step * localStep);
583 llNew = logLikelihood(newCenter, A, b);
584 }
585
586 const auto converged = (newCenter - center).norm() < 1e-8 * (1.0 + maxShift);
587 center = newCenter;
588
589 computeKernel(center, g, w, invSq);
590 solvePoissonMatchedFilter(g, w, normG, volume, A, b, sigA);
591
592 if (converged)
593 break;
594 }
595 }
596
597 inti = A * normG;
598 sigi = sigA * normG;
599}
600
613 bool forceSpherical, double sphericityTol) {
614
615 auto result = getEvents(center);
616 if (!result)
617 return .0;
618
619 const auto &events = *result;
620 if (events.empty())
621 return .0;
622
623 DblMatrix cov_matrix(3, 3);
624 makeCovarianceMatrix(events, cov_matrix, params.regionRadius);
625
626 std::array<V3D, 3> eigen_vectors;
627 std::array<double, 3> eigen_values;
628 getEigenVectors(cov_matrix, eigen_vectors, eigen_values);
629
630 std::array<double, 3> sigmas;
631 for (int i = 0; i < 3; i++) {
632 sigmas[i] = sqrt(eigen_values[i]);
633 }
634
635 const auto max_sigma = *std::max_element(sigmas.begin(), sigmas.end());
636 const auto min_sigma = *std::min_element(sigmas.begin(), sigmas.end());
637 if (max_sigma == 0)
638 return .0;
639
640 auto rValues = calculateRadiusFactors(params, max_sigma);
641 auto const &r1 = std::get<0>(rValues), r2 = std::get<1>(rValues), r3 = std::get<2>(rValues);
642 DataObjects::PeakEllipsoidExtent abcBackgroundOuterRadii, abcBackgroundInnerRadii;
644 if (forceSpherical) {
645 // test for spherically symmeteric peak (within tolerance)
646 if ((max_sigma - min_sigma) / max_sigma > sphericityTol)
647 return .0;
648 for (int i = 0; i < 3; i++) {
649 abcBackgroundOuterRadii[i] = r3 * max_sigma;
650 abcBackgroundInnerRadii[i] = r2 * max_sigma;
651 peakRadii[i] = r1 * max_sigma;
652 }
653 } else {
654 for (int i = 0; i < 3; i++) {
655 abcBackgroundOuterRadii[i] = r3 * sigmas[i];
656 abcBackgroundInnerRadii[i] = r2 * sigmas[i];
657 peakRadii[i] = r1 * sigmas[i];
658 }
659 }
660
661 // Background / Peak / Background
662 std::pair<double, double> backgrd = numInEllipsoidBkg(events, eigen_vectors, abcBackgroundOuterRadii,
663 abcBackgroundInnerRadii, m_useOnePercentBackgroundCorrection);
664
665 std::pair<double, double> peak_w_back = numInEllipsoid(events, eigen_vectors, peakRadii);
666
667 double ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
668 auto inti = peak_w_back.first - ratio * backgrd.first;
669 auto sigi = sqrt(peak_w_back.second + ratio * ratio * backgrd.second);
670
671 return inti / sigi;
672}
673
674const std::vector<std::pair<std::pair<double, double>, V3D>> *Integrate3DEvents::getEvents(const V3D &peak_q) {
675 auto hkl_key = getHklKey(peak_q);
676 if (maxOrder)
677 hkl_key = getHklMnpKey(peak_q);
678
679 if (hkl_key == 0)
680 return nullptr;
681
682 const auto pos = m_event_lists.find(hkl_key);
683
684 if (m_event_lists.end() == pos)
685 return nullptr;
686
687 if (pos->second.size() < 3) // if there are not enough events
688 return nullptr;
689
690 return &(pos->second);
691}
692
693bool Integrate3DEvents::correctForDetectorEdges(std::tuple<double, double, double> &radii,
694 const std::vector<V3D> &E1Vecs, const V3D &peak_q,
695 const DataObjects::PeakEllipsoidExtent &axesRadii,
696 const DataObjects::PeakEllipsoidExtent &bkgInnerRadii,
697 const DataObjects::PeakEllipsoidExtent &bkgOuterRadii) {
698
699 if (E1Vecs.empty())
700 return true;
701
702 const auto &r1 = std::get<0>(radii);
703 auto &r2 = std::get<1>(radii);
704 auto &r3 = std::get<2>(radii);
705 auto h3 = 1.0 - detectorQ(E1Vecs, peak_q, bkgOuterRadii);
706 // scaled from area of circle minus segment when r normalized to 1
707 auto m3 = std::sqrt(1.0 - (std::acos(1.0 - h3) - (1.0 - h3) * std::sqrt(2.0 * h3 - h3 * h3)) / M_PI);
708 auto h1 = 1.0 - detectorQ(E1Vecs, peak_q, axesRadii);
709 // Do not use peak if edge of detector is inside integration radius
710 if (h1 > 0.0)
711 return false;
712
713 r3 *= m3;
714 if (r2 != r1) {
715 auto h2 = 1.0 - detectorQ(E1Vecs, peak_q, bkgInnerRadii);
716 // scaled from area of circle minus segment when r normalized to 1
717 auto m2 = std::sqrt(1.0 - (std::acos(1.0 - h2) - (1.0 - h2) * std::sqrt(2.0 * h2 - h2 * h2)) / M_PI);
718 r2 *= m2;
719 }
720
721 return true;
722}
723
761Integrate3DEvents::ellipseIntegrateEvents(const std::vector<V3D> &E1Vec, V3D const &peak_q, bool specify_size,
762 double peak_radius, double back_inner_radius, double back_outer_radius,
763 DataObjects::PeakEllipsoidExtent &axes_radii, double &inti, double &sigi) {
764 inti = 0.0; // default values, in case something
765 sigi = 0.0; // is wrong with the peak.
766
767 int64_t hkl_key = getHklKey(peak_q);
768
769 if (hkl_key == 0) {
770 return std::make_shared<NoShape>();
771 }
772
773 auto pos = m_event_lists.find(hkl_key);
774 if (m_event_lists.end() == pos)
775 return std::make_shared<NoShape>();
776 ;
777
778 const std::vector<std::pair<std::pair<double, double>, V3D>> &some_events = pos->second;
779
780 if (some_events.size() < 3) // if there are not enough events to
781 { // find covariance matrix, return
782 return std::make_shared<NoShape>();
783 }
784
785 DblMatrix cov_matrix(3, 3);
786 makeCovarianceMatrix(some_events, cov_matrix, m_radius);
787
788 std::array<V3D, 3> eigen_vectors;
789 std::array<double, 3> eigen_values;
790 getEigenVectors(cov_matrix, eigen_vectors, eigen_values);
791
792 std::array<double, 3> sigmas;
793 for (int i = 0; i < 3; i++) {
794 sigmas[i] = sqrt(eigen_values[i]);
795 }
796
797 bool invalid_peak =
798 std::any_of(sigmas.cbegin(), sigmas.cend(), [](const double sigma) { return std::isnan(sigma) || sigma <= 0; });
799
800 if (invalid_peak) // if data collapses to a line or
801 { // to a plane, the volume of the
802 return std::make_shared<NoShape>(); // ellipsoids will be zero.
803 }
804
805 return ellipseIntegrateEvents(E1Vec, peak_q, some_events, eigen_vectors, sigmas, specify_size, peak_radius,
806 back_inner_radius, back_outer_radius, axes_radii, inti, sigi);
807}
808
810Integrate3DEvents::ellipseIntegrateModEvents(const std::vector<V3D> &E1Vec, V3D const &peak_q, V3D const &hkl,
811 V3D const &mnp, bool specify_size, double peak_radius,
812 double back_inner_radius, double back_outer_radius,
813 DataObjects::PeakEllipsoidExtent &axes_radii, double &inti, double &sigi) {
814 inti = 0.0; // default values, in case something
815 sigi = 0.0; // is wrong with the peak.
816
817 int64_t hkl_key = getHklMnpKey(boost::math::iround<double>(hkl[0]), boost::math::iround<double>(hkl[1]),
818 boost::math::iround<double>(hkl[2]), boost::math::iround<double>(mnp[0]),
819 boost::math::iround<double>(mnp[1]), boost::math::iround<double>(mnp[2]));
820
821 if (hkl_key == 0) {
822 return std::make_shared<NoShape>();
823 }
824
825 auto pos = m_event_lists.find(hkl_key);
826 if (m_event_lists.end() == pos)
827 return std::make_shared<NoShape>();
828 ;
829
830 const std::vector<std::pair<std::pair<double, double>, V3D>> &some_events = pos->second;
831
832 if (some_events.size() < 3) // if there are not enough events to
833 { // find covariance matrix, return
834 return std::make_shared<NoShape>();
835 }
836
837 DblMatrix cov_matrix(3, 3);
838 if (hkl_key % 1000 == 0)
839 makeCovarianceMatrix(some_events, cov_matrix, m_radius);
840 else
841 makeCovarianceMatrix(some_events, cov_matrix, s_radius);
842
843 std::array<V3D, 3> eigen_vectors;
844 std::array<double, 3> eigen_values;
845 getEigenVectors(cov_matrix, eigen_vectors, eigen_values);
846
847 std::array<double, 3> sigmas;
848 for (int i = 0; i < 3; i++)
849 sigmas[i] = sqrt(eigen_values[i]);
850
851 bool invalid_peak =
852 std::any_of(sigmas.cbegin(), sigmas.cend(), [](const double sigma) { return std::isnan(sigma) || sigma <= 0; });
853
854 if (invalid_peak) // if data collapses to a line or
855 { // to a plane, the volume of the
856 return std::make_shared<NoShape>(); // ellipsoids will be zero.
857 }
858
859 return ellipseIntegrateEvents(E1Vec, peak_q, some_events, eigen_vectors, sigmas, specify_size, peak_radius,
860 back_inner_radius, back_outer_radius, axes_radii, inti, sigi);
861}
875std::pair<double, double>
876Integrate3DEvents::numInEllipsoid(std::vector<std::pair<std::pair<double, double>, V3D>> const &events,
877 DataObjects::PeakEllipsoidFrame const &directions,
879
880 std::pair<double, double> count(0, 0);
881 for (const auto &event : events) {
882 double sum = 0;
883 for (size_t k = 0; k < 3; k++) {
884 double comp = event.second.scalar_prod(directions[k]) / sizes[k];
885 sum += comp * comp;
886 }
887 if (sum <= 1) {
888 count.first += event.first.first; // count
889 count.second += event.first.second; // error squared (add in quadrature)
890 }
891 }
892
893 return count;
894}
912std::pair<double, double> Integrate3DEvents::numInEllipsoidBkg(
913 std::vector<std::pair<std::pair<double, double>, V3D>> const &events,
915 DataObjects::PeakEllipsoidExtent const &sizesIn, const bool useOnePercentBackgroundCorrection) {
916 std::pair<double, double> count(0, 0);
917 std::vector<std::pair<double, double>> eventVec;
918 for (const auto &event : events) {
919 double sum = 0;
920 double sumIn = 0;
921 for (size_t k = 0; k < 3; k++) {
922 double comp = event.second.scalar_prod(directions[k]) / sizes[k];
923 sum += comp * comp;
924 comp = event.second.scalar_prod(directions[k]) / sizesIn[k];
925 sumIn += comp * comp;
926 }
927 if (sum <= 1 && sumIn >= 1)
928 eventVec.emplace_back(event.first);
929 }
930
931 auto endIndex = eventVec.size();
932 if (useOnePercentBackgroundCorrection) {
933 // Remove top 1% of background
934 std::sort(eventVec.begin(), eventVec.end(),
935 [](const std::pair<double, double> &a, const std::pair<double, double> &b) { return a.first < b.first; });
936 endIndex = static_cast<size_t>(0.99 * static_cast<double>(endIndex));
937 }
938
939 for (size_t k = 0; k < endIndex; ++k) {
940 count.first += eventVec[k].first;
941 count.second += eventVec[k].second;
942 }
943
944 return count;
945}
974void Integrate3DEvents::makeCovarianceMatrix(std::vector<std::pair<std::pair<double, double>, V3D>> const &events,
975 DblMatrix &matrix, double radius) {
976 double totalCounts;
977 for (int row = 0; row < 3; row++) {
978 for (int col = 0; col < 3; col++) {
979 totalCounts = 0;
980 double sum = 0;
981 for (const auto &event : events) {
982 if (event.second.norm() <= radius) {
983 totalCounts += event.first.first;
984 sum += event.first.first * event.second[row] * event.second[col];
985 }
986 }
987 if (totalCounts > 1)
988 matrix[row][col] = sum / (totalCounts - 1);
989 else
990 matrix[row][col] = sum;
991 }
992 }
993}
994
1003void Integrate3DEvents::getEigenVectors(DblMatrix const &cov_matrix, std::array<V3D, 3> &eigen_vectors,
1004 std::array<double, 3> &eigen_values) {
1005 constexpr unsigned int size = 3;
1006
1007 gsl_matrix *matrix = gsl_matrix_alloc(size, size);
1008 gsl_vector *eigen_val = gsl_vector_alloc(size);
1009 gsl_matrix *eigen_vec = gsl_matrix_alloc(size, size);
1010 gsl_eigen_symmv_workspace *wkspace = gsl_eigen_symmv_alloc(size);
1011
1012 // copy the matrix data into the gsl matrix
1013 for (size_t row = 0; row < size; row++)
1014 for (size_t col = 0; col < size; col++) {
1015 gsl_matrix_set(matrix, row, col, cov_matrix[row][col]);
1016 }
1017
1018 gsl_eigen_symmv(matrix, eigen_val, eigen_vec, wkspace);
1019
1020 // copy the resulting eigen vectors to output vector
1021 for (size_t col = 0; col < size; col++) {
1022 eigen_vectors[col] =
1023 V3D(gsl_matrix_get(eigen_vec, 0, col), gsl_matrix_get(eigen_vec, 1, col), gsl_matrix_get(eigen_vec, 2, col));
1024 eigen_values[col] = gsl_vector_get(eigen_val, col);
1025 }
1026
1027 gsl_matrix_free(matrix);
1028 gsl_vector_free(eigen_val);
1029 gsl_matrix_free(eigen_vec);
1030 gsl_eigen_symmv_free(wkspace);
1031}
1032
1041int64_t Integrate3DEvents::getHklKey(int h, int k, int l) {
1042 int64_t key(0);
1043
1044 if (h != 0 || k != 0 || l != 0)
1045 key = 1000000000000 * h + 100000000 * k + 10000 * l;
1046
1047 return key;
1048}
1060int64_t Integrate3DEvents::getHklMnpKey(int h, int k, int l, int m, int n, int p) {
1061 int64_t key(0);
1062
1063 if (h != 0 || k != 0 || l != 0 || m != 0 || n != 0 || p != 0)
1064 key = 1000000000000 * h + 100000000 * k + 10000 * l + 100 * m + 10 * n + p;
1065
1066 return key;
1067}
1076 int h = boost::math::iround<double>(hkl[0]);
1077 int k = boost::math::iround<double>(hkl[1]);
1078 int l = boost::math::iround<double>(hkl[2]);
1079 return getHklKey(h, k, l);
1080}
1089 V3D modvec1 = V3D(m_ModHKL[0][0], m_ModHKL[1][0], m_ModHKL[2][0]);
1090 V3D modvec2 = V3D(m_ModHKL[0][1], m_ModHKL[1][1], m_ModHKL[2][1]);
1091 V3D modvec3 = V3D(m_ModHKL[0][2], m_ModHKL[1][2], m_ModHKL[2][2]);
1093 int h = boost::math::iround<double>(hkl[0]);
1094 int k = boost::math::iround<double>(hkl[1]);
1095 int l = boost::math::iround<double>(hkl[2]);
1096
1097 return getHklMnpKey(h, k, l, 0, 0, 0);
1098 } else if (!crossterm) {
1099 if (modvec1 != V3D(0, 0, 0))
1100 for (int order = -maxOrder; order <= maxOrder; order++) {
1101 if (order == 0)
1102 continue; // exclude order 0
1103 V3D hkl1(hkl);
1104 hkl1[0] -= order * modvec1[0];
1105 hkl1[1] -= order * modvec1[1];
1106 hkl1[2] -= order * modvec1[2];
1108 int h = boost::math::iround<double>(hkl1[0]);
1109 int k = boost::math::iround<double>(hkl1[1]);
1110 int l = boost::math::iround<double>(hkl1[2]);
1111 return getHklMnpKey(h, k, l, order, 0, 0);
1112 }
1113 }
1114 if (modvec2 != V3D(0, 0, 0))
1115 for (int order = -maxOrder; order <= maxOrder; order++) {
1116 if (order == 0)
1117 continue; // exclude order 0
1118 V3D hkl1(hkl);
1119 hkl1[0] -= order * modvec2[0];
1120 hkl1[1] -= order * modvec2[1];
1121 hkl1[2] -= order * modvec2[2];
1123 int h = boost::math::iround<double>(hkl1[0]);
1124 int k = boost::math::iround<double>(hkl1[1]);
1125 int l = boost::math::iround<double>(hkl1[2]);
1126 return getHklMnpKey(h, k, l, 0, order, 0);
1127 }
1128 }
1129 if (modvec3 != V3D(0, 0, 0))
1130 for (int order = -maxOrder; order <= maxOrder; order++) {
1131 if (order == 0)
1132 continue; // exclude order 0
1133 V3D hkl1(hkl);
1134 hkl1[0] -= order * modvec3[0];
1135 hkl1[1] -= order * modvec3[1];
1136 hkl1[2] -= order * modvec3[2];
1138 int h = boost::math::iround<double>(hkl1[0]);
1139 int k = boost::math::iround<double>(hkl1[1]);
1140 int l = boost::math::iround<double>(hkl1[2]);
1141 return getHklMnpKey(h, k, l, 0, 0, order);
1142 }
1143 }
1144 } else {
1145 int maxOrder1 = maxOrder;
1146 if (modvec1 == V3D(0, 0, 0))
1147 maxOrder1 = 0;
1148 int maxOrder2 = maxOrder;
1149 if (modvec2 == V3D(0, 0, 0))
1150 maxOrder2 = 0;
1151 int maxOrder3 = maxOrder;
1152 if (modvec3 == V3D(0, 0, 0))
1153 maxOrder3 = 0;
1154 for (int m = -maxOrder1; m <= maxOrder1; m++)
1155 for (int n = -maxOrder2; n <= maxOrder2; n++)
1156 for (int p = -maxOrder3; p <= maxOrder3; p++) {
1157 if (m == 0 && n == 0 && p == 0)
1158 continue; // exclude 0,0,0
1159 V3D hkl1(hkl);
1160 V3D mnp = V3D(m, n, p);
1161 hkl1 -= m_ModHKL * mnp;
1163 int h = boost::math::iround<double>(hkl1[0]);
1164 int k = boost::math::iround<double>(hkl1[1]);
1165 int l = boost::math::iround<double>(hkl1[2]);
1166 return getHklMnpKey(h, k, l, m, n, p);
1167 }
1168 }
1169 }
1170 return 0;
1171}
1179int64_t Integrate3DEvents::getHklKey(V3D const &q_vector) {
1180 V3D hkl = m_UBinv * q_vector;
1181 int h = boost::math::iround<double>(hkl[0]);
1182 int k = boost::math::iround<double>(hkl[1]);
1183 int l = boost::math::iround<double>(hkl[2]);
1184 return getHklKey(h, k, l);
1185}
1186
1195int64_t Integrate3DEvents::getHklMnpKey(V3D const &q_vector) {
1196 V3D hkl = m_UBinv * q_vector;
1197
1198 V3D modvec1 = V3D(m_ModHKL[0][0], m_ModHKL[1][0], m_ModHKL[2][0]);
1199 V3D modvec2 = V3D(m_ModHKL[0][1], m_ModHKL[1][1], m_ModHKL[2][1]);
1200 V3D modvec3 = V3D(m_ModHKL[0][2], m_ModHKL[1][2], m_ModHKL[2][2]);
1202 int h = boost::math::iround<double>(hkl[0]);
1203 int k = boost::math::iround<double>(hkl[1]);
1204 int l = boost::math::iround<double>(hkl[2]);
1205
1206 return getHklMnpKey(h, k, l, 0, 0, 0);
1207 } else if (!crossterm) {
1208 if (modvec1 != V3D(0, 0, 0))
1209 for (int order = -maxOrder; order <= maxOrder; order++) {
1210 if (order == 0)
1211 continue; // exclude order 0
1212 V3D hkl1(hkl);
1213 hkl1[0] -= order * modvec1[0];
1214 hkl1[1] -= order * modvec1[1];
1215 hkl1[2] -= order * modvec1[2];
1217 int h = boost::math::iround<double>(hkl1[0]);
1218 int k = boost::math::iround<double>(hkl1[1]);
1219 int l = boost::math::iround<double>(hkl1[2]);
1220 return getHklMnpKey(h, k, l, order, 0, 0);
1221 }
1222 }
1223 if (modvec2 != V3D(0, 0, 0))
1224 for (int order = -maxOrder; order <= maxOrder; order++) {
1225 if (order == 0)
1226 continue; // exclude order 0
1227 V3D hkl1(hkl);
1228 hkl1[0] -= order * modvec2[0];
1229 hkl1[1] -= order * modvec2[1];
1230 hkl1[2] -= order * modvec2[2];
1232 int h = boost::math::iround<double>(hkl1[0]);
1233 int k = boost::math::iround<double>(hkl1[1]);
1234 int l = boost::math::iround<double>(hkl1[2]);
1235 return getHklMnpKey(h, k, l, 0, order, 0);
1236 }
1237 }
1238 if (modvec3 != V3D(0, 0, 0))
1239 for (int order = -maxOrder; order <= maxOrder; order++) {
1240 if (order == 0)
1241 continue; // exclude order 0
1242 V3D hkl1(hkl);
1243 hkl1[0] -= order * modvec3[0];
1244 hkl1[1] -= order * modvec3[1];
1245 hkl1[2] -= order * modvec3[2];
1247 int h = boost::math::iround<double>(hkl1[0]);
1248 int k = boost::math::iround<double>(hkl1[1]);
1249 int l = boost::math::iround<double>(hkl1[2]);
1250 return getHklMnpKey(h, k, l, 0, 0, order);
1251 }
1252 }
1253 } else {
1254 int maxOrder1 = maxOrder;
1255 if (modvec1 == V3D(0, 0, 0))
1256 maxOrder1 = 0;
1257 int maxOrder2 = maxOrder;
1258 if (modvec2 == V3D(0, 0, 0))
1259 maxOrder2 = 0;
1260 int maxOrder3 = maxOrder;
1261 if (modvec3 == V3D(0, 0, 0))
1262 maxOrder3 = 0;
1263 for (int m = -maxOrder1; m <= maxOrder1; m++)
1264 for (int n = -maxOrder2; n <= maxOrder2; n++)
1265 for (int p = -maxOrder3; p <= maxOrder3; p++) {
1266 if (m == 0 && n == 0 && p == 0)
1267 continue; // exclude 0,0,0
1268 V3D hkl1(hkl);
1269 V3D mnp = V3D(m, n, p);
1270 hkl1 -= m_ModHKL * mnp;
1272 int h = boost::math::iround<double>(hkl1[0]);
1273 int k = boost::math::iround<double>(hkl1[1]);
1274 int l = boost::math::iround<double>(hkl1[2]);
1275 return getHklMnpKey(h, k, l, m, n, p);
1276 }
1277 }
1278 }
1279 return 0;
1280}
1281
1296void Integrate3DEvents::addEvent(std::pair<std::pair<double, double>, V3D> event_Q, bool hkl_integ) {
1297 int64_t hkl_key;
1298 if (hkl_integ)
1299 hkl_key = getHklKey2(event_Q.second);
1300 else
1301 hkl_key = getHklKey(event_Q.second);
1302
1303 if (hkl_key == 0) // don't keep events associated with 0,0,0
1304 return;
1305
1306 auto peak_it = m_peak_qs.find(hkl_key);
1307 if (peak_it != m_peak_qs.end()) {
1308 if (!peak_it->second.nullVector()) {
1309 if (hkl_integ)
1310 event_Q.second = event_Q.second - m_UBinv * peak_it->second;
1311 else
1312 event_Q.second = event_Q.second - peak_it->second;
1313 if (event_Q.second.norm() < m_radius) {
1314 m_event_lists[hkl_key].emplace_back(event_Q);
1315 }
1316 }
1317 }
1318}
1319
1334void Integrate3DEvents::addModEvent(std::pair<std::pair<double, double>, V3D> event_Q, bool hkl_integ) {
1335 int64_t hklmnp_key;
1336
1337 if (hkl_integ)
1338 hklmnp_key = getHklMnpKey2(event_Q.second);
1339 else
1340 hklmnp_key = getHklMnpKey(event_Q.second);
1341
1342 if (hklmnp_key == 0) // don't keep events associated with 0,0,0
1343 return;
1344
1345 auto peak_it = m_peak_qs.find(hklmnp_key);
1346 if (peak_it != m_peak_qs.end()) {
1347 if (!peak_it->second.nullVector()) {
1348 if (hkl_integ)
1349 event_Q.second = event_Q.second - m_UBinv * peak_it->second;
1350 else
1351 event_Q.second = event_Q.second - peak_it->second;
1352
1353 if (hklmnp_key % 10000 == 0) {
1354 if (event_Q.second.norm() < m_radius)
1355 m_event_lists[hklmnp_key].emplace_back(event_Q);
1356 } else if (event_Q.second.norm() < s_radius) {
1357 m_event_lists[hklmnp_key].emplace_back(event_Q);
1358 }
1359 }
1360 }
1361}
1362
1401Integrate3DEvents::ellipseIntegrateEvents(const std::vector<Kernel::V3D> &E1Vec, Kernel::V3D const &peak_q,
1402 std::vector<std::pair<std::pair<double, double>, Kernel::V3D>> const &ev_list,
1403 DataObjects::PeakEllipsoidFrame const &directions,
1404 std::array<double, 3> const &sigmas, bool specify_size, double peak_radius,
1405 double back_inner_radius, double back_outer_radius,
1406 DataObjects::PeakEllipsoidExtent &axes_radii, double &inti, double &sigi) {
1407 // r1, r2 and r3 will give the sizes of the major axis of
1408 // the peak ellipsoid, and of the inner and outer surface
1409 // of the background ellipsoidal shell, respectively.
1410 // They will specify the size as the number of standard
1411 // deviations in the direction of each of the pricipal
1412 // axes that the ellipsoid will extend from the center.
1413 double r1, r2, r3;
1414
1415 double max_sigma = sigmas[0];
1416 for (int i = 1; i < 3; i++) {
1417 if (sigmas[i] > max_sigma) {
1418 max_sigma = sigmas[i];
1419 }
1420 }
1421
1422 if (specify_size) {
1423 r1 = peak_radius / max_sigma; // scale specified sizes by 1/max_sigma so when multiplied by
1424 r2 = back_inner_radius / max_sigma; // the individual sigmas in different directions, the major
1425 r3 = back_outer_radius / max_sigma; // axis has the specified size
1426 } else {
1427 r1 = 3;
1428 r2 = 3;
1429 r3 = r2 * 1.25992105; // A factor of 2 ^ (1/3) will make the background
1430 // shell volume equal to the peak region volume.
1431
1432 // if necessary restrict the background ellipsoid
1433 // to lie within the specified sphere, and adjust
1434 // the other sizes, proportionally
1435 if (r3 * max_sigma > m_radius) {
1436 r3 = m_radius / max_sigma;
1437 r1 = r3 * 0.79370053f; // This value for r1 and r2 makes the background
1438 r2 = r1; // shell volume equal to the peak region volume.
1439 }
1440 }
1441
1442 DataObjects::PeakEllipsoidExtent abcBackgroundOuterRadii;
1443 DataObjects::PeakEllipsoidExtent abcBackgroundInnerRadii;
1445 for (int i = 0; i < 3; i++) {
1446 abcBackgroundOuterRadii[i] = r3 * sigmas[i];
1447 abcBackgroundInnerRadii[i] = r2 * sigmas[i];
1448 abcRadii[i] = r1 * sigmas[i];
1449 axes_radii[i] = r1 * sigmas[i];
1450 }
1451
1452 if (!E1Vec.empty()) {
1453 double h3 = 1.0 - detectorQ(E1Vec, peak_q, abcBackgroundOuterRadii);
1454 // scaled from area of circle minus segment when r normalized to 1
1455 double m3 = std::sqrt(1.0 - (std::acos(1.0 - h3) - (1.0 - h3) * std::sqrt(2.0 * h3 - h3 * h3)) / M_PI);
1456 double h1 = 1.0 - detectorQ(E1Vec, peak_q, axes_radii);
1457 // Do not use peak if edge of detector is inside integration radius
1458 if (h1 > 0.0)
1459 return std::make_shared<const PeakShapeEllipsoid>(directions, abcRadii, abcBackgroundInnerRadii,
1460 abcBackgroundOuterRadii, Mantid::Kernel::QLab,
1461 "IntegrateEllipsoids");
1462 r3 *= m3;
1463 if (r2 != r1) {
1464 double h2 = 1.0 - detectorQ(E1Vec, peak_q, abcBackgroundInnerRadii);
1465 // scaled from area of circle minus segment when r normalized to 1
1466 double m2 = std::sqrt(1.0 - (std::acos(1.0 - h2) - (1.0 - h2) * std::sqrt(2.0 * h2 - h2 * h2)) / M_PI);
1467 r2 *= m2;
1468 }
1469 }
1470
1471 std::pair<double, double> backgrd = numInEllipsoidBkg(ev_list, directions, abcBackgroundOuterRadii,
1472 abcBackgroundInnerRadii, m_useOnePercentBackgroundCorrection);
1473
1474 std::pair<double, double> peak_w_back = numInEllipsoid(ev_list, directions, axes_radii);
1475
1476 double ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
1477
1478 inti = peak_w_back.first - ratio * backgrd.first;
1479 sigi = sqrt(peak_w_back.second + ratio * ratio * backgrd.second);
1480
1481 // Make the shape and return it.
1482 return std::make_shared<const PeakShapeEllipsoid>(directions, abcRadii, abcBackgroundInnerRadii,
1483 abcBackgroundOuterRadii, Mantid::Kernel::QLab,
1484 "IntegrateEllipsoids");
1485}
1497double Integrate3DEvents::detectorQ(const std::vector<V3D> &E1Vec, const V3D &QLabFrame,
1499 double quot = 1.0;
1500 for (const auto &E1 : E1Vec) {
1501 V3D distv = QLabFrame - E1 * (QLabFrame.scalar_prod(E1)); // distance to the trajectory as a vector
1502 double quot0 = distv.norm() / *(std::min_element(r.begin(), r.end()));
1503 if (quot0 < quot) {
1504 quot = quot0;
1505 }
1506 }
1507 return quot;
1508}
1509
1517std::tuple<double, double, double> Integrate3DEvents::calculateRadiusFactors(const IntegrationParameters &params,
1518 double max_sigma) const {
1519 double r1 = 0, r2 = 0, r3 = 0;
1520
1521 if (!params.specifySize) {
1522 r1 = 3;
1523 r2 = 3;
1524 r3 = r2 * 1.25992105; // A factor of 2 ^ (1/3) will make the background
1525 // shell volume equal to the peak region volume.
1526
1527 // if necessary restrict the background ellipsoid
1528 // to lie within the specified sphere, and adjust
1529 // the other sizes, proportionally
1530 if (r3 * max_sigma > params.regionRadius) {
1531 r3 = params.regionRadius / max_sigma;
1532 r1 = r3 * 0.79370053f; // This value for r1 and r2 makes the background
1533 r2 = r1; // shell volume equal to the peak region volume.
1534 }
1535 } else {
1536 // scale specified sizes by 1/max_sigma
1537 // so when multiplied by the individual
1538 // sigmas in different directions, the
1539 r1 = params.peakRadius / max_sigma;
1540 r2 = params.backgroundInnerRadius / max_sigma;
1541 r3 = params.backgroundOuterRadius / max_sigma;
1542 }
1543
1544 return std::make_tuple(r1, r2, r3);
1545}
1546
1547} // namespace Mantid::MDAlgorithms
const int maxOrder
int count
counter
Definition Matrix.cpp:37
PeakShapeEllipsoid : PeakShape representing a 3D ellipsoid.
const Kernel::V3D & translation() const
Get translation of center.
const PeakEllipsoidFrame & directions() const
Get ellipsoid directions.
const PeakEllipsoidExtent & abcRadii() const
Get radii.
const PeakEllipsoidExtent & abcRadiiBackgroundInner() const
Get background inner radii.
const PeakEllipsoidExtent & abcRadiiBackgroundOuter() const
Get background outer radii.
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:280
double norm() const noexcept
Definition V3D.h:269
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.
double detectorQ(const std::vector< Kernel::V3D > &E1Vec, const Mantid::Kernel::V3D &QLabFrame, const DataObjects::PeakEllipsoidExtent &r)
Compute if a particular Q falls on the edge of a detector.
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.
bool correctForDetectorEdges(std::tuple< double, double, double > &radii, const std::vector< Mantid::Kernel::V3D > &E1Vecs, const Mantid::Kernel::V3D &peak_q, const DataObjects::PeakEllipsoidExtent &axesRadii, const DataObjects::PeakEllipsoidExtent &bkgInnerRadii, const DataObjects::PeakEllipsoidExtent &bkgOuterRadii)
Correct integration radii if peak extends to detector edges and return false if peak center is off de...
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.
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 strong peak, using ellipsoidal volumes fit from the events,...
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.
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, DataObjects::PeakEllipsoidExtent &axes_radii, double &inti, double &sigi)
Find the net integrated intensity of a peak, using ellipsoidal volumes.
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 ...
void integrateUsingShapeProfileFit(const Mantid::DataObjects::PeakShapeEllipsoid &shape, const Mantid::Kernel::V3D &peak_q, bool adjustCenter, Mantid::Kernel::V3D &center, double &inti, double &sigi)
Integrate a peak using a shape supplied by the caller, by maximizing the Poisson log-likelihood of a ...
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, DataObjects::PeakEllipsoidExtent &axes_radii, double &inti, double &sigi)
Find the net integrated intensity of a modulated peak, using ellipsoidal volumes.
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)
Integrate a weak peak using a shape from a library peak, scaling the shape by the fractional intensit...
void integrateUsingShape(const Mantid::DataObjects::PeakShapeEllipsoid &shape, const Mantid::Kernel::V3D &peak_q, double &inti, double &sigi)
Integrate a peak using a shape supplied by the caller (e.g.
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)
Estimate the signal-to-noise ratio for a peak at the given position by fitting an ellipsoid to the ev...
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.
static std::pair< double, double > numInEllipsoidBkg(std::vector< std::pair< std::pair< double, double >, Mantid::Kernel::V3D > > const &events, DataObjects::PeakEllipsoidFrame const &directions, DataObjects::PeakEllipsoidExtent const &sizes, DataObjects::PeakEllipsoidExtent const &sizesIn, const bool useOnePercentBackgroundCorrection)
Calculate the number of events in an ellipsoid centered at 0,0,0.
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.
static void getEigenVectors(Kernel::DblMatrix const &cov_matrix, std::array< Mantid::Kernel::V3D, 3 > &eigen_vectors, DataObjects::PeakEllipsoidExtent &eigen_values)
Calculate the eigen vectors of a 3x3 real symmetric matrix.
static std::pair< double, double > numInEllipsoid(std::vector< std::pair< std::pair< double, double >, Mantid::Kernel::V3D > > const &events, DataObjects::PeakEllipsoidFrame const &directions, DataObjects::PeakEllipsoidExtent const &sizes)
Calculate the number of events in an ellipsoid centered at 0,0,0.
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::array< Kernel::V3D, PEAK_ELLIPSOID_DIMS > PeakEllipsoidFrame
std::array< double, PEAK_ELLIPSOID_DIMS > PeakEllipsoidExtent
std::shared_ptr< const PeakShapeEllipsoid > PeakShapeEllipsoid_const_sptr
std::shared_ptr< const PeakShape > PeakShape_const_sptr
Definition PeakShape.h:43
STL namespace.
Parameters for controlling peak integration.
double backgroundOuterRadius
Outer radius of background shell.
double peakRadius
Radius of peak ellipsoid.
bool specifySize
If true, use specified sizes; if false, use data-driven sizes.
double backgroundInnerRadius
Inner radius of background shell.
std::vector< Kernel::V3D > E1Vectors
Vectors for calculating detector edges.
double regionRadius
Radius of region to search for events.