322 throw std::invalid_argument(
"For now, we expect the input MDEventWorkspace "
323 "to have 3 dimensions only.");
330 if (peakWS != inPeakWS)
331 peakWS = inPeakWS->clone();
343 g_log.
error(
"Can't execute MaskBTP algorithm for this instrument to set "
344 "edge for IntegrateIfOnEdge option");
351 std::vector<double> PeakRadius =
getProperty(
"PeakRadius");
353 std::vector<double> BackgroundOuterRadius =
getProperty(
"BackgroundOuterRadius");
355 std::vector<double> BackgroundInnerRadius =
getProperty(
"BackgroundInnerRadius");
357 bool useOnePercentBackgroundCorrection =
getProperty(
"UseOnePercentBackgroundCorrection");
359 bool manualEllip =
false;
360 if (PeakRadius.size() > 1) {
363 if (BackgroundInnerRadius.size() == 1)
364 BackgroundInnerRadius.resize(3, BackgroundInnerRadius[0]);
365 if (BackgroundOuterRadius.size() == 1)
366 BackgroundOuterRadius.resize(3, BackgroundOuterRadius[0]);
369 double minInnerRadius = PeakRadius[0];
370 for (
size_t r = 0; r < BackgroundInnerRadius.size(); r++) {
372 minInnerRadius = PeakRadius[r];
374 if (BackgroundInnerRadius[r] < minInnerRadius)
375 BackgroundInnerRadius[r] = minInnerRadius;
380 bool majorAxisLengthFixed =
getProperty(
"FixMajorAxisLength");
384 double cylinderLength =
getProperty(
"CylinderLength");
388 bool adaptiveQBackground =
getProperty(
"AdaptiveQBackground");
389 double adaptiveQMultiplier =
getProperty(
"AdaptiveQMultiplier");
390 double adaptiveQBackgroundMultiplier = 0.0;
391 if (adaptiveQBackground)
392 adaptiveQBackgroundMultiplier = adaptiveQMultiplier;
393 std::vector<double> PeakRadiusVector(peakWS->getNumberPeaks(), PeakRadius[0]);
394 std::vector<double> BackgroundInnerRadiusVector(peakWS->getNumberPeaks(), BackgroundInnerRadius[0]);
395 std::vector<double> BackgroundOuterRadiusVector(peakWS->getNumberPeaks(), BackgroundOuterRadius[0]);
398 size_t histogramNumber = peakWS->getNumberPeaks();
400 wsProfile2D = std::dynamic_pointer_cast<Workspace2D>(wsProfile);
401 AnalysisDataService::Instance().addOrReplace(
"ProfilesData", wsProfile2D);
403 wsFit2D = std::dynamic_pointer_cast<Workspace2D>(wsFit);
404 AnalysisDataService::Instance().addOrReplace(
"ProfilesFit", wsFit2D);
406 wsDiff2D = std::dynamic_pointer_cast<Workspace2D>(wsDiff);
407 AnalysisDataService::Instance().addOrReplace(
"ProfilesFitDiff", wsDiff2D);
408 auto newAxis1 = std::make_unique<TextAxis>(peakWS->getNumberPeaks());
409 auto newAxis2 = std::make_unique<TextAxis>(peakWS->getNumberPeaks());
410 auto newAxis3 = std::make_unique<TextAxis>(peakWS->getNumberPeaks());
411 auto newAxis1Raw = newAxis1.get();
412 auto newAxis2Raw = newAxis2.get();
413 auto newAxis3Raw = newAxis3.get();
414 wsProfile2D->replaceAxis(1, std::move(newAxis1));
415 wsFit2D->replaceAxis(1, std::move(newAxis2));
416 wsDiff2D->replaceAxis(1, std::move(newAxis3));
417 for (
int i = 0; i < peakWS->getNumberPeaks(); ++i) {
419 IPeak &p = peakWS->getPeak(i);
420 std::ostringstream label;
423 newAxis1Raw->setLabel(i, label.str());
424 newAxis2Raw->setLabel(i, label.str());
425 newAxis3Raw->setLabel(i, label.str());
428 double percentBackground =
getProperty(
"PercentBackground");
430 size_t peakMax = numSteps;
433 peakMin =
static_cast<size_t>(
static_cast<double>(numSteps) * percentBackground / 100.);
434 peakMax = numSteps - peakMin - 1;
435 size_t numPeakCh = peakMax - peakMin + 1;
436 size_t numBkgCh = numSteps - numPeakCh;
437 ratio =
static_cast<double>(numPeakCh) /
static_cast<double>(numBkgCh);
440 bool replaceIntensity =
getProperty(
"ReplaceIntensity");
441 bool integrateEdge =
getProperty(
"IntegrateIfOnEdge");
444 std::string profileFunction =
getProperty(
"ProfileFunction");
445 std::string integrationOption =
getProperty(
"IntegrationOption");
447 if (cylinderBool && profileFunction !=
"NoFit") {
448 std::string outFile =
getProperty(
"InputWorkspace");
449 outFile.append(profileFunction);
450 outFile.append(
".dat");
451 std::string save_path = ConfigService::Instance().getString(
"defaultsave.directory");
452 outFile = save_path + outFile;
453 out.open(outFile.c_str(), std::ofstream::out);
456 double volumeBkg = 4.0 / 3.0 * M_PI * (std::pow(BackgroundOuterRadius[0], 3) - std::pow(BackgroundOuterRadius[0], 3));
458 double volumeRadius = 4.0 / 3.0 * M_PI * std::pow(PeakRadius[0], 3);
461 int nPeaks = peakWS->getNumberPeaks();
465 for (
int i = 0; i < nPeaks; ++i) {
470 IPeak &p = peakWS->getPeak(i);
484 if (edgeDist < std::max(BackgroundOuterRadius[0], PeakRadius[0])) {
485 g_log.
warning() <<
"Warning: sphere/cylinder for integration is off edge "
486 "of detector for peak "
487 << i <<
"; radius of edge = " << edgeDist <<
'\n';
488 if (!integrateEdge) {
489 if (replaceIntensity) {
498 bool dimensionsUsed[nd];
500 for (
size_t d = 0;
d < nd; ++
d) {
501 dimensionsUsed[
d] =
true;
502 center[
d] =
static_cast<coord_t>(pos[
d]);
508 double background_total = 0.0;
512 if (adaptiveQMultiplier != 0.0) {
514 for (
size_t d = 0;
d < nd; ++
d) {
515 lenQpeak += center[
d] * center[
d];
517 lenQpeak = std::sqrt(lenQpeak);
519 double adaptiveRadius = adaptiveQMultiplier * lenQpeak + *std::max_element(PeakRadius.begin(), PeakRadius.end());
520 if (adaptiveRadius <= 0.0) {
521 g_log.
error() <<
"Error: Radius for integration sphere of peak " << i <<
" is negative = " << adaptiveRadius
526 PeakRadiusVector[i] = 0.0;
527 BackgroundInnerRadiusVector[i] = 0.0;
528 BackgroundOuterRadiusVector[i] = 0.0;
531 PeakRadiusVector[i] = adaptiveRadius;
532 BackgroundInnerRadiusVector[i] = adaptiveQBackgroundMultiplier * lenQpeak +
533 *std::max_element(BackgroundInnerRadius.begin(), BackgroundInnerRadius.end());
534 BackgroundOuterRadiusVector[i] = adaptiveQBackgroundMultiplier * lenQpeak +
535 *std::max_element(BackgroundOuterRadius.begin(), BackgroundOuterRadius.end());
540 new PeakShapeSpherical(PeakRadiusVector[i], BackgroundInnerRadiusVector[i], BackgroundOuterRadiusVector[i],
543 const double scaleFactor = pow(PeakRadiusVector[i], 3) /
544 (pow(BackgroundOuterRadiusVector[i], 3) - pow(BackgroundInnerRadiusVector[i], 3));
546 if (BackgroundOuterRadius[0] > PeakRadius[0]) {
549 getRadiusSq,
static_cast<coord_t>(pow(BackgroundOuterRadiusVector[i], 2)), bgSignal, bgErrorSquared,
550 static_cast<coord_t>(pow(BackgroundInnerRadiusVector[i], 2)), useOnePercentBackgroundCorrection);
552 bgSignal *= scaleFactor;
553 bgErrorSquared *= scaleFactor * scaleFactor;
559 const auto bgDensity = bgSignal / (4 * M_PI * pow(PeakRadiusVector[i], 3) / 3);
560 std::array<V3D, 3> eigenvects;
561 std::array<double, 3> eigenvals;
562 V3D translation(0.0, 0.0, 0.0);
563 if (PeakRadius.size() == 1) {
564 V3D mean(0.0, 0.0, 0.0);
565 findEllipsoid<MDE, nd>(ws, getRadiusSq, pos,
static_cast<coord_t>(pow(PeakRadiusVector[i], 2)), qAxisIsFixed,
566 useCentroid, bgDensity, eigenvects, eigenvals, mean, maxCovarIter);
567 if (!majorAxisLengthFixed) {
569 auto max_stdev = sqrt(*std::max_element(eigenvals.begin(), eigenvals.end()));
570 BackgroundOuterRadiusVector[i] = 3 * max_stdev * (BackgroundOuterRadiusVector[i] / PeakRadiusVector[i]);
571 BackgroundInnerRadiusVector[i] = 3 * max_stdev * (BackgroundInnerRadiusVector[i] / PeakRadiusVector[i]);
572 PeakRadiusVector[i] = 3 * max_stdev;
576 translation = mean - pos;
578 for (
size_t d = 0;
d < 3; ++
d) {
579 center[
d] =
static_cast<coord_t>(mean[
d]);
585 std::transform(PeakRadius.cbegin(), PeakRadius.cend(), eigenvals.begin(),
586 [](
double r) { return std::pow(r, 2.0); });
587 eigenvects[0] =
V3D(1.0, 0.0, 0.0);
588 eigenvects[1] =
V3D(0.0, 1.0, 0.0);
589 eigenvects[2] =
V3D(0.0, 0.0, 1.0);
593 eigenvects, eigenvals);
595 if (PeakRadius.size() == 1) {
596 if (BackgroundOuterRadius[0] > PeakRadius[0]) {
601 getRadiusSq,
static_cast<coord_t>(pow(BackgroundOuterRadiusVector[i], 2)), bgSignal, bgErrorSquared,
602 static_cast<coord_t>(pow(BackgroundInnerRadiusVector[i], 2)), useOnePercentBackgroundCorrection);
605 bgSignal *= scaleFactor;
606 bgErrorSquared *= scaleFactor * scaleFactor;
610 auto max_stdev = pow(*std::max_element(eigenvals.begin(), eigenvals.end()), 0.5);
614 for (
size_t irad = 0; irad < peakRadii.size(); irad++) {
615 auto scale = pow(eigenvals[irad], 0.5) / max_stdev;
616 peakRadii[irad] = PeakRadiusVector[i] * scale;
617 backgroundInnerRadii[irad] = BackgroundInnerRadiusVector[i] * scale;
618 backgroundOuterRadii[irad] = BackgroundOuterRadiusVector[i] * scale;
621 new PeakShapeEllipsoid(eigenvects, peakRadii, backgroundInnerRadii, backgroundOuterRadii,
622 CoordinatesToUse, this->
name(), this->
version(), translation);
627 std::array<double, 3> eigenvals_background_inner;
628 std::array<double, 3> eigenvals_background_outer;
629 std::transform(BackgroundInnerRadius.cbegin(), BackgroundInnerRadius.cend(),
630 eigenvals_background_inner.begin(), [](
double r) { return std::pow(r, 2.0); });
631 std::transform(BackgroundOuterRadius.cbegin(), BackgroundOuterRadius.cend(),
632 eigenvals_background_outer.begin(), [](
double r) { return std::pow(r, 2.0); });
634 if (BackgroundOuterRadiusVector[0] > PeakRadiusVector[0]) {
637 eigenvects, eigenvals_background_inner);
639 eigenvects, eigenvals_background_outer);
648 static_cast<coord_t>(pow(BackgroundInnerRadiusVector[i], 2)), bgSignalInner,
649 bgErrorSquaredInner, 0.0, useOnePercentBackgroundCorrection);
651 static_cast<coord_t>(pow(BackgroundOuterRadiusVector[i], 2)), bgSignalOuter,
652 bgErrorSquaredOuter, 0.0, useOnePercentBackgroundCorrection);
655 bgSignal = bgSignalOuter - bgSignalInner;
656 bgErrorSquared = bgErrorSquaredInner + bgErrorSquaredOuter;
657 g_log.
debug() <<
"unscaled background signal from ellipsoid integration = " << bgSignal <<
'\n';
658 const double scale = (PeakRadius[0] * PeakRadius[1] * PeakRadius[2]) /
659 (BackgroundOuterRadius[0] * BackgroundOuterRadius[1] * BackgroundOuterRadius[2] -
660 BackgroundInnerRadius[0] * BackgroundInnerRadius[1] * BackgroundInnerRadius[2]);
662 bgErrorSquared *= pow(scale, 2);
666 auto max_stdev = pow(*std::max_element(eigenvals.begin(), eigenvals.end()), 0.5);
667 auto max_stdev_inner =
668 pow(*std::max_element(eigenvals_background_inner.begin(), eigenvals_background_inner.end()), 0.5);
669 auto max_stdev_outer =
670 pow(*std::max_element(eigenvals_background_outer.begin(), eigenvals_background_outer.end()), 0.5);
674 for (
size_t irad = 0; irad < peakRadii.size(); irad++) {
675 peakRadii[irad] = PeakRadiusVector[i] * pow(eigenvals[irad], 0.5) / max_stdev;
676 backgroundInnerRadii[irad] =
677 BackgroundInnerRadiusVector[i] * pow(eigenvals_background_inner[irad], 0.5) / max_stdev_inner;
678 backgroundOuterRadii[irad] =
679 BackgroundOuterRadiusVector[i] * pow(eigenvals_background_outer[irad], 0.5) / max_stdev_outer;
682 new PeakShapeEllipsoid(eigenvects, peakRadii, backgroundInnerRadii, backgroundOuterRadii,
688 signal, errorSquared, 0.0 ,
689 useOnePercentBackgroundCorrection);
695 Counts signal_fit(numSteps);
699 static_cast<coord_t>(cylinderLength), signal, errorSquared,
700 signal_fit.mutableRawData());
703 if (BackgroundOuterRadius[0] > PeakRadius[0]) {
708 static_cast<coord_t>(cylinderLength), bgSignal, bgErrorSquared,
709 signal_fit.mutableRawData());
711 Points points(signal_fit.size(), LinearGenerator(0, 1));
712 wsProfile2D->setHistogram(i, points, signal_fit);
719 if (BackgroundInnerRadius[0] != PeakRadius[0]) {
721 static_cast<coord_t>(cylinderLength), interiorSignal, interiorErrorSquared,
722 signal_fit.mutableRawData());
726 interiorSignal = signal;
727 interiorErrorSquared = errorSquared;
731 bgSignal -= interiorSignal;
735 bgErrorSquared -= interiorErrorSquared;
737 const double radiusRatio = (PeakRadius[0] / BackgroundOuterRadius[0]);
738 const double peakVolume = radiusRatio * radiusRatio * cylinderLength;
742 const double interiorRatio = (BackgroundInnerRadius[0] / BackgroundOuterRadius[0]);
745 const double bgVolume = 1.0 - interiorRatio * interiorRatio * cylinderLength;
749 const double scaleFactor = peakVolume / bgVolume;
750 bgSignal *= scaleFactor;
751 bgErrorSquared *= scaleFactor * scaleFactor;
753 Points points(signal_fit.size(), LinearGenerator(0, 1));
754 wsProfile2D->setHistogram(i, points, signal_fit);
757 if (profileFunction ==
"NoFit") {
759 for (
size_t j = 0; j < numSteps; j++) {
760 if (j < peakMin || j > peakMax)
761 background_total = background_total + wsProfile2D->y(i)[j];
763 signal = signal + wsProfile2D->y(i)[j];
765 errorSquared = std::fabs(signal);
771 std::string myFunc = std::string(
"name=LinearBackground;name=") + profileFunction;
772 auto maxPeak = std::max_element(signal_fit.begin(), signal_fit.end());
774 std::ostringstream strs;
776 std::string strMax = strs.str();
777 if (profileFunction ==
"Gaussian") {
778 myFunc +=
", PeakCentre=50, Height=" + strMax;
779 fitAlgorithm->setProperty(
"Constraints",
"40<f1.PeakCentre<60");
780 }
else if (profileFunction ==
"BackToBackExponential" || profileFunction ==
"IkedaCarpenterPV") {
781 myFunc +=
", X0=50, I=" + strMax;
782 fitAlgorithm->setProperty(
"Constraints",
"40<f1.X0<60");
784 fitAlgorithm->setProperty(
"CalcErrors",
true);
785 fitAlgorithm->setProperty(
"Function", myFunc);
786 fitAlgorithm->setProperty(
"InputWorkspace", wsProfile2D);
787 fitAlgorithm->setProperty(
"WorkspaceIndex",
static_cast<int>(i));
789 fitAlgorithm->executeAsChildAlg();
797 out << std::setw(20) <<
"spectrum"
799 for (
size_t j = 0; j < ifun->nParams(); ++j)
800 out << std::setw(20) << ifun->parameterName(j) <<
" ";
801 out << std::setw(20) <<
"chi2"
805 out << std::setw(20) << i <<
" ";
806 for (
size_t j = 0; j < ifun->nParams(); ++j) {
807 out << std::setw(20) << std::fixed << std::setprecision(10) << ifun->getParameter(j) <<
" ";
809 double chi2 = fitAlgorithm->getProperty(
"OutputChi2overDoF");
810 out << std::setw(20) << std::fixed << std::setprecision(10) << chi2 <<
"\n";
812 std::shared_ptr<const CompositeFunction> fun = std::dynamic_pointer_cast<const CompositeFunction>(ifun);
814 const auto &
x = wsProfile2D->x(i);
815 wsFit2D->setSharedX(i, wsProfile2D->sharedX(i));
816 wsDiff2D->setSharedX(i, wsProfile2D->sharedX(i));
820 fun->function(domain, yy);
823 wsFit2D->mutableY(i) = funcValues;
824 wsDiff2D->setSharedY(i, wsProfile2D->sharedY(i));
825 wsDiff2D->mutableY(i) -= wsFit2D->y(i);
829 if (integrationOption ==
"Sum") {
830 for (
size_t j = peakMin; j <= peakMax; j++)
831 if (std::isfinite(yy[j]))
834 gsl_integration_workspace *w = gsl_integration_workspace_alloc(1000);
842 gsl_integration_qags(&F,
x[peakMin],
x[peakMax], 0, 1e-7, 1000, w, &signal, &
error);
844 gsl_integration_workspace_free(w);
846 errorSquared = std::fabs(signal);
848 for (
size_t j = 0; j < numSteps; j++) {
849 double background = ifun->getParameter(0) + ifun->getParameter(1) *
x[j];
850 if (j < peakMin || j > peakMax)
851 background_total = background_total + background;
855 checkOverlap(i, peakWS, CoordinatesToUse, 2.0 * std::max(PeakRadiusVector[i], BackgroundOuterRadiusVector[i]));
857 if (signal != 0. || replaceIntensity) {
858 double edgeMultiplier = 1.0;
859 double peakMultiplier = 1.0;
861 if (edgeDist < BackgroundOuterRadius[0]) {
862 double e1 = BackgroundOuterRadius[0] - edgeDist;
864 double f1 = M_PI * std::pow(e1, 2) / 3 * (3 * BackgroundOuterRadius[0] - e1);
865 edgeMultiplier = volumeBkg / (volumeBkg - f1);
867 if (edgeDist < PeakRadius[0]) {
868 double sigma = PeakRadius[0] / 3.0;
870 double e1 = std::exp(-std::pow(edgeDist, 2) / (2 *
sigma *
sigma)) * PeakRadius[0];
872 double f1 = M_PI * std::pow(e1, 2) / 3 * (3 * PeakRadius[0] - e1);
873 peakMultiplier = volumeRadius / (volumeRadius - f1);
877 p.
setIntensity(peakMultiplier * signal - edgeMultiplier * (ratio * background_total + bgSignal));
879 edgeMultiplier * (ratio * ratio * std::fabs(background_total) + bgErrorSquared)));
882 g_log.
information() <<
"Peak " << i <<
" at " << pos <<
": signal " << signal <<
" (sig^2 " << errorSquared
883 <<
"), with background " << bgSignal + ratio * background_total <<
" (sig^2 "
884 << bgErrorSquared + ratio * ratio * std::fabs(background_total) <<
") subtracted.\n";
890 peakWS->mutableRun().addProperty(
"PeaksIntegrated", 1,
true);
892 peakWS->mutableRun().addProperty(
"PeakRadius", PeakRadiusVector,
true);
893 peakWS->mutableRun().addProperty(
"BackgroundInnerRadius", BackgroundInnerRadiusVector,
true);
894 peakWS->mutableRun().addProperty(
"BackgroundOuterRadius", BackgroundOuterRadiusVector,
true);
897 const std::string outfile =
getProperty(
"ProfilesFile");
898 if (outfile.length() > 0) {
903 g_log.
error(
"Can't locate SaveIsawPeaks algorithm");
906 alg->setProperty(
"InputWorkspace", peakWS);
907 alg->setProperty(
"ProfileWorkspace", wsProfile2D);
908 alg->setPropertyValue(
"Filename", outfile);