Mantid
Loading...
Searching...
No Matches
AppendSpectra.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 +
7#include <utility>
8
12#include "MantidAPI/Run.h"
14#include "MantidAPI/TextAxis.h"
21#include "MantidIndexing/IndexInfo.h"
24
25using namespace Mantid::Indexing;
26using namespace Mantid::Kernel;
27using namespace Mantid::API;
28using namespace Mantid::DataObjects;
29
30namespace {
31std::vector<Mantid::detid_t> getDetectorsSkippingMonitors(const Mantid::Geometry::DetectorInfo &detectorInfo) {
32 const auto &pixelIDs = detectorInfo.detectorIDs();
33 std::vector<Mantid::detid_t> nonMonitorDetectors;
34 nonMonitorDetectors.reserve(pixelIDs.size());
35 // getting only non monitors in
36 for (Mantid::detid_t detIdx = 0; std::cmp_less(detIdx, pixelIDs.size()); detIdx++) {
37 if (!detectorInfo.isMonitor(detIdx)) {
38 nonMonitorDetectors.push_back(pixelIDs.at(detIdx));
39 }
40 }
41 return nonMonitorDetectors;
42}
43} // namespace
44
45namespace Mantid::Algorithms {
46// Register the algorithm into the AlgorithmFactory
47DECLARE_ALGORITHM(AppendSpectra)
48
49
50const std::string AppendSpectra::name() const { return "AppendSpectra"; }
51
53int AppendSpectra::version() const { return 1; }
54
58 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace1", "", Direction::Input,
59 std::make_shared<CommonBinsValidator>()),
60 "The name of the first input workspace");
61 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace2", "", Direction::Input,
62 std::make_shared<CommonBinsValidator>()),
63 "The name of the second input workspace");
64
65 declareProperty("ValidateInputs", true, "Perform a set of checks that the two input workspaces are compatible.");
66 declareProperty("Number", 1, std::make_shared<BoundedValidator<int>>(1, EMPTY_INT()),
67 "Append the spectra from InputWorkspace2 multiple times.");
68
69 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
70 "The name of the output workspace");
71
72 declareProperty("MergeLogs", false, "Whether to combine the logs of the two input workspaces");
73 declareProperty("AppendYAxisLabels", false,
74 "Whether to append y axis labels; this is done automatically if there is spectra overlap");
75 declareProperty("RewriteSpectraMap", false,
76 "Rewrites the detectorID associated with each spectrum, moving each detector to the position on the "
77 "original workspace");
78}
79
80std::map<std::string, std::string> AppendSpectra::validateInputs() {
81 std::map<std::string, std::string> inputs;
82 const MatrixWorkspace_const_sptr ws1 = getProperty("InputWorkspace1");
83 const MatrixWorkspace_const_sptr ws2 = getProperty("InputWorkspace2");
84 const EventWorkspace_const_sptr eventWs1 = std::dynamic_pointer_cast<const EventWorkspace>(ws1);
85 const EventWorkspace_const_sptr eventWs2 = std::dynamic_pointer_cast<const EventWorkspace>(ws2);
86
87 // Make sure that we are not mis-matching EventWorkspaces and other types of workspaces
88 if (((eventWs1) && (!eventWs2)) || ((!eventWs1) && (eventWs2))) {
89 const std::string msg = "Only one of the input workspaces are of type "
90 "EventWorkspace; please use matching workspace "
91 "types (both EventWorkspace or both "
92 "Workspace2D). ";
93 inputs["InputWorkspace1"] = msg;
94 inputs["InputWorkspace2"] = msg;
95 }
96 if (!isDefault("RewriteSpectraMap")) {
97 if (!isDefault("Number")) {
98 inputs["RewriteSpectraMap"] += "Rewrite spectra only implemented when number is 1. ";
99 }
100 if (const auto &inst1Name = ws1->getInstrumentName(), inst2Name = ws2->getInstrumentName();
101 inst1Name.empty() || inst2Name.empty() || (inst1Name != inst2Name)) {
102 inputs["RewriteSpectraMap"] += "Both input workspace must have valid instruments to rewrite the spectra maps.";
103 } else {
104 const auto countNonMonitorSpectra = [](const MatrixWorkspace_const_sptr &workspace) {
105 const auto &spectrumInfo = workspace->spectrumInfo();
106 size_t count = 0;
107 for (size_t index = 0; index < workspace->getNumberHistograms(); ++index) {
108 if (!spectrumInfo.isMonitor(index))
109 ++count;
110 }
111 return count;
112 };
113 if (ws1->getInstrument()->getNumberDetectors(true) < countNonMonitorSpectra(ws1) + countNonMonitorSpectra(ws2)) {
114 inputs["RewriteSpectraMap"] +=
115 "There are less available detectors than total number of spectrum in the appended workspace. "
116 "Not possible to rewrite their positions.";
117 }
118 }
119 }
120 return inputs;
121}
122
126 // Retrieve the input workspaces
127 MatrixWorkspace_const_sptr ws1 = getProperty("InputWorkspace1");
128 MatrixWorkspace_const_sptr ws2 = getProperty("InputWorkspace2");
129 DataObjects::EventWorkspace_const_sptr eventWs1 = std::dynamic_pointer_cast<const EventWorkspace>(ws1);
130 DataObjects::EventWorkspace_const_sptr eventWs2 = std::dynamic_pointer_cast<const EventWorkspace>(ws2);
131
132 bool ValidateInputs = this->getProperty("ValidateInputs");
133 if (ValidateInputs) {
134 // Check that the input workspaces meet the requirements for this algorithm
135 this->checkCompatibility(*ws1, *ws2);
136 }
137
138 const bool mergeLogs = getProperty("MergeLogs");
139 const int number = getProperty("Number");
141
142 if (eventWs1 && eventWs2) {
143 // Both are event workspaces. Use the special method
144 DataObjects::EventWorkspace_sptr eOutput = this->execEvent(*eventWs1, *eventWs2);
145 for (int i = 1; i < number; i++) {
146 eOutput = this->execEvent(*eOutput, *eventWs2);
147 }
148 output = std::static_pointer_cast<MatrixWorkspace>(eOutput);
149 } else { // So it is a workspace 2D.
150 // The only restriction, even with ValidateInputs=false
151 if (ws1->blocksize() != ws2->blocksize())
152 throw std::runtime_error("Workspace2D's must have the same number of bins.");
153
154 output = execWS2D(*ws1, *ws2);
155 for (int i = 1; i < number; i++) {
156 output = execWS2D(*output, *ws2);
157 }
158 }
159 if (!isDefault("RewriteSpectraMap")) {
160 rewriteSpectraMap(ws1, ws2, output);
161 }
162
163 if (mergeLogs)
164 combineLogs(ws1->run(), ws2->run(), output->mutableRun());
165
166 // Set the output workspace
167 setProperty("OutputWorkspace", std::dynamic_pointer_cast<MatrixWorkspace>(output));
168}
169
179 MatrixWorkspace &output) {
180 specnum_t ws1min;
181 specnum_t ws1max;
182 getMinMax(ws1, ws1min, ws1max);
183
184 specnum_t ws2min;
185 specnum_t ws2max;
186 getMinMax(ws2, ws2min, ws2max);
187
188 const bool appendYAxis = getProperty("AppendYAxisLabels");
189 if (ws2min <= ws1max) {
190 auto indexInfo = output.indexInfo();
191 indexInfo.setSpectrumNumbers(0, static_cast<int32_t>(output.getNumberHistograms() - 1));
192 output.setIndexInfo(indexInfo);
193
194 appendYAxisLabels(ws1, ws2, output);
195 } else if (appendYAxis) {
196 appendYAxisLabels(ws1, ws2, output);
197 }
198}
199
201 const MatrixWorkspace &output) {
202 const auto yAxisNum = 1;
203 const auto yAxisWS1 = ws1.getAxis(yAxisNum);
204 const auto yAxisWS2 = ws2.getAxis(yAxisNum);
205 const auto outputYAxis = output.getAxis(yAxisNum);
206 const auto ws1Len = ws1.getNumberHistograms();
207
208 const bool isSpectra = yAxisWS1->isSpectra() && yAxisWS2->isSpectra();
209 const bool isTextAxis = yAxisWS1->isText() && yAxisWS2->isText();
210 const bool isNumericAxis = yAxisWS1->isNumeric() && yAxisWS2->isNumeric();
211
212 if (!isSpectra && !isTextAxis && !isNumericAxis) {
213 const std::string message(
214 "Y-Axis type mismatch. Ensure that the Y-axis types in both workspaces match. The Y-axis should be set to the "
215 "same type in each workspace, whether it be Numeric, Spectra, or Text.");
216
217 throw std::invalid_argument(message);
218 }
219
220 bool isBinEdgeAxis =
221 dynamic_cast<BinEdgeAxis *>(yAxisWS1) != nullptr && dynamic_cast<BinEdgeAxis *>(yAxisWS2) != nullptr;
222 auto outputTextAxis = dynamic_cast<TextAxis *>(outputYAxis);
223 const auto outputLen = output.getNumberHistograms() + (isBinEdgeAxis ? 1 : 0);
224 for (size_t i = 0; i < outputLen; ++i) {
225 if (isTextAxis) {
226 // check if we're outside the spectra of the first workspace
227 const std::string inputLabel = i < ws1Len ? yAxisWS1->label(i) : yAxisWS2->label(i - ws1Len);
228 outputTextAxis->setLabel(i, !inputLabel.empty() ? inputLabel : "");
229
230 } else if (isNumericAxis) {
231 // check if we're outside the spectra of the first workspace
232 const double inputVal = i < ws1Len ? yAxisWS1->getValue(i) : yAxisWS2->getValue(i - ws1Len);
233 outputYAxis->setValue(i, inputVal);
234 }
235 }
236}
237
239 // No need to worry about ordering here as for Plus - they have to be
240 // different workspaces
241 if (&lhs != &rhs) {
242 ans = lhs;
243 ans += rhs;
244 }
245}
246
248 const MatrixWorkspace_sptr &output) {
249 const size_t lenWs1 = ws1->getNumberHistograms();
250 const size_t totLength = lenWs1 + ws2->getNumberHistograms();
251 const auto &detectorInfo = output->detectorInfo();
252 const auto &pixelIDs = getDetectorsSkippingMonitors(detectorInfo);
253
254 auto &componentInfo = output->mutableComponentInfo();
255 const auto &spInfo1 = ws1->spectrumInfo();
256 const auto &spInfo2 = ws2->spectrumInfo();
257 size_t detIndex = 0;
258 for (size_t i = 0; i < totLength; i++) {
259 if (i < lenWs1 ? spInfo1.isMonitor(i) : spInfo2.isMonitor(i - lenWs1)) {
260 continue;
261 }
262 const auto pos = i < lenWs1 ? spInfo1.position(i) : spInfo2.position(i - lenWs1);
263 const auto detID = pixelIDs.at(detIndex);
264 // should be the same as the component
265 const auto &detIDidx = detectorInfo.indexOf(detID);
266 auto &sp = output->getSpectrum(i);
267 // Do the move
268 componentInfo.setPosition(detIDidx, pos);
269 sp.setDetectorID(detID);
270 detIndex++;
271 }
272}
273} // namespace Mantid::Algorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
const std::vector< double > & rhs
IPeaksWorkspace_sptr workspace
std::map< DeltaEMode::Type, std::string > index
int count
counter
Definition Matrix.cpp:37
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.
bool isDefault(const std::string &name) const
Stores numeric values that are assumed to be bin edge values.
Definition BinEdgeAxis.h:20
Base MatrixWorkspace Abstract Class.
virtual std::size_t getNumberHistograms() const =0
Returns the number of histograms in the workspace.
const Indexing::IndexInfo & indexInfo() const
Returns a const reference to the IndexInfo object of the workspace.
virtual Axis * getAxis(const std::size_t &axisIndex) const
Get a non owning pointer to a workspace axis.
void setIndexInfo(const Indexing::IndexInfo &indexInfo)
Sets the IndexInfo object of the workspace.
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:36
Class to represent a text axis of a workspace.
Definition TextAxis.h:35
A property class for workspaces.
Joins two partial, non-overlapping workspaces into one.
void combineLogs(const API::Run &lhs, const API::Run &rhs, API::Run &ans)
void rewriteSpectraMap(const API::MatrixWorkspace_const_sptr &ws1, const API::MatrixWorkspace_const_sptr &ws2, const API::MatrixWorkspace_sptr &output)
void appendYAxisLabels(const API::MatrixWorkspace &ws1, const API::MatrixWorkspace &ws2, const API::MatrixWorkspace &output)
void fixSpectrumNumbers(const API::MatrixWorkspace &ws1, const API::MatrixWorkspace &ws2, API::MatrixWorkspace &output) override
If there is an overlap in spectrum numbers between ws1 and ws2 or the y axis is a bin edges axis,...
int version() const override
Algorithm's version for identification.
void init() override
Initialize the algorithm's properties.
void exec() override
Execute the algorithm.
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
void checkCompatibility(const API::MatrixWorkspace &ws1, const API::MatrixWorkspace &ws2)
Checks that the two input workspace have the same instrument, unit and distribution flag.
DataObjects::EventWorkspace_sptr execEvent(const DataObjects::EventWorkspace &eventWs1, const DataObjects::EventWorkspace &eventWs2)
Executes the algorithm for event workspace inputs.
void getMinMax(const API::MatrixWorkspace &ws, specnum_t &min, specnum_t &max)
Determine the minimum and maximum spectra ids.
API::MatrixWorkspace_sptr execWS2D(const API::MatrixWorkspace &ws1, const API::MatrixWorkspace &ws2)
Executes the algorithm for histogram workspace inputs.
Geometry::DetectorInfo is an intermediate step towards a DetectorInfo that is part of Instrument-2....
const std::vector< detid_t > & detectorIDs() const
Returns a sorted vector of all detector IDs.
size_t indexOf(const detid_t id) const
Returns the index of the detector with the given detector ID.
bool isMonitor(const size_t index) const
Returns true if the detector is a monitor.
BoundedValidator is a validator that requires the values to be between upper or lower bounds,...
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
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
std::shared_ptr< const EventWorkspace > EventWorkspace_const_sptr
shared pointer to a const Workspace2D
std::shared_ptr< EventWorkspace > EventWorkspace_sptr
shared pointer to the EventWorkspace class
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition EmptyValues.h:24
int32_t detid_t
Typedef for a detector ID.
int32_t specnum_t
Typedef for a spectrum Number.
Definition IDTypes.h:14
STL namespace.
@ Input
An input workspace.
Definition Property.h:54
@ Output
An output workspace.
Definition Property.h:55