327 throw std::invalid_argument(
"For now, we expect the input MDEventWorkspace "
328 "to have 3 dimensions only.");
335 if (peakWS != inPeakWS)
336 peakWS = inPeakWS->clone();
348 g_log.
error(
"Can't execute MaskBTP algorithm for this instrument to set "
349 "edge for IntegrateIfOnEdge option");
356 std::vector<double> PeakRadius =
getProperty(
"PeakRadius");
358 std::vector<double> BackgroundOuterRadius =
getProperty(
"BackgroundOuterRadius");
360 std::vector<double> BackgroundInnerRadius =
getProperty(
"BackgroundInnerRadius");
362 bool useOnePercentBackgroundCorrection =
getProperty(
"UseOnePercentBackgroundCorrection");
364 bool manualEllip =
false;
365 if (PeakRadius.size() > 1) {
368 if (BackgroundInnerRadius.size() == 1)
369 BackgroundInnerRadius.resize(3, BackgroundInnerRadius[0]);
370 if (BackgroundOuterRadius.size() == 1)
371 BackgroundOuterRadius.resize(3, BackgroundOuterRadius[0]);
374 double minInnerRadius = PeakRadius[0];
375 for (
size_t r = 0; r < BackgroundInnerRadius.size(); r++) {
377 minInnerRadius = PeakRadius[r];
379 if (BackgroundInnerRadius[r] < minInnerRadius)
380 BackgroundInnerRadius[r] = minInnerRadius;
385 bool majorAxisLengthFixed =
getProperty(
"FixMajorAxisLength");
389 double cylinderLength =
getProperty(
"CylinderLength");
393 bool adaptiveQBackground =
getProperty(
"AdaptiveQBackground");
394 double adaptiveQMultiplier =
getProperty(
"AdaptiveQMultiplier");
395 double adaptiveQBackgroundMultiplier = 0.0;
396 if (adaptiveQBackground)
397 adaptiveQBackgroundMultiplier = adaptiveQMultiplier;
398 std::vector<double> PeakRadiusVector(peakWS->getNumberPeaks(), PeakRadius[0]);
399 std::vector<double> BackgroundInnerRadiusVector(peakWS->getNumberPeaks(), BackgroundInnerRadius[0]);
400 std::vector<double> BackgroundOuterRadiusVector(peakWS->getNumberPeaks(), BackgroundOuterRadius[0]);
403 size_t histogramNumber = peakWS->getNumberPeaks();
405 wsProfile2D = std::dynamic_pointer_cast<Workspace2D>(wsProfile);
406 AnalysisDataService::Instance().addOrReplace(
"ProfilesData", wsProfile2D);
408 wsFit2D = std::dynamic_pointer_cast<Workspace2D>(wsFit);
409 AnalysisDataService::Instance().addOrReplace(
"ProfilesFit", wsFit2D);
411 wsDiff2D = std::dynamic_pointer_cast<Workspace2D>(wsDiff);
412 AnalysisDataService::Instance().addOrReplace(
"ProfilesFitDiff", wsDiff2D);
413 auto newAxis1 = std::make_unique<TextAxis>(peakWS->getNumberPeaks());
414 auto newAxis2 = std::make_unique<TextAxis>(peakWS->getNumberPeaks());
415 auto newAxis3 = std::make_unique<TextAxis>(peakWS->getNumberPeaks());
416 auto newAxis1Raw = newAxis1.get();
417 auto newAxis2Raw = newAxis2.get();
418 auto newAxis3Raw = newAxis3.get();
419 wsProfile2D->replaceAxis(1, std::move(newAxis1));
420 wsFit2D->replaceAxis(1, std::move(newAxis2));
421 wsDiff2D->replaceAxis(1, std::move(newAxis3));
422 for (
int i = 0; i < peakWS->getNumberPeaks(); ++i) {
424 IPeak &p = peakWS->getPeak(i);
425 std::ostringstream label;
428 newAxis1Raw->setLabel(i, label.str());
429 newAxis2Raw->setLabel(i, label.str());
430 newAxis3Raw->setLabel(i, label.str());
433 double percentBackground =
getProperty(
"PercentBackground");
435 size_t peakMax = numSteps;
438 peakMin =
static_cast<size_t>(
static_cast<double>(numSteps) * percentBackground / 100.);
439 peakMax = numSteps - peakMin - 1;
440 size_t numPeakCh = peakMax - peakMin + 1;
441 size_t numBkgCh = numSteps - numPeakCh;
442 ratio =
static_cast<double>(numPeakCh) /
static_cast<double>(numBkgCh);
445 bool replaceIntensity =
getProperty(
"ReplaceIntensity");
446 bool integrateEdge =
getProperty(
"IntegrateIfOnEdge");
449 std::string profileFunction =
getProperty(
"ProfileFunction");
450 std::string integrationOption =
getProperty(
"IntegrationOption");
452 if (cylinderBool && profileFunction !=
"NoFit") {
453 std::string outFile =
getProperty(
"InputWorkspace");
454 outFile.append(profileFunction);
455 outFile.append(
".dat");
456 std::string save_path = ConfigService::Instance().getString(
"defaultsave.directory");
457 outFile = save_path + outFile;
458 out.open(outFile.c_str(), std::ofstream::out);
461 double volumeBkg = 4.0 / 3.0 * M_PI * (std::pow(BackgroundOuterRadius[0], 3) - std::pow(BackgroundOuterRadius[0], 3));
463 double volumeRadius = 4.0 / 3.0 * M_PI * std::pow(PeakRadius[0], 3);
466 int nPeaks = peakWS->getNumberPeaks();
468 int zeroHKLCount = 0;
471 for (
int i = 0; i < nPeaks; ++i) {
476 IPeak &p = peakWS->getPeak(i);
479 bool missingIndex =
false;
487 if (pos.
X() == 0 && pos.
Y() == 0 && pos.
Z() == 0) {
492 throw std::runtime_error(
"Workspace does not have a coordinate system set");
497 if (edgeDist < std::max(BackgroundOuterRadius[0], PeakRadius[0])) {
498 g_log.
warning() <<
"Warning: sphere/cylinder for integration is off edge "
499 "of detector for peak "
500 << i <<
"; radius of edge = " << edgeDist <<
'\n';
501 if (!integrateEdge) {
502 if (replaceIntensity) {
511 bool dimensionsUsed[nd];
513 for (
size_t d = 0;
d < nd; ++
d) {
514 dimensionsUsed[
d] =
true;
515 center[
d] =
static_cast<coord_t>(pos[
d]);
521 double background_total = 0.0;
525 if (adaptiveQMultiplier != 0.0) {
527 for (
size_t d = 0;
d < nd; ++
d) {
528 lenQpeak += center[
d] * center[
d];
530 lenQpeak = std::sqrt(lenQpeak);
532 double adaptiveRadius = adaptiveQMultiplier * lenQpeak + *std::max_element(PeakRadius.begin(), PeakRadius.end());
533 if (adaptiveRadius <= 0.0) {
534 g_log.
error() <<
"Error: Radius for integration sphere of peak " << i <<
" is negative = " << adaptiveRadius
539 PeakRadiusVector[i] = 0.0;
540 BackgroundInnerRadiusVector[i] = 0.0;
541 BackgroundOuterRadiusVector[i] = 0.0;
544 PeakRadiusVector[i] = adaptiveRadius;
545 BackgroundInnerRadiusVector[i] = adaptiveQBackgroundMultiplier * lenQpeak +
546 *std::max_element(BackgroundInnerRadius.begin(), BackgroundInnerRadius.end());
547 BackgroundOuterRadiusVector[i] = adaptiveQBackgroundMultiplier * lenQpeak +
548 *std::max_element(BackgroundOuterRadius.begin(), BackgroundOuterRadius.end());
553 new PeakShapeSpherical(PeakRadiusVector[i], BackgroundInnerRadiusVector[i], BackgroundOuterRadiusVector[i],
556 const double scaleFactor = pow(PeakRadiusVector[i], 3) /
557 (pow(BackgroundOuterRadiusVector[i], 3) - pow(BackgroundInnerRadiusVector[i], 3));
559 if (BackgroundOuterRadius[0] > PeakRadius[0]) {
562 getRadiusSq,
static_cast<coord_t>(pow(BackgroundOuterRadiusVector[i], 2)), bgSignal, bgErrorSquared,
563 static_cast<coord_t>(pow(BackgroundInnerRadiusVector[i], 2)), useOnePercentBackgroundCorrection);
565 bgSignal *= scaleFactor;
566 bgErrorSquared *= scaleFactor * scaleFactor;
570 bool integrateAsEllipse = isEllipse;
571 if (isEllipse && missingIndex) {
572 integrateAsEllipse =
false;
573 g_log.
warning() <<
"Integrating un-indexed peak. Falling back to sphere peak shape to avoid numeric issues\n";
576 if (integrateAsEllipse) {
578 const auto bgDensity = bgSignal / (4 * M_PI * pow(PeakRadiusVector[i], 3) / 3);
579 std::array<V3D, 3> eigenvects;
580 std::array<double, 3> eigenvals;
581 V3D translation(0.0, 0.0, 0.0);
582 if (PeakRadius.size() == 1) {
583 V3D mean(0.0, 0.0, 0.0);
584 findEllipsoid<MDE, nd>(ws, getRadiusSq, pos,
static_cast<coord_t>(pow(PeakRadiusVector[i], 2)), qAxisIsFixed,
585 useCentroid, bgDensity, eigenvects, eigenvals, mean, maxCovarIter);
586 if (!majorAxisLengthFixed) {
588 auto max_stdev = sqrt(*std::max_element(eigenvals.begin(), eigenvals.end()));
589 BackgroundOuterRadiusVector[i] = 3 * max_stdev * (BackgroundOuterRadiusVector[i] / PeakRadiusVector[i]);
590 BackgroundInnerRadiusVector[i] = 3 * max_stdev * (BackgroundInnerRadiusVector[i] / PeakRadiusVector[i]);
591 PeakRadiusVector[i] = 3 * max_stdev;
595 translation = mean - pos;
597 for (
size_t d = 0;
d < 3; ++
d) {
598 center[
d] =
static_cast<coord_t>(mean[
d]);
604 std::transform(PeakRadius.cbegin(), PeakRadius.cend(), eigenvals.begin(),
605 [](
double r) { return std::pow(r, 2.0); });
606 eigenvects[0] =
V3D(1.0, 0.0, 0.0);
607 eigenvects[1] =
V3D(0.0, 1.0, 0.0);
608 eigenvects[2] =
V3D(0.0, 0.0, 1.0);
612 eigenvects, eigenvals);
614 if (PeakRadius.size() == 1) {
615 if (BackgroundOuterRadius[0] > PeakRadius[0]) {
620 getRadiusSq,
static_cast<coord_t>(pow(BackgroundOuterRadiusVector[i], 2)), bgSignal, bgErrorSquared,
621 static_cast<coord_t>(pow(BackgroundInnerRadiusVector[i], 2)), useOnePercentBackgroundCorrection);
624 bgSignal *= scaleFactor;
625 bgErrorSquared *= scaleFactor * scaleFactor;
629 auto max_stdev = pow(*std::max_element(eigenvals.begin(), eigenvals.end()), 0.5);
633 for (
size_t irad = 0; irad < peakRadii.size(); irad++) {
634 auto scale = pow(eigenvals[irad], 0.5) / max_stdev;
635 peakRadii[irad] = PeakRadiusVector[i] * scale;
636 backgroundInnerRadii[irad] = BackgroundInnerRadiusVector[i] * scale;
637 backgroundOuterRadii[irad] = BackgroundOuterRadiusVector[i] * scale;
640 new PeakShapeEllipsoid(eigenvects, peakRadii, backgroundInnerRadii, backgroundOuterRadii,
641 CoordinatesToUse, this->
name(), this->
version(), translation);
646 std::array<double, 3> eigenvals_background_inner;
647 std::array<double, 3> eigenvals_background_outer;
648 std::transform(BackgroundInnerRadius.cbegin(), BackgroundInnerRadius.cend(),
649 eigenvals_background_inner.begin(), [](
double r) { return std::pow(r, 2.0); });
650 std::transform(BackgroundOuterRadius.cbegin(), BackgroundOuterRadius.cend(),
651 eigenvals_background_outer.begin(), [](
double r) { return std::pow(r, 2.0); });
653 if (BackgroundOuterRadiusVector[0] > PeakRadiusVector[0]) {
656 eigenvects, eigenvals_background_inner);
658 eigenvects, eigenvals_background_outer);
667 static_cast<coord_t>(pow(BackgroundInnerRadiusVector[i], 2)), bgSignalInner,
668 bgErrorSquaredInner, 0.0, useOnePercentBackgroundCorrection);
670 static_cast<coord_t>(pow(BackgroundOuterRadiusVector[i], 2)), bgSignalOuter,
671 bgErrorSquaredOuter, 0.0, useOnePercentBackgroundCorrection);
674 bgSignal = bgSignalOuter - bgSignalInner;
675 bgErrorSquared = bgErrorSquaredInner + bgErrorSquaredOuter;
676 g_log.
debug() <<
"unscaled background signal from ellipsoid integration = " << bgSignal <<
'\n';
677 const double scale = (PeakRadius[0] * PeakRadius[1] * PeakRadius[2]) /
678 (BackgroundOuterRadius[0] * BackgroundOuterRadius[1] * BackgroundOuterRadius[2] -
679 BackgroundInnerRadius[0] * BackgroundInnerRadius[1] * BackgroundInnerRadius[2]);
681 bgErrorSquared *= pow(scale, 2);
685 auto max_stdev = pow(*std::max_element(eigenvals.begin(), eigenvals.end()), 0.5);
686 auto max_stdev_inner =
687 pow(*std::max_element(eigenvals_background_inner.begin(), eigenvals_background_inner.end()), 0.5);
688 auto max_stdev_outer =
689 pow(*std::max_element(eigenvals_background_outer.begin(), eigenvals_background_outer.end()), 0.5);
693 for (
size_t irad = 0; irad < peakRadii.size(); irad++) {
694 peakRadii[irad] = PeakRadiusVector[i] * pow(eigenvals[irad], 0.5) / max_stdev;
695 backgroundInnerRadii[irad] =
696 BackgroundInnerRadiusVector[i] * pow(eigenvals_background_inner[irad], 0.5) / max_stdev_inner;
697 backgroundOuterRadii[irad] =
698 BackgroundOuterRadiusVector[i] * pow(eigenvals_background_outer[irad], 0.5) / max_stdev_outer;
701 new PeakShapeEllipsoid(eigenvects, peakRadii, backgroundInnerRadii, backgroundOuterRadii,
707 signal, errorSquared, 0.0 ,
708 useOnePercentBackgroundCorrection);
714 Counts signal_fit(numSteps);
718 static_cast<coord_t>(cylinderLength), signal, errorSquared,
719 signal_fit.mutableRawData());
722 if (BackgroundOuterRadius[0] > PeakRadius[0]) {
727 static_cast<coord_t>(cylinderLength), bgSignal, bgErrorSquared,
728 signal_fit.mutableRawData());
730 Points points(signal_fit.size(), LinearGenerator(0, 1));
731 wsProfile2D->setHistogram(i, points, signal_fit);
738 if (BackgroundInnerRadius[0] != PeakRadius[0]) {
740 static_cast<coord_t>(cylinderLength), interiorSignal, interiorErrorSquared,
741 signal_fit.mutableRawData());
745 interiorSignal = signal;
746 interiorErrorSquared = errorSquared;
750 bgSignal -= interiorSignal;
754 bgErrorSquared -= interiorErrorSquared;
756 const double radiusRatio = (PeakRadius[0] / BackgroundOuterRadius[0]);
757 const double peakVolume = radiusRatio * radiusRatio * cylinderLength;
761 const double interiorRatio = (BackgroundInnerRadius[0] / BackgroundOuterRadius[0]);
764 const double bgVolume = 1.0 - interiorRatio * interiorRatio * cylinderLength;
768 const double scaleFactor = peakVolume / bgVolume;
769 bgSignal *= scaleFactor;
770 bgErrorSquared *= scaleFactor * scaleFactor;
772 Points points(signal_fit.size(), LinearGenerator(0, 1));
773 wsProfile2D->setHistogram(i, points, signal_fit);
776 if (profileFunction ==
"NoFit") {
778 for (
size_t j = 0; j < numSteps; j++) {
779 if (j < peakMin || j > peakMax)
780 background_total = background_total + wsProfile2D->y(i)[j];
782 signal = signal + wsProfile2D->y(i)[j];
784 errorSquared = std::fabs(signal);
790 std::string myFunc = std::string(
"name=LinearBackground;name=") + profileFunction;
791 auto maxPeak = std::max_element(signal_fit.begin(), signal_fit.end());
793 std::ostringstream strs;
795 std::string strMax = strs.str();
796 if (profileFunction ==
"Gaussian") {
797 myFunc +=
", PeakCentre=50, Height=" + strMax;
798 fitAlgorithm->setProperty(
"Constraints",
"40<f1.PeakCentre<60");
799 }
else if (profileFunction ==
"BackToBackExponential" || profileFunction ==
"IkedaCarpenterPV") {
800 myFunc +=
", X0=50, I=" + strMax;
801 fitAlgorithm->setProperty(
"Constraints",
"40<f1.X0<60");
803 fitAlgorithm->setProperty(
"CalcErrors",
true);
804 fitAlgorithm->setProperty(
"Function", myFunc);
805 fitAlgorithm->setProperty(
"InputWorkspace", wsProfile2D);
806 fitAlgorithm->setProperty(
"WorkspaceIndex",
static_cast<int>(i));
808 fitAlgorithm->executeAsChildAlg();
816 out << std::setw(20) <<
"spectrum"
818 for (
size_t j = 0; j < ifun->nParams(); ++j)
819 out << std::setw(20) << ifun->parameterName(j) <<
" ";
820 out << std::setw(20) <<
"chi2"
824 out << std::setw(20) << i <<
" ";
825 for (
size_t j = 0; j < ifun->nParams(); ++j) {
826 out << std::setw(20) << std::fixed << std::setprecision(10) << ifun->getParameter(j) <<
" ";
828 double chi2 = fitAlgorithm->getProperty(
"OutputChi2overDoF");
829 out << std::setw(20) << std::fixed << std::setprecision(10) << chi2 <<
"\n";
831 std::shared_ptr<const CompositeFunction> fun = std::dynamic_pointer_cast<const CompositeFunction>(ifun);
833 const auto &
x = wsProfile2D->x(i);
834 wsFit2D->setSharedX(i, wsProfile2D->sharedX(i));
835 wsDiff2D->setSharedX(i, wsProfile2D->sharedX(i));
839 fun->function(domain, yy);
842 wsFit2D->mutableY(i) = funcValues;
843 wsDiff2D->setSharedY(i, wsProfile2D->sharedY(i));
844 wsDiff2D->mutableY(i) -= wsFit2D->y(i);
848 if (integrationOption ==
"Sum") {
849 for (
size_t j = peakMin; j <= peakMax; j++)
850 if (std::isfinite(yy[j]))
853 gsl_integration_workspace *w = gsl_integration_workspace_alloc(1000);
861 gsl_integration_qags(&F,
x[peakMin],
x[peakMax], 0, 1e-7, 1000, w, &signal, &
error);
863 gsl_integration_workspace_free(w);
865 errorSquared = std::fabs(signal);
867 for (
size_t j = 0; j < numSteps; j++) {
868 double background = ifun->getParameter(0) + ifun->getParameter(1) *
x[j];
869 if (j < peakMin || j > peakMax)
870 background_total = background_total + background;
874 checkOverlap(i, peakWS, CoordinatesToUse, 2.0 * std::max(PeakRadiusVector[i], BackgroundOuterRadiusVector[i]));
876 if (signal != 0. || replaceIntensity) {
877 double edgeMultiplier = 1.0;
878 double peakMultiplier = 1.0;
880 if (edgeDist < BackgroundOuterRadius[0]) {
881 double e1 = BackgroundOuterRadius[0] - edgeDist;
883 double f1 = M_PI * std::pow(e1, 2) / 3 * (3 * BackgroundOuterRadius[0] - e1);
884 edgeMultiplier = volumeBkg / (volumeBkg - f1);
886 if (edgeDist < PeakRadius[0]) {
887 double sigma = PeakRadius[0] / 3.0;
889 double e1 = std::exp(-std::pow(edgeDist, 2) / (2 *
sigma *
sigma)) * PeakRadius[0];
891 double f1 = M_PI * std::pow(e1, 2) / 3 * (3 * PeakRadius[0] - e1);
892 peakMultiplier = volumeRadius / (volumeRadius - f1);
896 p.
setIntensity(peakMultiplier * signal - edgeMultiplier * (ratio * background_total + bgSignal));
898 edgeMultiplier * (ratio * ratio * std::fabs(background_total) + bgErrorSquared)));
901 g_log.
information() <<
"Peak " << i <<
" at " << pos <<
": signal " << signal <<
" (sig^2 " << errorSquared
902 <<
"), with background " << bgSignal + ratio * background_total <<
" (sig^2 "
903 << bgErrorSquared + ratio * ratio * std::fabs(background_total) <<
") subtracted.\n";
908 if (CoordinatesToUse ==
Kernel::HKL && zeroHKLCount > 0) {
909 if (zeroHKLCount == nPeaks) {
910 g_log.
error() <<
"No peaks are indexed, integrated at HKL=0,0,0. You might need to run IndexPeaks.\n";
912 g_log.
warning() << zeroHKLCount <<
" of " << nPeaks <<
" peaks are not indexed, integrated at HKL=0,0,0\n";
918 peakWS->mutableRun().addProperty(
"PeaksIntegrated", 1,
true);
920 peakWS->mutableRun().addProperty(
"PeakRadius", PeakRadiusVector,
true);
921 peakWS->mutableRun().addProperty(
"BackgroundInnerRadius", BackgroundInnerRadiusVector,
true);
922 peakWS->mutableRun().addProperty(
"BackgroundOuterRadius", BackgroundOuterRadiusVector,
true);
925 const std::string outfile =
getProperty(
"ProfilesFile");
926 if (outfile.length() > 0) {
931 g_log.
error(
"Can't locate SaveIsawPeaks algorithm");
934 alg->setProperty(
"InputWorkspace", peakWS);
935 alg->setProperty(
"ProfileWorkspace", wsProfile2D);
936 alg->setPropertyValue(
"Filename", outfile);