25#include "MantidIndexing/IndexInfo.h"
32#include "MantidTypes/SpectrumDefinition.h"
39using namespace Kernel;
41using namespace Geometry;
42using namespace DataObjects;
47 auto dataVal = std::make_shared<CompositeValidator>();
53 "Particle counts as a function of wavelength");
55 "Name of the workspace that will contain the result of the calculation");
57 "A comma separated list of first bin boundary, width, last bin boundary. "
59 "this can be followed by a comma and more widths and last boundary "
61 "Negative width values indicate logarithmic binning.");
63 "Scaling to apply to each spectrum. Must have\n"
64 "the same number of spectra as the DetBankWorkspace");
65 auto wavVal = std::make_shared<CompositeValidator>();
70 "Scaling to apply to each bin.\n"
71 "Must have the same number of bins as the DetBankWorkspace");
74 "Scaling that depends on both pixel and wavelength together.\n"
75 "Must have the same number of bins and spectra as the DetBankWorkspace.");
76 declareProperty(
"AccountForGravity",
false,
"Whether to correct for the effects of gravity");
77 declareProperty(
"SolidAngleWeighting",
true,
"If true, pixels will be weighted by their solid angle.",
79 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
80 mustBePositive->setLower(0.0);
81 auto mustBeIntOver2 = std::make_shared<BoundedValidator<int>>();
82 mustBeIntOver2->setLower(3);
85 "To increase resolution some wavelengths are excluded within "
86 "this distance from the beam center (mm). Note that RadiusCut\n"
87 " and WaveCut both need to be larger than 0 to affect \n"
88 "the effective cutoff. See the algorithm description for\n"
89 " a detailed explanation of the cutoff.");
92 "To increase resolution by starting to remove some wavelengths below this"
93 " threshold (angstrom). Note that WaveCut\n"
94 " and RadiusCut both need to be larger than 0 to affect \n"
95 "on the effective cutoff. See the algorithm description for\n"
96 " a detailed explanation of the cutoff.");
99 "Set to true to output two additional workspaces which will "
100 "have the names OutputWorkspace_sumOfCounts "
101 "OutputWorkspace_sumOfNormFactors. The division of "
102 "_sumOfCounts and _sumOfNormFactors equals the workspace"
103 " returned by the property OutputWorkspace "
104 "(default is false).");
105 declareProperty(
"ExtraLength", 0.0, mustBePositive,
"Additional length for gravity correction.");
107 "SolidAngleNumberOfCylinderSlices", 10, mustBeIntOver2,
108 "The number of angular slices used when triangulating a cylinder in order to calculate the solid "
109 "angle of a tube detector. The default is 10 to preserve legacy behaviour, but increased accuracy has "
110 "been observed when using values of 11+");
113 "Workspace to calculate the Q resolution.\n");
125 const bool doGravity =
getProperty(
"AccountForGravity");
132 g_log.
debug() <<
"All input workspaces were found to be valid\n";
134 double const *
const binNorms = waveAdj ? &(waveAdj->y(0)[0]) :
nullptr;
136 double const *
const binNormEs = waveAdj ? &(waveAdj->e(0)[0]) :
nullptr;
142 auto useQResolution =
static_cast<bool>(qResolution);
147 auto &QOut = outputWS->x(0);
148 auto &YOut = outputWS->mutableY(0);
149 auto &EOutTo2 = outputWS->mutableE(0);
151 HistogramData::HistogramY normSum(YOut.size(), 0.0);
153 HistogramData::HistogramE normError2(EOutTo2.size(), 0.0);
155 HistogramData::HistogramDx qResolutionOut(YOut.size(), 0.0);
157 const auto numSpec =
static_cast<int>(
m_dataWS->getNumberHistograms());
160 const auto &spectrumInfo =
m_dataWS->spectrumInfo();
162 for (
int i = 0; i < numSpec; ++i) {
164 if (!spectrumInfo.hasDetectors(i)) {
165 g_log.
warning() <<
"Workspace index " << i <<
" (SpectrumIndex = " <<
m_dataWS->getSpectrum(i).getSpectrumNo()
166 <<
") has no detector assigned to it - discarding\n";
170 if (spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i))
176 const size_t wavStart =
178 if (wavStart >=
m_dataWS->y(i).size()) {
183 const size_t numWavbins =
m_dataWS->y(i).size() - wavStart;
187 HistogramData::HistogramY _noDirectUseStorage_(3 * numWavbins);
189 auto norms = _noDirectUseStorage_.begin();
192 auto normETo2s = norms + numWavbins;
194 auto QIn = normETo2s + numWavbins;
203 auto YIn =
m_dataWS->y(i).cbegin() + wavStart;
204 auto EIn =
m_dataWS->e(i).cbegin() + wavStart;
212 auto QResIn = useQResolution ? (qResolution->y(i).cbegin() + wavStart) : YIn;
216 auto loc = QOut.cend();
219 const auto end =
m_dataWS->y(i).cend();
220 for (; YIn != end; ++YIn, ++EIn, ++QIn, ++norms, ++normETo2s) {
225 if ((loc != QOut.begin()) && (loc != QOut.end())) {
227 const size_t bin = loc - QOut.begin() - 1;
230 normSum[bin] += *norms;
233 EOutTo2[bin] += (*EIn) * (*EIn);
234 normError2[bin] += *normETo2s;
235 if (useQResolution) {
236 auto QBin = (QOut[bin + 1] - QOut[bin]);
240 qResolutionOut[bin] += (*YIn) * std::sqrt((*QResIn) * (*QResIn) + QBin * QBin / 12.0);
246 if (useQResolution) {
254 const auto &inSpec =
m_dataWS->getSpectrum(i);
255 auto &outSpec = outputWS->getSpectrum(0);
256 outSpec.addDetectorIDs(inSpec.getDetectorIDs());
263 if (useQResolution) {
267 auto countsIterator = YOut.cbegin();
268 auto qResolutionIterator = qResolutionOut.begin();
269 for (; countsIterator != YOut.end(); ++countsIterator, ++qResolutionIterator) {
272 if ((*countsIterator) > 0.0) {
273 *qResolutionIterator = (*qResolutionIterator) / (*countsIterator);
276 outputWS->setPointStandardDeviations(0, std::move(qResolutionOut));
282 ws_sumOfCounts->setSharedX(0, outputWS->sharedX(0));
284 ws_sumOfCounts->mutableY(0) = YOut;
285 ws_sumOfCounts->setSharedDx(0, outputWS->sharedDx(0));
286 ws_sumOfCounts->setFrequencyVariances(0, outputWS->e(0));
289 ws_sumOfNormFactors->setSharedX(0, outputWS->sharedX(0));
290 ws_sumOfNormFactors->mutableY(0) = normSum;
291 ws_sumOfNormFactors->setSharedDx(0, outputWS->sharedDx(0));
292 ws_sumOfNormFactors->setFrequencyVariances(0, normError2);
294 helper.
outputParts(
this, ws_sumOfCounts, ws_sumOfNormFactors);
297 progress.report(
"Normalizing I(Q)");
299 normalize(normSum, normError2, YOut, EOutTo2);
311 HistogramData::BinEdges XOut(0);
315 Indexing::IndexInfo indexInfo(1);
316 indexInfo.setSpectrumDefinitions(std::vector<SpectrumDefinition>(1));
317 auto outputWS = create<MatrixWorkspace>(*
m_dataWS, indexInfo, XOut);
318 outputWS->getAxis(0)->unit() = UnitFactory::Instance().create(
"MomentumTransfer");
319 outputWS->setYUnitLabel(
"1/cm");
321 outputWS->setDistribution(
true);
323 outputWS->getSpectrum(0).clearDetectorIDs();
324 outputWS->getSpectrum(0).setSpectrumNo(1);
349 double const *
const binNormEs, HistogramData::HistogramY::iterator norm,
350 HistogramData::HistogramY::iterator normETo2)
const {
351 double detectorAdj, detAdjErr;
352 pixelWeight(pixelAdj, wsIndex, detectorAdj, detAdjErr);
354 for (
auto n = norm, e = normETo2;
n != normETo2; ++
n, ++e) {
356 *e = detAdjErr * detAdjErr;
359 if (binNorms && binNormEs) {
362 addWaveAdj(binNorms + wavStart, binNormEs + wavStart, norm, normETo2, wavePixelAdj->y(wsIndex).begin() + wavStart,
363 wavePixelAdj->e(wsIndex).begin() + wavStart);
365 addWaveAdj(binNorms + wavStart, binNormEs + wavStart, norm, normETo2);
367 normToMask(wavStart, wsIndex, norm, normETo2);
382 double &
error)
const {
383 const auto &detectorInfo =
m_dataWS->detectorInfo();
384 const V3D samplePos = detectorInfo.samplePosition();
387 const int numberOfCylinderSlices =
getProperty(
"SolidAngleNumberOfCylinderSlices");
389 for (
const auto detID :
m_dataWS->getSpectrum(wsIndex).getDetectorIDs()) {
390 const auto index = detectorInfo.indexOf(detID);
391 if (!detectorInfo.isMasked(
index))
392 weight += detectorInfo.detector(
index).solidAngle({samplePos, numberOfCylinderSlices});
397 if (weight < 1e-200) {
398 throw std::logic_error(
"Invalid (zero or negative) solid angle for one detector");
402 weight *= pixelAdj->readY(wsIndex)[0];
403 error = weight * pixelAdj->readE(wsIndex)[0];
421void Q1D2::addWaveAdj(
const double *c,
const double *Dc, HistogramData::HistogramY::iterator bInOut,
422 HistogramData::HistogramY::iterator e2InOut)
const {
434 const auto end = e2InOut;
435 for (; bInOut != end; ++e2InOut, ++c, ++Dc, ++bInOut) {
437 *e2InOut = ((*e2InOut) * (*c) * (*c)) + ((*Dc) * (*Dc) * (*bInOut) * (*bInOut));
439 *bInOut = (*bInOut) * (*c);
458void Q1D2::addWaveAdj(
const double *c,
const double *Dc, HistogramData::HistogramY::iterator bInOut,
459 HistogramData::HistogramY::iterator e2InOut,
460 HistogramData::HistogramY::const_iterator wavePixelAdjData,
461 HistogramData::HistogramE::const_iterator wavePixelAdjError)
const {
483 const auto end = e2InOut;
484 for (; bInOut != end; ++e2InOut, ++c, ++Dc, ++bInOut, ++wavePixelAdjData, ++wavePixelAdjError) {
486 *e2InOut = ((*e2InOut) * (*c) * (*c) * (*wavePixelAdjData) * (*wavePixelAdjData)) +
487 ((*Dc) * (*Dc) * (*bInOut) * (*bInOut) * (*wavePixelAdjData) * (*wavePixelAdjData)) +
488 ((*wavePixelAdjError) * (*wavePixelAdjError) * (*c) * (*c) * (*bInOut) * (*bInOut));
490 *bInOut = (*bInOut) * (*c) * (*wavePixelAdjData);
503void Q1D2::normToMask(
const size_t offSet,
const size_t wsIndex,
const HistogramData::HistogramY::iterator theNorms,
504 const HistogramData::HistogramY::iterator errorSquared)
const {
506 if (
m_dataWS->hasMaskedBins(wsIndex)) {
510 MatrixWorkspace::MaskList::const_iterator it;
511 for (it = mask.begin(); it != mask.end(); ++it) {
512 size_t outBin = it->first;
513 if (outBin < offSet) {
520 const double factor = 1.0 - (it->second);
521 *(theNorms + outBin) *= factor;
522 *(errorSquared + outBin) *= factor * factor;
541 const size_t offset, HistogramData::HistogramY::iterator Qs,
const double extraLength)
const {
542 static const double FOUR_PI = 4.0 * M_PI;
545 auto waves =
m_dataWS->x(wsInd).cbegin() + offset;
547 const auto end =
m_dataWS->x(wsInd).end() - 1;
550 for (; waves != end; ++Qs, ++waves) {
553 const double lambda = 0.5 * (*(waves + 1) + (*waves));
558 *Qs = FOUR_PI * sinTheta /
lambda;
563 const double factor = 2.0 * FOUR_PI * sin(spectrumInfo.
twoTheta(wsInd) * 0.5);
564 for (; waves != end; ++Qs, ++waves) {
567 *Qs = factor / (*(waves + 1) + (*waves));
585 HistogramData::HistogramX::const_iterator &loc)
const {
586 if (loc != OutQs.end()) {
587 while (loc != OutQs.begin()) {
588 if ((QToFind >= *(loc - 1)) && (QToFind < *loc)) {
593 if (QToFind < *loc) {
599 if (OutQs.empty() || QToFind > *(loc - 1)) {
607 loc = std::lower_bound(OutQs.begin(), OutQs.end(), QToFind);
620void Q1D2::normalize(
const HistogramData::HistogramY &normSum,
const HistogramData::HistogramE &normError2,
621 HistogramData::HistogramY &counts, HistogramData::HistogramE &errors)
const {
622 for (
size_t k = 0; k < counts.size(); ++k) {
624 const double c = normSum[k];
625 const double a = counts[k] /= c;
632 const double aOverc = a / c;
633 errors[k] = std::sqrt(errors[k] / (c * c) + normError2[k] * aOverc * aOverc);
#define DECLARE_ALGORITHM(classname)
const std::vector< double > * lambda
std::map< DeltaEMode::Type, std::string > index
#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_CRITICAL(name)
#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.
Base class from which all concrete algorithm classes should be derived.
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.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
A validator which provides a TENTATIVE check that a workspace contains common bins in each spectrum.
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
A validator which checks that a workspace has a valid instrument.
std::map< size_t, double > MaskList
Masked bins for each spectrum are stored as a set of pairs containing <bin index, weight>
Helper class for reporting progress from algorithms.
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
double twoTheta(const size_t index) const
Returns the scattering angle 2 theta in radians (angle w.r.t.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
A helper class for calculating neutron's gravitional drop.
double calcSinTheta(const double wavAngstroms) const
Caclulates the sin of the that the neutron left the sample at, before the effect of gravity.
void normToMask(const size_t offSet, const size_t wsIndex, const HistogramData::HistogramY::iterator theNorms, const HistogramData::HistogramY::iterator errorSquared) const
Scaled to bin masking, to the normalization.
void calculateNormalization(const size_t wavStart, const size_t wsIndex, const API::MatrixWorkspace_const_sptr &pixelAdj, const API::MatrixWorkspace_const_sptr &wavePixelAdj, double const *const binNorms, double const *const binNormEs, HistogramData::HistogramY::iterator norm, HistogramData::HistogramY::iterator normETo2) const
Calculate the normalization term for each output bin.
void normalize(const HistogramData::HistogramY &normSum, const HistogramData::HistogramE &normError2, HistogramData::HistogramY &counts, HistogramData::HistogramE &errors) const
Divides the number of counts in each output Q bin by the wrighting ("number that would expected to ar...
void pixelWeight(const API::MatrixWorkspace_const_sptr &pixelAdj, const size_t wsIndex, double &weight, double &error) const
Calculates the normalisation for the spectrum specified by the index number that was passed as the so...
Q1D2()
Default constructor.
void convertWavetoQ(const API::SpectrumInfo &spectrumInfo, const size_t wsInd, const bool doGravity, const size_t offset, HistogramData::HistogramY::iterator Qs, const double extraLength) const
Fills a vector with the Q values calculated from the wavelength bin centers from the input workspace ...
API::MatrixWorkspace_sptr setUpOutputWorkspace(const std::vector< double > &binParams) const
Creates the output workspace, its size, units, etc.
API::MatrixWorkspace_const_sptr m_dataWS
the experimental workspace with counts across the detector
void init() override
Initialisation code.
void getQBinPlus1(const HistogramData::HistogramX &OutQs, const double QToFind, HistogramData::HistogramY::const_iterator &loc) const
This is a slightly "clever" method as it makes some guesses about where is best to look for the right...
void exec() override
Execution code.
void addWaveAdj(const double *c, const double *Dc, HistogramData::HistogramY::iterator bInOut, HistogramData::HistogramY::iterator e2InOut) const
Calculates the contribution to the normalization terms from each bin in a spectrum.
Helper class for the Q1D and Qxy algorithms.
size_t waveLengthCutOff(const API::MatrixWorkspace_const_sptr &dataWS, const API::SpectrumInfo &spectrumInfo, const double RCut, const double WCut, const size_t wsInd) const
Finds the first index number of the first wavelength bin that should included based on the the calcul...
void outputParts(API::Algorithm *alg, const API::MatrixWorkspace_sptr &sumOfCounts, const API::MatrixWorkspace_sptr &sumOfNormFactors)
This method performs the common work between Qxy and Q1D2 if algorihtm parameter OutputParts=True.
void examineInput(const API::MatrixWorkspace_const_sptr &dataWS, const API::MatrixWorkspace_const_sptr &binAdj, const API::MatrixWorkspace_const_sptr &detectAdj, const API::MatrixWorkspace_const_sptr &qResolution)
Checks if workspaces input to Q1D or Qxy are reasonable.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
void warning(const std::string &msg)
Logs at warning level.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
int MANTID_KERNEL_DLL createAxisFromRebinParams(const std::vector< double > ¶ms, std::vector< double > &xnew, const bool resize_xnew=true, const bool full_bins_only=false, const double xMinHint=std::nan(""), const double xMaxHint=std::nan(""), const bool useReverseLogarithmic=false, const double power=-1)
Creates a new output X array given a 'standard' set of rebinning parameters.
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.
@ Input
An input workspace.
@ Output
An output workspace.