13#include "MantidHistogramData/HistogramDx.h"
14#include "MantidHistogramData/HistogramE.h"
15#include "MantidHistogramData/HistogramX.h"
16#include "MantidHistogramData/HistogramY.h"
25#include <boost/format.hpp>
26#include <boost/math/special_functions.hpp>
27#include <boost/tuple/tuple.hpp>
33using Mantid::HistogramData::HistogramE;
34using Mantid::HistogramData::HistogramY;
39using MinMaxTuple = boost::tuple<double, double>;
41 return MinMaxTuple(rhsWS->x(0).front(), lhsWS->x(0).back());
45 const size_t binIndex) {
46 const auto &indexes = specialIndexes[spectrumIndex];
47 return std::binary_search(indexes.cbegin(), indexes.cend(), binIndex);
51 const size_t binIndex) {
52 return hasSpecialValue(specialValues.
nanY, spectrumIndex, binIndex) ||
53 hasSpecialValue(specialValues.
infY, spectrumIndex, binIndex);
58 const size_t spectrumIndex,
const size_t binIndex) {
59 if (hasSpecialValue(lhsSpecialValues.
infY, spectrumIndex, binIndex) ||
60 hasSpecialValue(rhsSpecialValues.
infY, spectrumIndex, binIndex))
61 return std::numeric_limits<double>::infinity();
62 return std::numeric_limits<double>::quiet_NaN();
84 const auto histogramCount =
static_cast<int>(source->getNumberHistograms());
86 for (
int i = 0; i < histogramCount; ++i) {
89 product->setSharedX(i, source->sharedX(i));
91 const auto &sourceY = source->y(i);
92 const auto &sourceE = source->e(i);
95 product->mutableY(i) = HistogramY(sourceY.size(), 0);
96 product->mutableE(i) = HistogramE(sourceE.size(), 0);
98 auto &newY = product->mutableY(i);
99 auto &newE = product->mutableE(i);
102 std::copy(sourceY.begin() + a1 + 1, sourceY.begin() + a2, newY.begin() + a1 + 1);
103 std::copy(sourceE.begin() + a1 + 1, sourceE.begin() + a2, newE.begin() + a1 + 1);
118 const auto histogramCount =
static_cast<int>(source->getNumberHistograms());
120 for (
int i = 0; i < histogramCount; ++i) {
123 auto &sourceY = source->mutableY(i);
124 auto &sourceE = source->mutableE(i);
125 for (
int binIndex = a1; binIndex < a2; ++binIndex) {
126 sourceY[binIndex] = 0;
127 sourceE[binIndex] = 0;
141 "LHS input workspace.");
143 "RHS input workspace, must be same type as LHSWorkspace "
144 "(histogram or point data).");
146 "Output stitched workspace.");
148 "Start overlap x-value in units of x-axis.");
150 "End overlap x-value in units of x-axis.");
152 "Rebinning Parameters. See Rebin for format. If only a "
153 "single value is provided, start and end are taken from "
154 "input workspaces.");
156 "Scaling either with respect to LHS workspace or RHS workspace");
158 "True to use a provided value for the scale factor.");
160 "OutputScalingWorkspace",
"",
161 "Output WorkspaceSingleValue containing the scale factor and its error. No workspace creation if left empty.");
162 auto manualScaleFactorValidator = std::make_shared<BoundedValidator<double>>();
163 manualScaleFactorValidator->setLower(0);
164 manualScaleFactorValidator->setExclusive(
true);
167 "Provided value for the scale factor.");
169 "The actual used value for the scaling factor.");
171 "If true, invalid signal values do not contribute to overlap bins where the other workspace has "
172 "valid signal values.");
179 std::map<std::string, std::string> issues;
183 issues[
"LHSWorkspace"] =
"The LHSWorkspace must be a MatrixWorkspace.";
186 issues[
"RHSWorkspace"] =
"The RHSWorkspace must be a MatrixWorkspace.";
188 if (!issues.empty()) {
194 if (!compatible.empty()) {
196 if (!(compatible ==
"spectra must have either Dx values or not; ") ||
197 (
rhs->isHistogramData()))
198 issues[
"RHSWorkspace"] =
"Workspace " +
rhs->getName() +
" is not compatible: " + compatible +
"\n";
212 double startOverlapVal = this->
getProperty(
"StartOverlap");
214 const bool startOverlapBeyondRange = (startOverlapVal < intesectionMin) || (startOverlapVal > intesectionMax);
215 if (startOverlapProp->
isDefault() || startOverlapBeyondRange) {
216 if (!startOverlapProp->
isDefault() && startOverlapBeyondRange) {
218 std::sprintf(message,
219 "StartOverlap is outside range at %0.4f, Min is "
220 "%0.4f, Max is %0.4f . Forced to be: %0.4f",
221 startOverlapVal, intesectionMin, intesectionMax, intesectionMin);
224 startOverlapVal = intesectionMin;
225 std::stringstream buffer;
226 buffer <<
"StartOverlap calculated to be: " << startOverlapVal;
229 return startOverlapVal;
241 double endOverlapVal = this->
getProperty(
"EndOverlap");
243 const bool endOverlapBeyondRange = (endOverlapVal < intesectionMin) || (endOverlapVal > intesectionMax);
244 if (endOverlapProp->
isDefault() || endOverlapBeyondRange) {
245 if (!endOverlapProp->
isDefault() && endOverlapBeyondRange) {
247 std::sprintf(message,
248 "EndOverlap is outside range at %0.4f, Min is "
249 "%0.4f, Max is %0.4f . Forced to be: %0.4f",
250 endOverlapVal, intesectionMin, intesectionMax, intesectionMax);
253 endOverlapVal = intesectionMax;
254 std::stringstream buffer;
255 buffer <<
"EndOverlap calculated to be: " << endOverlapVal;
258 return endOverlapVal;
268 const bool scaleRHS)
const {
269 std::vector<double> inputParams = this->
getProperty(
"Params");
271 const bool areParamsDefault = prop->
isDefault();
273 const auto &lhsX = lhsWS->x(0);
274 auto it = std::min_element(lhsX.begin(), lhsX.end());
275 const double minLHSX = *it;
277 const auto &rhsX = rhsWS->x(0);
278 it = std::max_element(rhsX.begin(), rhsX.end());
279 const double maxRHSX = *it;
281 std::vector<double> result;
282 if (areParamsDefault) {
283 std::vector<double> calculatedParams;
287 double calculatedStep = 0;
291 calculatedStep = lhsX[1] - lhsX[0];
295 calculatedStep = rhsX[1] - rhsX[0];
298 calculatedParams.emplace_back(minLHSX);
299 calculatedParams.emplace_back(calculatedStep);
300 calculatedParams.emplace_back(maxRHSX);
301 result = calculatedParams;
303 if (inputParams.size() == 1) {
304 std::vector<double> calculatedParams;
305 calculatedParams.emplace_back(minLHSX);
306 calculatedParams.emplace_back(inputParams.front());
307 calculatedParams.emplace_back(maxRHSX);
308 result = calculatedParams;
310 result = inputParams;
326 rebin->setProperty(
"InputWorkspace", input);
327 rebin->setProperty(
"Params", params);
328 std::stringstream ssParams;
329 ssParams << params[0] <<
"," << params[1] <<
"," << params[2];
334 const auto histogramCount =
static_cast<int>(outWS->getNumberHistograms());
339 for (
int i = 0; i < histogramCount; ++i) {
341 std::vector<size_t> &nanEIndexes = specialValues.
nanE[i];
342 std::vector<size_t> &nanYIndexes = specialValues.
nanY[i];
343 std::vector<size_t> &infEIndexes = specialValues.
infE[i];
344 std::vector<size_t> &infYIndexes = specialValues.
infY[i];
346 auto &sourceY = outWS->mutableY(i);
347 auto &sourceE = outWS->mutableE(i);
349 for (
size_t j = 0; j < sourceY.size(); ++j) {
350 const double value = sourceY[j];
351 if (std::isnan(
value)) {
352 nanYIndexes.emplace_back(j);
354 }
else if (std::isinf(
value)) {
355 infYIndexes.emplace_back(j);
359 const double eValue = sourceE[j];
360 if (std::isnan(eValue)) {
361 nanEIndexes.emplace_back(j);
363 }
else if (std::isinf(eValue)) {
364 infEIndexes.emplace_back(j);
388 g_log.
information(
"Integration RangeLower: " + boost::lexical_cast<std::string>(start));
389 g_log.
information(
"Integration RangeUpper: " + boost::lexical_cast<std::string>(stop));
391 return integration->getProperty(
"OutputWorkspace");
414 const std::string in1 =
"__Stitch1D_intermediate_workspace_1__";
415 const std::string in2 =
"__Stitch1D_intermediate_workspace_2__";
416 Mantid::API::AnalysisDataService::Instance().addOrReplace(in1, inOne);
417 Mantid::API::AnalysisDataService::Instance().addOrReplace(in2, inTwo);
419 conjoinX->initialize();
420 conjoinX->setProperty(
"InputWorkspaces", std::vector<std::string>{in1, in2});
422 Mantid::API::AnalysisDataService::Instance().remove(in1);
423 Mantid::API::AnalysisDataService::Instance().remove(in2);
425 return std::dynamic_pointer_cast<Mantid::API::MatrixWorkspace>(ws);
435 auto a1 =
static_cast<int>(
workspace->yIndexOfX(startOverlap));
436 auto a2 =
static_cast<int>(
workspace->yIndexOfX(endOverlap));
438 throw std::runtime_error(
"The Params you have provided for binning yield a "
439 "workspace in which start and end overlap appear "
440 "in the same bin. Make binning finer via input "
443 return boost::tuple<int, int>(a1, a2);
451 for (
auto i = 0u; i < ws->getNumberHistograms(); ++i) {
452 const auto &e = ws->e(i);
454 if (std::accumulate(e.begin(), e.end(), 0.0) != 0.0) {
471 if (ws->size() == dxWS->size()) {
472 for (
size_t i = 0; i < ws->getNumberHistograms(); ++i) {
473 if (dxWS->hasDx(i) && !ws->hasDx(i) && !ws->isHistogramData()) {
474 ws->setSharedDx(i, dxWS->sharedDx(i));
480 if (m_scaleFactor < 1e-2 || m_scaleFactor > 1e2 || std::isnan(
m_scaleFactor)) {
481 std::stringstream messageBuffer;
482 messageBuffer <<
"Stitch1D calculated scale factor is: " <<
m_scaleFactor
483 <<
". Check the overlap region is non-zero.";
494 const MinMaxTuple intesectionXRegion = calculateXIntersection(lhsWS, rhsWS);
496 const size_t histogramCount = rhsWS->getNumberHistograms();
501 const double intersectionMin = intesectionXRegion.get<0>();
502 const double intersectionMax = intesectionXRegion.get<1>();
503 double startOverlap =
getStartOverlap(intersectionMin, intersectionMax);
504 double endOverlap =
getEndOverlap(intersectionMin, intersectionMax);
505 if (startOverlap > endOverlap) {
506 std::string message = boost::str(boost::format(
"Stitch1D cannot have a StartOverlap > EndOverlap. "
507 "StartOverlap: %0.9f, EndOverlap: %0.9f") %
508 startOverlap % endOverlap);
509 throw std::runtime_error(message);
512 const bool scaleRHS = this->
getProperty(
"ScaleRHSWorkspace");
516 if (lhsWS->isHistogramData()) {
518 const double xMin = params.front();
519 const double xMax = params.back();
527 if (startOverlap < xMin) {
528 std::string message = boost::str(boost::format(
"Stitch1D StartOverlap is outside the available X range. "
529 "StartOverlap: %10.9f, X min: %10.9f") %
530 startOverlap % xMin);
531 throw std::runtime_error(message);
533 if (endOverlap > xMax) {
534 std::string message = boost::str(boost::format(
"Stitch1D EndOverlap is outside the available X range. "
535 "EndOverlap: %10.9f, X max: %10.9f") %
537 throw std::runtime_error(message);
545 const bool useManualScaleFactor = this->
getProperty(
"UseManualScaleFactor");
546 if (useManualScaleFactor) {
552 auto rhsOverlapIntegrated =
integration(
rhs, startOverlap, endOverlap);
553 auto lhsOverlapIntegrated =
integration(lhs, startOverlap, endOverlap);
555 auto scalingFactors = lhsOverlapIntegrated / rhsOverlapIntegrated;
558 auto scalingFactors = rhsOverlapIntegrated / lhsOverlapIntegrated;
563 std::stringstream messageBuffer;
568 if (lhsWS->isHistogramData()) {
570 int a1 = boost::tuples::get<0>(startEnd);
571 int a2 = boost::tuples::get<1>(startEnd);
577 maskInPlace(a1 + 1,
static_cast<int>(lhs->blocksize()), lhs);
586 overlapMeans = sum * 0.5;
593 result = lhs + overlapMeans +
rhs;
598 g_log.
error(
"Could not retrieve joined workspace.");
602 childAlg->setProperty(
"InputWorkspace", result);
604 result = childAlg->getProperty(
"OutputWorkspace");
610 const std::string scalingWsName = this->
getPropertyValue(
"OutputScalingWorkspace");
611 if (!scalingWsName.empty()) {
615 createSingleAlg->executeAsChildAlg();
617 AnalysisDataService::Instance().addOrReplace(scalingWsName, singleWS);
629 auto histogramCount =
static_cast<int>(ws->getNumberHistograms());
630 bool defaultBounds =
false;
631 if (bounds.a1 == -1 && bounds.a2 == -1) {
632 defaultBounds =
true;
634 }
else if (bounds.a1 < 0 || bounds.a2 < 0) {
635 throw std::runtime_error(
636 "Invalid bounds provided for re-inserting special values. Bounds must be either both -1 or both non-negative.");
640 for (
int i = 0; i < histogramCount; ++i) {
643 bounds.a1 =
static_cast<int>(ws->y(i).size()) - 1;
645 auto &sourceY = ws->mutableY(i);
646 auto &sourceE = ws->mutableE(i);
648 for (
auto j : lhsSpecialValues.nanY[i])
649 if (static_cast<int>(j) <= bounds.a1)
650 sourceY[j] =
std::numeric_limits<double>::quiet_NaN();
651 for (
auto j : lhsSpecialValues.infY[i])
652 if (static_cast<int>(j) <= bounds.a1)
653 sourceY[j] =
std::numeric_limits<double>::infinity();
654 for (
auto j : lhsSpecialValues.nanE[i])
655 if (static_cast<int>(j) <= bounds.a1)
656 sourceE[j] =
std::numeric_limits<double>::quiet_NaN();
657 for (
auto j : lhsSpecialValues.infE[i])
658 if (static_cast<int>(j) <= bounds.a1)
659 sourceE[j] =
std::numeric_limits<double>::infinity();
661 for (
auto j : rhsSpecialValues.nanY[i])
662 if (static_cast<int>(j) >= bounds.a2)
663 sourceY[j] =
std::numeric_limits<double>::quiet_NaN();
664 for (
auto j : rhsSpecialValues.infY[i])
665 if (static_cast<int>(j) >= bounds.a2)
666 sourceY[j] =
std::numeric_limits<double>::infinity();
667 for (
auto j : rhsSpecialValues.nanE[i])
668 if (static_cast<int>(j) >= bounds.a2)
669 sourceE[j] =
std::numeric_limits<double>::quiet_NaN();
670 for (
auto j : rhsSpecialValues.infE[i])
671 if (static_cast<int>(j) >= bounds.a2)
672 sourceE[j] =
std::numeric_limits<double>::infinity();
690 auto histogramCount =
static_cast<int>(overlap->getNumberHistograms());
692 for (
int i = 0; i < histogramCount; ++i) {
694 auto &overlapY = overlap->mutableY(i);
695 auto &overlapE = overlap->mutableE(i);
696 const auto &lhsY = lhs->y(i);
697 const auto &lhsE = lhs->e(i);
698 const auto &rhsY =
rhs->y(i);
699 const auto &rhsE =
rhs->e(i);
701 for (
size_t j =
static_cast<size_t>(bounds.
a1 + 1); j < static_cast<size_t>(bounds.
a2); ++j) {
702 const bool lhsInvalidY = hasInvalidY(lhsSpecialValues, i, j);
703 const bool rhsInvalidY = hasInvalidY(rhsSpecialValues, i, j);
704 if (lhsInvalidY && !rhsInvalidY) {
705 overlapY[j] = rhsY[j];
706 overlapE[j] = rhsE[j];
707 }
else if (!lhsInvalidY && rhsInvalidY) {
708 overlapY[j] = lhsY[j];
709 overlapE[j] = lhsE[j];
710 }
else if (lhsInvalidY && rhsInvalidY) {
711 overlapY[j] = specialYValue(lhsSpecialValues, rhsSpecialValues, i, j);
712 overlapE[j] = specialYValue(lhsSpecialValues, rhsSpecialValues, i,
#define DECLARE_ALGORITHM(classname)
const std::vector< double > & rhs
double value
The value of the point.
IPeaksWorkspace_sptr workspace
#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.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
A property class for workspaces.
void setReferenceProperties(const API::MatrixWorkspace_sptr &)
Sets the properties of the reference (usually first) workspace, to later check the compatibility of t...
std::string checkCompatibility(const API::MatrixWorkspace_sptr &, bool checkNumberHistograms=false)
Compares the properties of the input workspace with the reference.
Stitch1D : Stitches two Matrix Workspaces together into a single output.
SpecialValueIndexes m_lhsSpecialValues
Index per workspace spectra of special values.
Mantid::API::MatrixWorkspace_sptr maskAllBut(int a1, int a2, Mantid::API::MatrixWorkspace_sptr &source)
Mask out everything but the data in the ranges.
bool hasNonzeroErrors(Mantid::API::MatrixWorkspace_sptr &ws)
Does the x-axis have non-zero errors.
Mantid::API::MatrixWorkspace_sptr rebin(Mantid::API::MatrixWorkspace_sptr &input, const std::vector< double > ¶ms, SpecialValueIndexes &specialValues)
Perform rebin and record special values that are zeroed.
boost::tuple< int, int > findStartEndIndexes(double startOverlap, double endOverlap, Mantid::API::MatrixWorkspace_sptr &workspace)
Find the start and end indexes.
std::vector< std::vector< size_t > > SpecialTypeIndexes
Helper typedef for storing special-value indexes per spectrum.
void useValidOverlapData(const Mantid::API::MatrixWorkspace_sptr &overlap, const Mantid::API::MatrixWorkspace_sptr &lhs, const Mantid::API::MatrixWorkspace_sptr &rhs, const SpecialValueIndexes &lhsSpecialValues, const SpecialValueIndexes &rhsSpecialValues, const overlapBounds bounds)
Use valid overlap values where exactly one workspace has an invalid signal value.
double m_errorScaleFactor
std::vector< double > getRebinParams(Mantid::API::MatrixWorkspace_const_sptr &lhsWS, Mantid::API::MatrixWorkspace_const_sptr &rhsWS, const bool scaleRHS) const
Get the rebin parameters.
void maskInPlace(int a1, int a2, Mantid::API::MatrixWorkspace_sptr &source)
Mask out everything but the data in the ranges, but do it inplace.
SpecialValueIndexes m_rhsSpecialValues
Mantid::API::MatrixWorkspace_sptr integration(Mantid::API::MatrixWorkspace_sptr const &input, const double start, const double stop)
Perform integration.
void scaleWorkspace(API::MatrixWorkspace_sptr &ws, API::MatrixWorkspace_sptr &scaleFactorWS, API::MatrixWorkspace_const_sptr &dxWS)
Scale workspace (left hand side or right hand side)
double getStartOverlap(const double intesectionMin, const double intesectionMax) const
Get the start overlap.
void exec() override
Overwrites Algorithm method.
void reinsertSpecialValues(const Mantid::API::MatrixWorkspace_sptr &ws, const SpecialValueIndexes &lhsSpecialValues, const SpecialValueIndexes &rhsSpecialValues, overlapBounds bounds)
Add back special values only where no valid data contributes to the output.
double m_scaleFactor
Scaling factors.
void init() override
Overwrites Algorithm method.
double getEndOverlap(const double intesectionMin, const double intesectionMax) const
Get the end overlap.
static const double range_tolerance
Range tolerance.
Mantid::API::MatrixWorkspace_sptr weightedMean(Mantid::API::MatrixWorkspace_sptr const &inOne, Mantid::API::MatrixWorkspace_sptr const &inTwo)
Calculate the weighted mean.
Mantid::API::MatrixWorkspace_sptr conjoinXAxis(Mantid::API::MatrixWorkspace_sptr const &inOne, Mantid::API::MatrixWorkspace_sptr const &inTwo)
Conjoin x axis.
std::map< std::string, std::string > validateInputs() override
Cross-check properties with each other.
Concrete workspace implementation.
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 notice(const std::string &msg)
Logs at notice level.
void error(const std::string &msg)
Logs at error level.
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
The concrete, templated class for properties.
Base class for properties.
virtual bool isDefault() const =0
Overriden function that returns if property has the same value that it was initialised with,...
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
MANTID_KERNEL_DLL bool withinAbsoluteDifference(T const x, T const y, S const tolerance)
Test whether x, y are within absolute tolerance tol.
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::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Indexes of Y and E values that were NaN or infinite before processing.
void resize(size_t histogramCount)
@ Input
An input workspace.
@ Output
An output workspace.