13#include "MantidHistogramData/Histogram.h"
24using namespace Kernel;
25using namespace DataObjects;
26using HistogramData::Histogram;
37 double centre{std::numeric_limits<double>::quiet_NaN()};
49Window resolveWindow(
const MatrixWorkspace &ws,
const size_t wi,
const double lo,
const double hi) {
51 const size_t istart = ws.yIndexOfX(lo, wi);
52 const size_t iend = ws.yIndexOfX(hi, wi);
55 }
catch (
const std::out_of_range &) {
65double centralMoment3(
const double s1,
const double s2,
const double s3,
const double count) {
67 const double mu2 = s2 /
count;
68 const double mu3 = s3 /
count;
69 return 2.0 * (
mu *
mu *
mu) - (3.0 *
mu * mu2) + mu3;
99double estimateSkewBackground(
const std::vector<double> &
y) {
100 const size_t n =
y.size();
107 std::vector<size_t> order(
n);
108 std::iota(order.begin(), order.end(),
size_t{0});
109 std::sort(order.begin(), order.end(), [&
y](
size_t a,
size_t b) { return y[a] > y[b]; });
112 double s1 = 0.0, s2 = 0.0, s3 = 0.0;
113 for (
const double v :
y) {
120 double prevMoment = centralMoment3(s1, s2, s3,
static_cast<double>(
n));
122 double bgCount =
static_cast<double>(
n);
124 for (
size_t dropped = 1; dropped <
n; ++dropped) {
125 const double v =
y[order[dropped - 1]];
129 const double count =
static_cast<double>(
n - dropped);
130 const double moment = centralMoment3(s1, s2, s3,
count);
131 if (moment >= prevMoment || moment < 0.0)
137 return bgSum / bgCount;
143Estimate estimateWindow(
const Histogram &histo,
const Window &window,
const double fallbackCentre) {
145 est.centre = fallbackCentre;
149 const auto &
X = histo.x();
150 const auto &
Y = histo.y();
151 const auto &E = histo.e();
152 const size_t istart = window.istart;
153 const size_t m = window.iend -
istart;
155 std::vector<double> yseg(
Y.begin() +
istart,
Y.begin() + window.iend);
156 if (!std::any_of(yseg.cbegin(), yseg.cend(), [](
double v) { return v > 0.0; }))
159 const double bg = estimateSkewBackground(yseg);
163 for (
size_t k = 0; k + 1 <
m; ++k)
164 integral += 0.5 * ((yseg[k] - bg) + (yseg[k + 1] - bg)) * (
X[
istart + k + 1] -
X[
istart + k]);
168 for (
size_t k = 0; k <
m; ++k) {
172 const double t = E[
istart + k] * bw;
176 const auto argmax =
static_cast<size_t>(std::distance(yseg.cbegin(), std::max_element(yseg.cbegin(), yseg.cend())));
179 est.sigma = std::sqrt(sig2);
181 est.centre =
X[
istart + argmax];
189 "Workspace whose spectra should be integrated.");
191 "Per-spectrum integration windows, one spectrum per InputWorkspace spectrum, following the "
192 "FitPeaks FitPeakWindowWorkspace convention: each spectrum holds 2*nPeaks X values arranged as "
193 "[min0, max0, min1, max1, ...] (in InputWorkspace X units), so a peak's window can differ per "
194 "spectrum. A ragged workspace is accepted: every spectrum must hold a non-zero even number of "
195 "X values, but spectra may hold different numbers of [min, max] pairs, and a peak index "
196 "beyond a spectrum's pairs is reported with a NaN PeakCentre.");
198 "Table with one row per (peak, spectrum): PeakIndex, WorkspaceIndex, Intensity, Sigma, "
199 "Background, PeakCentre. The number of peaks is the largest number of windows held by any "
200 "spectrum of PeakWindowWorkspace. PeakIndex is only the position of the window in that "
201 "spectrum's list, so rows sharing a PeakIndex refer to the same physical peak only if "
202 "PeakWindowWorkspace lists the windows in the same peak order for every spectrum.");
206 std::map<std::string, std::string> issues;
209 if (!inputWS || !windowWS)
211 if (windowWS->getNumberHistograms() != inputWS->getNumberHistograms()) {
212 issues[
"PeakWindowWorkspace"] =
"must have the same number of spectra as InputWorkspace.";
216 for (
size_t wi = 0; wi < windowWS->getNumberHistograms(); ++wi) {
217 const size_t nx = windowWS->x(wi).size();
218 if (nx == 0 || nx % 2 != 0) {
219 issues[
"PeakWindowWorkspace"] =
"each spectrum must hold a non-zero even number of X values ([min, max] "
220 "pairs, one per peak); spectrum " +
232 const auto nHist =
static_cast<int64_t
>(inputWS->getNumberHistograms());
237 size_t minPeaks = std::numeric_limits<size_t>::max();
238 for (int64_t i = 0; i < nHist; ++i) {
239 const size_t n = windowWS->x(
static_cast<size_t>(i)).size() / 2;
240 nPeaks = std::max(nPeaks,
n);
241 minPeaks = std::min(minPeaks,
n);
243 if (nHist > 0 && minPeaks != nPeaks)
244 g_log.
warning() <<
"PeakWindowWorkspace is ragged: spectra hold between " << minPeaks <<
" and " << nPeaks
245 <<
" windows. The table has " << nPeaks
246 <<
" peaks and rows for undefined windows are reported with a NaN PeakCentre. PeakIndex is "
247 "the position of the window in each spectrum's list, so it only identifies the same "
248 "physical peak across spectra if the windows are listed in the same peak order.\n";
252 const size_t nRows = nPeaks *
static_cast<size_t>(nHist);
253 std::vector<Estimate> results(nRows);
255 Progress prog(
this, 0.0, 1.0, nHist);
258 for (int64_t i = 0; i < nHist; ++i) {
260 const auto wi =
static_cast<size_t>(i);
261 const auto histo = inputWS->histogram(wi);
262 const auto &winX = windowWS->x(wi);
263 const size_t nPeaksHere = winX.size() / 2;
264 for (
size_t j = 0; j < nPeaksHere; ++j) {
265 const double lo = winX[2 * j];
266 const double hi = winX[2 * j + 1];
267 const Window window = resolveWindow(*inputWS, wi, lo, hi);
268 results[j *
static_cast<size_t>(nHist) + wi] = estimateWindow(histo, window, 0.5 * (lo + hi));
276 out->addColumn(
"int",
"PeakIndex");
277 out->addColumn(
"int",
"WorkspaceIndex");
278 out->addColumn(
"double",
"Intensity");
279 out->addColumn(
"double",
"Sigma");
280 out->addColumn(
"double",
"Background");
281 out->addColumn(
"double",
"PeakCentre");
282 for (
size_t j = 0; j < nPeaks; ++j) {
283 for (int64_t i = 0; i < nHist; ++i) {
284 const Estimate &e = results[j *
static_cast<size_t>(nHist) +
static_cast<size_t>(i)];
286 row << static_cast<int>(j) <<
static_cast<int>(i) << e.intensity << e.sigma << e.background << e.centre;
#define DECLARE_ALGORITHM(classname)
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Helper class for reporting progress from algorithms.
TableRow represents a row in a TableWorkspace.
A property class for workspaces.
void exec() override
Virtual method - must be overridden by concrete algorithm.
void init() override
Virtual method - must be overridden by concrete algorithm.
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void warning(const std::string &msg)
Logs at warning level.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
double integral(double func(const double, const double, const double), const double a, const double b, const double g, const double w0)
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
@ Output
An output workspace.