Mantid
Loading...
Searching...
No Matches
RemoveBins.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
9#include "MantidAPI/Axis.h"
16#include "MantidKernel/Unit.h"
18
19using Mantid::HistogramData::HistogramE;
20using Mantid::HistogramData::HistogramX;
21using Mantid::HistogramData::HistogramY;
22
23namespace Mantid::Algorithms {
24
25using namespace Kernel;
26using namespace API;
27
28// Register the class into the algorithm factory
29DECLARE_ALGORITHM(RemoveBins)
30
32 : API::Algorithm(), m_inputWorkspace(), m_spectrumInfo(nullptr), m_startX(DBL_MAX), m_endX(-DBL_MAX), m_rangeUnit(),
33 m_interpolate(false) {}
34
39 auto wsValidator = std::make_shared<HistogramValidator>();
40 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, wsValidator),
41 "The name of the input workspace.");
42 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
43 "The name of the output workspace.");
44
45 auto mustHaveValue = std::make_shared<MandatoryValidator<double>>();
46 declareProperty("XMin", Mantid::EMPTY_DBL(), mustHaveValue, "The lower bound of the region to be removed.");
47 declareProperty("XMax", Mantid::EMPTY_DBL(), mustHaveValue, "The upper bound of the region to be removed.");
48
49 std::vector<std::string> units = UnitFactory::Instance().getKeys();
50
51 // remove some known units that will not work
52 units.erase(std::remove(units.begin(), units.end(), "Empty"), units.end());
53 units.erase(std::remove(units.begin(), units.end(), "Label"), units.end());
54 units.erase(std::remove(units.begin(), units.end(), "Time"), units.end());
55 units.erase(std::remove(units.begin(), units.end(), "Degrees"), units.end());
56
57 // add a default do nothing value
58 units.insert(units.begin(), "AsInput");
59 declareProperty("RangeUnit", "AsInput", std::make_shared<StringListValidator>(units),
60 "The unit in which XMin/XMax are being given. If not given, "
61 "it will peak the unit from the Input workspace X unit.");
62
63 std::vector<std::string> propOptions{"None", "Linear"};
64 declareProperty("Interpolation", "None", std::make_shared<StringListValidator>(propOptions),
65 "Whether mid-axis bins should be interpolated linearly "
66 "(\"Linear\") or set to zero (\"None\"). Note: Used when the "
67 "region to be removed is within a bin. Linear scales the "
68 "value in that bin by the proportion of it that is outside "
69 "the region to be removed and none sets it to zero");
70
71 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
72 mustBePositive->setLower(0);
73 declareProperty("WorkspaceIndex", EMPTY_INT(), mustBePositive,
74 "If set, will remove data only in the given spectrum of the "
75 "workspace. Otherwise, all spectra will be acted upon.");
76}
77
81std::map<std::string, std::string> RemoveBins::validateInputs() {
82 std::map<std::string, std::string> result;
83 const std::string rangeUnit = getProperty("RangeUnit");
84
85 // Get input workspace
86 m_inputWorkspace = getProperty("InputWorkspace");
87
88 // If that was OK, then we can get their values
89 m_startX = getProperty("XMin");
90 m_endX = getProperty("XMax");
91
92 if (m_startX > m_endX) {
93 const std::string failure("XMax must be greater than XMin.");
94 g_log.error(failure);
95 result["XMax"] = failure;
96 }
97
98 // If WorkspaceIndex has been set it must be valid
99 const int index = getProperty("WorkspaceIndex");
100 if (!isEmpty(index) && index >= static_cast<int>(m_inputWorkspace->getNumberHistograms())) {
101 std::stringstream failureMsg;
102 failureMsg << "The value of WorkspaceIndex provided (" << index << ") is larger than the size of this workspace ("
103 << m_inputWorkspace->getNumberHistograms() << ")";
104 g_log.error(failureMsg.str());
105 result["WorkspaceIndex"] = failureMsg.str();
106 }
107
108 const std::string interpolation = getProperty("Interpolation");
109 m_interpolate = (interpolation == "Linear");
110
111 const bool unitChange = (rangeUnit != "AsInput");
112 if (unitChange) {
113 std::string errorString = "";
114 if (m_inputWorkspace->axes() == 0)
115 errorString = "A single valued workspace has no unit, which is required for "
116 "this algorithm";
117
118 Kernel::Unit_const_sptr unit = m_inputWorkspace->getAxis(0)->unit();
119 // If m_unitID is empty it means that the workspace must have units, which
120 // can be anything
121 if (unit && (!std::dynamic_pointer_cast<const Kernel::Unit>(unit))) {
122 errorString = "The workspace must have units if the RangeUnit is not \"AsInput\"";
123 }
124 if (!errorString.empty()) {
125 g_log.error() << "InputWorkspace: " << errorString << "\n";
126 result["InputWorkspace"] = errorString;
127 }
128 }
129 return result;
130}
131
136 // If the X range has been given in a different unit, or if the workspace
137 // isn't square, then we will need
138 // to calculate the bin indices to cut out each time.
139 const std::string rangeUnit = getProperty("RangeUnit");
140 const bool unitChange = (rangeUnit != "AsInput" && rangeUnit != m_inputWorkspace->getAxis(0)->unit()->unitID());
141 if (unitChange)
142 m_rangeUnit = UnitFactory::Instance().create(rangeUnit);
143 const bool commonBins = m_inputWorkspace->isCommonBins();
144 const int index = getProperty("WorkspaceIndex");
145 const bool singleSpectrum = !isEmpty(index);
146 const bool recalcRange = (unitChange || !commonBins);
147
148 // If the above evaluates to false, and the range given is at the edge of the
149 // workspace, then we can just call
150 // CropWorkspace as a ChildAlgorithm and we're done.
151 auto &X0 = m_inputWorkspace->x(0);
152 if (!singleSpectrum && !recalcRange && (m_startX <= X0.front() || m_endX >= X0.back())) {
153 double start, end;
154 if (m_startX <= X0.front()) {
155 start = m_endX;
156 end = X0.back();
157 } else {
158 start = X0.front();
159 end = m_startX;
160 }
161
162 try {
163 this->crop(start, end);
164 return;
165 } catch (...) {
166 } // If this fails for any reason, just carry on and do it the other way
167 }
168
169 m_spectrumInfo = &m_inputWorkspace->spectrumInfo();
170
171 MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
172
173 if (m_inputWorkspace != outputWS) // Create the output workspace only if not the same as input
174 {
175 outputWS = WorkspaceFactory::Instance().create(m_inputWorkspace);
176 }
177
178 // Loop over the spectra
179 int start = 0, end = 0;
180 const auto blockSize = static_cast<int>(m_inputWorkspace->x(0).size());
181 const auto numHists = static_cast<int>(m_inputWorkspace->getNumberHistograms());
182 Progress prog(this, 0.0, 1.0, numHists);
183 for (int i = 0; i < numHists; ++i) {
184 outputWS->setHistogram(i, m_inputWorkspace->histogram(i));
185
186 // If just operating on a single spectrum and this isn't it, go to next
187 // iteration
188 if (singleSpectrum && (i != index))
189 continue;
190
191 double startX(m_startX), endX(m_endX);
192 // Calculate the X limits for this spectrum, if necessary
193 if (unitChange) {
194 this->transformRangeUnit(i, startX, endX);
195 }
196
197 auto &X = m_inputWorkspace->x(i);
198 auto &outY = outputWS->mutableY(i);
199 auto &outE = outputWS->mutableE(i);
200 // Calculate the bin indices corresponding to the X range, if necessary
201 if (recalcRange || singleSpectrum || !i) {
202 start = this->findIndex(startX, X);
203 end = this->findIndex(endX, X);
204 }
205
206 if (start == 0 || end == blockSize) {
207 // Remove bins from either end
208 this->RemoveFromEnds(start, end, outY, outE);
209 } else {
210 // Remove bins from middle
211 const double startFrac = (X[start] - startX) / (X[start] - X[start - 1]);
212 const double endFrac = (endX - X[end - 1]) / (X[end] - X[end - 1]);
213 this->RemoveFromMiddle(start - 1, end, startFrac, endFrac, outY, outE);
214 }
215 prog.report();
216 } // Loop over spectra
217
218 // Assign to the output workspace property
219 setProperty("OutputWorkspace", outputWS);
220 m_inputWorkspace.reset();
221}
222
225void RemoveBins::crop(const double &start, const double &end) {
226 auto childAlg = createChildAlgorithm("CropWorkspace");
227 childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace",
228 std::const_pointer_cast<MatrixWorkspace>(m_inputWorkspace));
229 childAlg->setProperty<double>("XMin", start);
230 childAlg->setProperty<double>("XMax", end);
231 childAlg->executeAsChildAlg();
232
233 // Only get to here if successful
234 // Assign the result to the output workspace property
235 MatrixWorkspace_sptr outputWS = childAlg->getProperty("OutputWorkspace");
236 setProperty("OutputWorkspace", outputWS);
237}
238
244void RemoveBins::transformRangeUnit(const int index, double &startX, double &endX) {
245 const Kernel::Unit_sptr inputUnit = m_inputWorkspace->getAxis(0)->unit();
246 // First check for a 'quick' conversion
247 double factor, power;
248 if (m_rangeUnit->quickConversion(*inputUnit, factor, power)) {
249 startX = factor * std::pow(m_startX, power);
250 endX = factor * std::pow(m_endX, power);
251 } else {
252 double l1 = m_spectrumInfo->l1();
253
256 double l2 = 0.;
257 if (pmap.find(UnitParams::l2) != pmap.end()) {
258 l2 = pmap[UnitParams::l2];
259 }
260 double theta = 0.;
261 if (pmap.find(UnitParams::twoTheta) != pmap.end()) {
262 l2 = pmap[UnitParams::twoTheta];
263 }
264 g_log.debug() << "Detector for index " << index << " has L1+L2=" << l1 + l2 << " & 2theta= " << theta << '\n';
265 std::vector<double> endPoints;
266 endPoints.emplace_back(startX);
267 endPoints.emplace_back(endX);
268 try {
269 std::vector<double> emptyVec;
270 // assume elastic
271 m_rangeUnit->toTOF(endPoints, emptyVec, l1, 0, pmap);
272 inputUnit->fromTOF(endPoints, emptyVec, l1, 0, pmap);
273 startX = endPoints.front();
274 endX = endPoints.back();
275 } catch (std::exception &) {
276 throw std::runtime_error("Unable to retrieve detector properties "
277 "required for unit conversion");
278 }
279 }
280
281 if (startX > endX) {
282 const double temp = startX;
283 startX = endX;
284 endX = temp;
285 }
286
287 g_log.debug() << "For index " << index << ", X range given corresponds to " << startX << "-" << endX
288 << " in workspace's unit\n";
289}
290
297int RemoveBins::findIndex(const double &value, const HistogramX &vec) {
298 auto pos = std::lower_bound(vec.cbegin(), vec.cend(), value);
299 return static_cast<int>(std::distance(vec.cbegin(), pos));
300}
301
308void RemoveBins::RemoveFromEnds(int start, int end, HistogramY &Y, HistogramE &E) {
309 if (start)
310 --start;
311 auto size = static_cast<int>(Y.size());
312 if (end > size)
313 end = size;
314 for (int j = start; j < end; ++j) {
315 Y[j] = 0.0;
316 E[j] = 0.0;
317 }
318}
319
334void RemoveBins::RemoveFromMiddle(const int &start, const int &end, const double &startFrac, const double &endFrac,
335 HistogramY &Y, HistogramE &E) {
336 // Remove bins from middle
337 double valPrev = 0;
338 double valNext = 0;
339 double errPrev = 0;
340 double errNext = 0;
341
342 // Values for interpolation
343 if (m_interpolate) {
344 valPrev = Y[start - 1];
345 valNext = Y[end];
346 errPrev = E[start - 1];
347 errNext = E[end];
348 }
349
350 const double m = (valNext - valPrev) / (1.0 * (end - start) + 2.0); // Gradient
351 const double c = valPrev; // Intercept
352
353 double aveE = (errPrev + errNext) / 2; // Cheat: will do properly later
354
355 for (int j = start; j < end; ++j) {
356 if (!m_interpolate && j == start) {
357 Y[j] *= startFrac;
358 E[j] *= startFrac;
359 } else if (!m_interpolate && j == end - 1) {
360 Y[j] *= endFrac;
361 E[j] *= endFrac;
362 } else {
363 Y[j] = m * (j - start + 1) + c;
364 E[j] = aveE;
365 }
366 }
367}
368
369} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double value
The value of the point.
Definition: FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
Base class from which all concrete algorithm classes should be derived.
Definition: Algorithm.h:85
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
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.
Definition: Algorithm.cpp:842
Kernel::Logger & g_log
Definition: Algorithm.h:451
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
double l1() const
Returns L1 (distance from source to sample).
void getDetectorValues(const Kernel::Unit &inputUnit, const Kernel::Unit &outputUnit, const Kernel::DeltaEMode::Type emode, const bool signedTheta, int64_t wsIndex, Kernel::UnitParametersMap &pmap) const
Get the detector values relevant to unit conversion for a workspace index.
A property class for workspaces.
Removes bins from a workspace.
Definition: RemoveBins.h:46
void RemoveFromEnds(int start, int end, HistogramData::HistogramY &Y, HistogramData::HistogramE &E)
Zeroes data (Y/E) at the end of a spectrum.
Definition: RemoveBins.cpp:308
void init() override
Initialisation method.
Definition: RemoveBins.cpp:38
double m_endX
The range end point.
Definition: RemoveBins.h:77
void transformRangeUnit(const int index, double &startX, double &endX)
Convert the X range given into the unit of the input workspace.
Definition: RemoveBins.cpp:244
double m_startX
The range start point.
Definition: RemoveBins.h:76
void exec() override
Executes the algorithm.
Definition: RemoveBins.cpp:135
Kernel::Unit_sptr m_rangeUnit
The unit in which the above range is given.
Definition: RemoveBins.h:78
void crop(const double &start, const double &end)
Calls CropWorkspace as a Child Algorithm to remove bins from the start or end of a square workspace.
Definition: RemoveBins.cpp:225
void RemoveFromMiddle(const int &start, const int &end, const double &startFrac, const double &endFrac, HistogramData::HistogramY &Y, HistogramData::HistogramE &E)
Removes bins in the middle of the data (Y/E).
Definition: RemoveBins.cpp:334
API::MatrixWorkspace_const_sptr m_inputWorkspace
The input workspace.
Definition: RemoveBins.h:74
int findIndex(const double &value, const HistogramData::HistogramX &vec)
Finds the index in an ordered vector which follows the given value.
Definition: RemoveBins.cpp:297
bool m_interpolate
Whether removed bins should be interpolated.
Definition: RemoveBins.h:79
std::map< std::string, std::string > validateInputs() override
Checks cross property validation.
Definition: RemoveBins.cpp:81
const API::SpectrumInfo * m_spectrumInfo
Definition: RemoveBins.h:75
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.
Definition: Logger.cpp:114
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::unordered_map< UnitParams, double > UnitParametersMap
Definition: Unit.h:30
std::shared_ptr< const Unit > Unit_const_sptr
Shared pointer to the Unit base class (const version)
Definition: Unit.h:231
std::shared_ptr< Unit > Unit_sptr
Shared pointer to the Unit base class.
Definition: Unit.h:229
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition: EmptyValues.h:25
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
Generate a tableworkspace to store the calibration results.
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54