13#include <boost/math/special_functions/round.hpp>
25#include <gsl/gsl_eigen.h>
26#include <gsl/gsl_matrix.h>
27#include <gsl/gsl_vector.h>
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);
58 m_peak_qs[hkl_key] = peak_q_list[it].second;
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) {
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]));
97 m_peak_qs[hklmnp_key] = peak_q_list[it].second;
121 for (
const auto &event_q : event_qs)
124 for (
const auto &event_q : event_qs)
128std::pair<std::shared_ptr<const Geometry::PeakShape>, std::tuple<double, double, double>>
136 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
138 const auto &events = *result;
140 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
145 std::array<V3D, 3> eigen_vectors;
146 std::array<double, 3> eigen_values;
149 std::array<double, 3> sigmas;
150 for (
int i = 0; i < 3; i++) {
151 sigmas[i] = sqrt(eigen_values[i]);
155 std::any_of(sigmas.cbegin(), sigmas.cend(), [](
const double sigma) { return std::isnan(sigma) || sigma <= 0; });
158 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
160 const auto max_sigma = *std::max_element(sigmas.begin(), sigmas.end());
162 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0., 0., 0.));
165 auto const &r1 = std::get<0>(rValues), r2 = std::get<1>(rValues), r3 = std::get<2>(rValues);
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];
176 abcBackgroundInnerRadii, abcBackgroundOuterRadii);
178 if (!isPeakOnDetector)
179 return std::make_pair(std::make_shared<NoShape>(), make_tuple(0.0, 0.0, 0.));
181 const auto backgrd =
numInEllipsoidBkg(events, eigen_vectors, abcBackgroundOuterRadii, abcBackgroundInnerRadii,
184 const auto peak =
numInEllipsoid(events, eigen_vectors, peakRadii);
185 const auto ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
187 inti = peak.first - ratio * backgrd.first;
188 sigi = sqrt(peak.second + ratio * ratio * backgrd.second);
191 const auto total = (core.first + peak.first) - ratio * backgrd.first;
192 const auto frac = std::min(1.0, std::abs(inti / total));
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));
199 const auto shape = std::make_shared<const PeakShapeEllipsoid>(eigen_vectors, peakRadii, abcBackgroundInnerRadii,
201 "IntegrateEllipsoidsTwoStep");
203 return std::make_pair(shape, std::make_tuple(frac, fracError, max_sigma));
220std::shared_ptr<const Geometry::PeakShape>
222 const std::tuple<double, double, double> &libPeak,
const V3D &peak_q,
double &inti,
230 return std::make_shared<NoShape>();
232 const auto &events = *result;
234 const auto &directions = shape->directions();
235 auto abcBackgroundInnerRadii = shape->abcRadiiBackgroundInner();
236 auto abcBackgroundOuterRadii = shape->abcRadiiBackgroundOuter();
237 auto abcRadii = shape->abcRadii();
239 const auto max_sigma = std::get<2>(libPeak);
243 abcBackgroundInnerRadii, abcBackgroundOuterRadii);
245 if (!isPeakOnDetector)
248 const double r1 = std::get<0>(rValues), r2 = std::get<1>(rValues), r3 = std::get<2>(rValues);
251 std::pair<double, double> backgrd =
numInEllipsoidBkg(events, directions, abcBackgroundOuterRadii,
253 std::pair<double, double> peak_w_back =
numInEllipsoid(events, directions, abcRadii);
254 double ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
256 const auto frac = std::get<0>(libPeak);
257 const auto fracError = std::get<1>(libPeak);
259 inti = peak_w_back.first - ratio * backgrd.first;
262 sigi = sigi / pow(inti, 2);
263 sigi += pow((fracError / frac), 2);
266 sigi = sqrt(sigi) * inti;
269 for (
size_t i = 0; i < abcRadii.size(); ++i) {
271 abcBackgroundInnerRadii[i] *= frac;
272 abcBackgroundOuterRadii[i] *= frac;
275 return std::make_shared<const PeakShapeEllipsoid>(shape->directions(), abcRadii, abcBackgroundInnerRadii,
277 "IntegrateEllipsoidsTwoStep");
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();
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));
309 auto logLikelihoodGradHess = [&](
double a,
double bb,
double &dA,
double &dB,
double &dAA,
double &dBB,
double &dAB) {
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;
320 dAA -= wi * g[i] * g[i] / (
mu *
mu);
321 dBB -= wi / (
mu *
mu);
322 dAB -= wi * g[i] / (
mu *
mu);
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;
335 for (
int iter = 0; iter < 100; ++iter) {
336 double dA, dB, dAA, dBB, dAB;
337 logLikelihoodGradHess(A, b, dA, dB, dAA, dBB, dAB);
339 const auto det = dAA * dBB - dAB * dAB;
340 if (std::abs(det) < 1e-300)
343 const auto deltaA = -(dBB * dA - dAB * dB) / det;
344 const auto deltaB = -(-dAB * dA + dAA * dB) / det;
349 const auto llOld = logLikelihood(A, b);
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);
366 const auto converged = std::abs(newA - A) < 1e-12 * (1.0 + A) && std::abs(newB - b) < 1e-12 * (1.0 + b);
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);
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)
400 for (
int col = 0; col < 3; ++col) {
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;
434 const auto &events = *result;
437 const auto &abcRadii = shape.
abcRadii();
443 const std::pair<double, double> peak =
numInEllipsoid(events, directions, abcRadii);
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;
452 inti = peak.first - ratio * backgrd.first;
453 sigi = sqrt(peak.second + ratio * ratio * backgrd.second);
474 bool adjustCenter,
V3D ¢er,
double &inti,
double &sigi) {
480 if (!result || result->empty())
483 const auto &events = *result;
486 const auto &sigmas = shape.
abcRadii();
489 auto applyInvS = [&](
const V3D &v) {
491 for (
size_t k = 0; k < 3; ++k)
492 out += directions[k] * (v.scalar_prod(directions[k]) / (sigmas[k] * sigmas[k]));
498 auto computeKernel = [&](
const V3D &c, std::vector<double> &g, std::vector<double> &w, std::vector<V3D> &invSq) {
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);
509 w.push_back(event.first.first);
516 const auto normG = pow(2.0 * M_PI, 1.5) * sigmas[0] * sigmas[1] * sigmas[2];
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));
529 std::vector<double> g, w;
530 std::vector<V3D> invSq;
531 computeKernel(center, g, w, invSq);
533 double A = 0.0, b = 0.0, sigA = 0.0;
534 solvePoissonMatchedFilter(g, w, normG, volume, A, b, sigA);
537 const auto maxShift = *std::min_element(sigmas.begin(), sigmas.end());
542 const V3D seedCenter = center;
544 for (
int outer = 0; outer < 20; ++outer) {
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];
561 if (!solve3x3(hess, grad, deltaC))
564 V3D step(deltaC[0], deltaC[1], deltaC[2]);
565 if (step.
norm() > 0.25 * maxShift)
566 step *= (0.25 * maxShift / step.
norm());
568 auto clampToMaxShift = [&](
V3D c) {
569 const V3D offsetFromSeed = c - seedCenter;
570 if (offsetFromSeed.
norm() > maxShift)
571 return seedCenter + offsetFromSeed * (maxShift / offsetFromSeed.
norm());
575 const auto llOld = logLikelihood(center, A, b);
576 V3D newCenter = clampToMaxShift(center + step);
577 auto llNew = logLikelihood(newCenter, A, b);
579 double localStep = 1.0;
580 while (llNew < llOld && localStep > 1e-4) {
582 newCenter = clampToMaxShift(center + step * localStep);
583 llNew = logLikelihood(newCenter, A, b);
586 const auto converged = (newCenter - center).norm() < 1e-8 * (1.0 + maxShift);
589 computeKernel(center, g, w, invSq);
590 solvePoissonMatchedFilter(g, w, normG, volume, A, b, sigA);
613 bool forceSpherical,
double sphericityTol) {
619 const auto &events = *result;
626 std::array<V3D, 3> eigen_vectors;
627 std::array<double, 3> eigen_values;
630 std::array<double, 3> sigmas;
631 for (
int i = 0; i < 3; i++) {
632 sigmas[i] = sqrt(eigen_values[i]);
635 const auto max_sigma = *std::max_element(sigmas.begin(), sigmas.end());
636 const auto min_sigma = *std::min_element(sigmas.begin(), sigmas.end());
641 auto const &r1 = std::get<0>(rValues), r2 = std::get<1>(rValues), r3 = std::get<2>(rValues);
644 if (forceSpherical) {
646 if ((max_sigma - min_sigma) / max_sigma > sphericityTol)
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;
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];
662 std::pair<double, double> backgrd =
numInEllipsoidBkg(events, eigen_vectors, abcBackgroundOuterRadii,
665 std::pair<double, double> peak_w_back =
numInEllipsoid(events, eigen_vectors, peakRadii);
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);
687 if (pos->second.size() < 3)
690 return &(pos->second);
694 const std::vector<V3D> &E1Vecs,
const V3D &peak_q,
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);
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);
715 auto h2 = 1.0 -
detectorQ(E1Vecs, peak_q, bkgInnerRadii);
717 auto m2 = std::sqrt(1.0 - (std::acos(1.0 - h2) - (1.0 - h2) * std::sqrt(2.0 * h2 - h2 * h2)) / M_PI);
762 double peak_radius,
double back_inner_radius,
double back_outer_radius,
770 return std::make_shared<NoShape>();
775 return std::make_shared<NoShape>();
778 const std::vector<std::pair<std::pair<double, double>,
V3D>> &some_events = pos->second;
780 if (some_events.size() < 3)
782 return std::make_shared<NoShape>();
788 std::array<V3D, 3> eigen_vectors;
789 std::array<double, 3> eigen_values;
792 std::array<double, 3> sigmas;
793 for (
int i = 0; i < 3; i++) {
794 sigmas[i] = sqrt(eigen_values[i]);
798 std::any_of(sigmas.cbegin(), sigmas.cend(), [](
const double sigma) { return std::isnan(sigma) || sigma <= 0; });
802 return std::make_shared<NoShape>();
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);
811 V3D const &mnp,
bool specify_size,
double peak_radius,
812 double back_inner_radius,
double back_outer_radius,
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]));
822 return std::make_shared<NoShape>();
827 return std::make_shared<NoShape>();
830 const std::vector<std::pair<std::pair<double, double>,
V3D>> &some_events = pos->second;
832 if (some_events.size() < 3)
834 return std::make_shared<NoShape>();
838 if (hkl_key % 1000 == 0)
843 std::array<V3D, 3> eigen_vectors;
844 std::array<double, 3> eigen_values;
847 std::array<double, 3> sigmas;
848 for (
int i = 0; i < 3; i++)
849 sigmas[i] = sqrt(eigen_values[i]);
852 std::any_of(sigmas.cbegin(), sigmas.cend(), [](
const double sigma) { return std::isnan(sigma) || sigma <= 0; });
856 return std::make_shared<NoShape>();
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);
875std::pair<double, double>
880 std::pair<double, double>
count(0, 0);
881 for (
const auto &event : events) {
883 for (
size_t k = 0; k < 3; k++) {
884 double comp =
event.second.scalar_prod(directions[k]) / sizes[k];
888 count.first +=
event.first.first;
889 count.second +=
event.first.second;
913 std::vector<std::pair<std::pair<double, double>,
V3D>>
const &events,
916 std::pair<double, double>
count(0, 0);
917 std::vector<std::pair<double, double>> eventVec;
918 for (
const auto &event : events) {
921 for (
size_t k = 0; k < 3; k++) {
922 double comp =
event.second.scalar_prod(directions[k]) / sizes[k];
924 comp =
event.second.scalar_prod(directions[k]) / sizesIn[k];
925 sumIn += comp * comp;
927 if (sum <= 1 && sumIn >= 1)
928 eventVec.emplace_back(event.first);
931 auto endIndex = eventVec.size();
932 if (useOnePercentBackgroundCorrection) {
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));
939 for (
size_t k = 0; k < endIndex; ++k) {
940 count.first += eventVec[k].first;
941 count.second += eventVec[k].second;
977 for (
int row = 0; row < 3; row++) {
978 for (
int col = 0; col < 3; col++) {
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];
988 matrix[row][col] = sum / (totalCounts - 1);
990 matrix[row][col] = sum;
1004 std::array<double, 3> &eigen_values) {
1005 constexpr unsigned int size = 3;
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);
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]);
1018 gsl_eigen_symmv(matrix, eigen_val, eigen_vec, wkspace);
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);
1027 gsl_matrix_free(matrix);
1028 gsl_vector_free(eigen_val);
1029 gsl_matrix_free(eigen_vec);
1030 gsl_eigen_symmv_free(wkspace);
1044 if (h != 0 || k != 0 || l != 0)
1045 key = 1000000000000 * h + 100000000 * k + 10000 * l;
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;
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]);
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]);
1099 if (modvec1 !=
V3D(0, 0, 0))
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]);
1114 if (modvec2 !=
V3D(0, 0, 0))
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]);
1129 if (modvec3 !=
V3D(0, 0, 0))
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]);
1146 if (modvec1 ==
V3D(0, 0, 0))
1149 if (modvec2 ==
V3D(0, 0, 0))
1152 if (modvec3 ==
V3D(0, 0, 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)
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]);
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]);
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]);
1208 if (modvec1 !=
V3D(0, 0, 0))
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]);
1223 if (modvec2 !=
V3D(0, 0, 0))
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]);
1238 if (modvec3 !=
V3D(0, 0, 0))
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]);
1255 if (modvec1 ==
V3D(0, 0, 0))
1258 if (modvec2 ==
V3D(0, 0, 0))
1261 if (modvec3 ==
V3D(0, 0, 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)
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]);
1308 if (!peak_it->second.nullVector()) {
1310 event_Q.second = event_Q.second -
m_UBinv * peak_it->second;
1312 event_Q.second = event_Q.second - peak_it->second;
1313 if (event_Q.second.norm() <
m_radius) {
1342 if (hklmnp_key == 0)
1345 auto peak_it =
m_peak_qs.find(hklmnp_key);
1347 if (!peak_it->second.nullVector()) {
1349 event_Q.second = event_Q.second -
m_UBinv * peak_it->second;
1351 event_Q.second = event_Q.second - peak_it->second;
1353 if (hklmnp_key % 10000 == 0) {
1354 if (event_Q.second.norm() <
m_radius)
1356 }
else if (event_Q.second.norm() <
s_radius) {
1402 std::vector<std::pair<std::pair<double, double>,
Kernel::V3D>>
const &ev_list,
1404 std::array<double, 3>
const &sigmas,
bool specify_size,
double peak_radius,
1405 double back_inner_radius,
double back_outer_radius,
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];
1423 r1 = peak_radius / max_sigma;
1424 r2 = back_inner_radius / max_sigma;
1425 r3 = back_outer_radius / max_sigma;
1429 r3 = r2 * 1.25992105;
1437 r1 = r3 * 0.79370053f;
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];
1452 if (!E1Vec.empty()) {
1453 double h3 = 1.0 -
detectorQ(E1Vec, peak_q, abcBackgroundOuterRadii);
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);
1459 return std::make_shared<const PeakShapeEllipsoid>(directions, abcRadii, abcBackgroundInnerRadii,
1461 "IntegrateEllipsoids");
1464 double h2 = 1.0 -
detectorQ(E1Vec, peak_q, abcBackgroundInnerRadii);
1466 double m2 = std::sqrt(1.0 - (std::acos(1.0 - h2) - (1.0 - h2) * std::sqrt(2.0 * h2 - h2 * h2)) / M_PI);
1471 std::pair<double, double> backgrd =
numInEllipsoidBkg(ev_list, directions, abcBackgroundOuterRadii,
1474 std::pair<double, double> peak_w_back =
numInEllipsoid(ev_list, directions, axes_radii);
1476 double ratio = pow(r1, 3) / (pow(r3, 3) - pow(r2, 3));
1478 inti = peak_w_back.first - ratio * backgrd.first;
1479 sigi = sqrt(peak_w_back.second + ratio * ratio * backgrd.second);
1482 return std::make_shared<const PeakShapeEllipsoid>(directions, abcRadii, abcBackgroundInnerRadii,
1484 "IntegrateEllipsoids");
1500 for (
const auto &
E1 : E1Vec) {
1502 double quot0 = distv.
norm() / *(std::min_element(r.begin(), r.end()));
1518 double max_sigma)
const {
1519 double r1 = 0, r2 = 0, r3 = 0;
1524 r3 = r2 * 1.25992105;
1532 r1 = r3 * 0.79370053f;
1544 return std::make_tuple(r1, r2, r3);
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.
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
double norm() const noexcept
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.
Kernel::DblMatrix m_ModHKL
Kernel::DblMatrix m_UBinv
EventListMap m_event_lists
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 ¶ms, 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 ¢er, 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 ¶ms, 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 ¶ms, double max_sigma) const
Calculate the radius to use for each axis of the ellipsoid from the parameters provided.
double estimateSignalToNoiseRatio(const IntegrationParameters ¶ms, const Mantid::Kernel::V3D ¢er, 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.
const bool m_useOnePercentBackgroundCorrection
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
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.