Mantid
Loading...
Searching...
No Matches
Stitch1DMany.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 +
19#include <memory>
20
21using namespace Mantid::Kernel;
22using namespace Mantid::API;
23
24namespace Mantid::Algorithms {
25DECLARE_ALGORITHM(Stitch1DMany)
26
27
28void Stitch1DMany::init() {
29
30 declareProperty(std::make_unique<ArrayProperty<std::string>>("InputWorkspaces", std::make_shared<ADSValidator>()),
31 "List or group of MatrixWorkspaces");
32
33 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "", Direction::Output),
34 "Stitched workspace.");
35
36 declareProperty(std::make_unique<ArrayProperty<std::string>>("OutputWorkspaceSuffixes", Direction::Input),
37 "Optional suffixes to use for child output workspaces when stitching workspace groups. "
38 "The default is to use 1-based numeric suffixes.");
39
40 declareProperty(
41 std::make_unique<ArrayProperty<double>>("Params", std::make_shared<RebinParamsValidator>(true), Direction::Input),
42 "Rebinning Parameters, see Rebin algorithm for format.");
43
44 declareProperty(std::make_unique<ArrayProperty<double>>("StartOverlaps", Direction::Input),
45 "Start overlaps for stitched workspaces "
46 "(number of input workspaces minus one).");
47
48 declareProperty(std::make_unique<ArrayProperty<double>>("EndOverlaps", Direction::Input),
49 "End overlaps for stitched workspaces "
50 "(number of input workspaces minus one).");
51
52 // This property is allowed to be Unset, as it no longer has an effect
53 declareProperty(
54 std::make_unique<PropertyWithValue<OptionalBool>>("ScaleRHSWorkspace", OptionalBool::Unset, Direction::Input),
55 "Scaling either with respect to first (first hand side, LHS) "
56 "or second (right hand side, RHS) workspace. "
57 "This property no longer has an effect, please use the IndexOfReference property instead.");
58 // Hide this property from the GUI as it now has no effect and will eventually be removed
59 setPropertySettings("ScaleRHSWorkspace", std::make_unique<InvisibleProperty>());
60
61 declareProperty(std::make_unique<PropertyWithValue<bool>>("UseManualScaleFactors", false, Direction::Input),
62 "True to use provided values for the scale factor.");
63
64 declareProperty(std::make_unique<ArrayProperty<double>>("ManualScaleFactors", Direction::Input),
65 "Either a single scale factor which will be applied to all "
66 "input workspaces or individual scale factors "
67 "(number of input workspaces minus one)");
68 setPropertySettings("ManualScaleFactors",
69 std::make_unique<VisibleWhenProperty>("UseManualScaleFactors", IS_EQUAL_TO, "1"));
70
71 declareProperty(std::make_unique<ArrayProperty<double>>("OutScaleFactors", Direction::Output),
72 "The actual used values for the scaling factors at each stitch step.");
73
74 auto scaleFactorFromPeriodValidator = std::make_shared<BoundedValidator<int>>();
75 scaleFactorFromPeriodValidator->setLower(1);
76 declareProperty(std::make_unique<PropertyWithValue<int>>("ScaleFactorFromPeriod", 1, scaleFactorFromPeriodValidator,
78 "Provided index of period to obtain scale factor from; "
79 "periods are indexed from 1 and used only if stitching group "
80 "workspaces, UseManualScaleFactors is true and "
81 "ManualScaleFactors is set to default.");
82
83 auto useManualScaleFactorsTrue = VisibleWhenProperty("UseManualScaleFactors", IS_EQUAL_TO, "1");
84 auto manualScaleFactorsDefault = VisibleWhenProperty("ManualScaleFactors", IS_DEFAULT);
85 auto scaleFactorFromPeriodVisible =
86 std::make_unique<VisibleWhenProperty>(useManualScaleFactorsTrue, manualScaleFactorsDefault, AND);
87
88 setPropertySettings("ScaleFactorFromPeriod", std::move(scaleFactorFromPeriodVisible));
89
90 auto indexValidator = std::make_shared<BoundedValidator<int>>();
91 indexValidator->setLower(-1);
92 declareProperty("IndexOfReference", 0, indexValidator,
93 "Index of the workspace to be used as reference for scaling, or -1 to choose the last workspace as "
94 "the reference.");
95
96 declareProperty(std::make_unique<PropertyWithValue<bool>>("UseValidDataOnly", false, Direction::Input),
97 "If true, invalid signal values do not contribute to overlap bins where another workspace has valid "
98 "signal values.");
99}
100
102std::map<std::string, std::string> Stitch1DMany::validateInputs() {
103 std::map<std::string, std::string> issues;
104 const std::vector<std::string> inputWorkspacesStr = this->getProperty("InputWorkspaces");
105 if (inputWorkspacesStr.size() < 2)
106 issues["InputWorkspaces"] = "Nothing to stitch";
107 else {
108 try { // input workspaces must be group or MatrixWorkspaces
109 auto workspaces = RunCombinationHelper::unWrapGroups(inputWorkspacesStr);
110 /*
111 * Column: one column of MatrixWorkspaces or several columns of
112 * MatrixWorkspaces from a group
113 * Row: each period only for groups
114 */
115 m_inputWSMatrix.reserve(inputWorkspacesStr.size());
119 m_inputWSMatrix.clear();
120 std::vector<MatrixWorkspace_sptr> column;
121 for (const auto &ws : inputWorkspacesStr) {
122 auto groupWS = AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(ws);
123 if (groupWS) {
124 for (size_t i = 0; i < groupWS->size(); i++) {
125 auto inputMatrix = std::dynamic_pointer_cast<MatrixWorkspace>(groupWS->getItem(i));
126 if (inputMatrix) {
127 column.emplace_back(inputMatrix);
128 } else
129 issues["InputWorkspaces"] = "Input workspace " + ws + " must be a MatrixWorkspace";
130 }
131 m_inputWSMatrix.emplace_back(column);
132 column.clear();
133 } else {
134 auto inputMatrix = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(ws);
135 column.emplace_back(inputMatrix);
136 }
137 }
138 if (!isDefault("IndexOfReference")) {
139 m_indexOfReference = static_cast<int>(this->getProperty("IndexOfReference"));
140 if (m_indexOfReference > -1 && static_cast<size_t>(m_indexOfReference) >= column.size() &&
141 static_cast<size_t>(m_indexOfReference) >= m_inputWSMatrix.size()) {
142 issues["IndexOfReference"] = "The index of reference workspace is larger than the number of "
143 "provided workspaces.";
144 }
145 }
146
147 if (m_inputWSMatrix.empty()) { // no group workspaces
148 // A column of matrix workspaces will be stitched
149 if (!isDefault("OutputWorkspaceSuffixes"))
150 issues["OutputWorkspaceSuffixes"] = "OutputWorkspaceSuffixes can only be used with group workspaces";
151 RunCombinationHelper combHelper;
152 combHelper.setReferenceProperties(column.front());
153 for (const auto &ws : column) {
154 // check if all the others are compatible with the reference
155 std::string compatible = combHelper.checkCompatibility(ws, true);
156 if (!compatible.empty()) {
157 if (!(compatible == "spectra must have either Dx values or not; ") ||
158 (ws->isHistogramData())) // Issue only for point data
159 issues["RHSWorkspace"] = "Workspace " + ws->getName() + " is not compatible: " + compatible + "\n";
160 }
161 }
162 m_inputWSMatrix.emplace_back(column);
163 } else if (m_inputWSMatrix.size() != inputWorkspacesStr.size()) { // not only group workspaces
164 issues["InputWorkspaces"] = "All input workspaces must be groups";
165 } else { // only group workspaces
166 m_outputWorkspaceSuffixes = this->getProperty("OutputWorkspaceSuffixes");
167 const std::unordered_set<std::string> suffixSet(m_outputWorkspaceSuffixes.cbegin(),
169 if (!m_outputWorkspaceSuffixes.empty() && m_outputWorkspaceSuffixes.size() != m_inputWSMatrix.front().size()) {
170 issues["OutputWorkspaceSuffixes"] =
171 "Expected " + std::to_string(m_inputWSMatrix.front().size()) + " suffix(es)";
172 } else if (suffixSet.size() != m_outputWorkspaceSuffixes.size()) {
173 issues["OutputWorkspaceSuffixes"] = "Suffixes must be unique";
174 }
175
176 // Each row of matrix workspaces will be stitched
177 for (size_t spec = 1; spec < m_inputWSMatrix.front().size(); ++spec) {
178 for (const auto &ws : m_inputWSMatrix) {
179 if (ws.size() != m_inputWSMatrix.front().size()) {
180 issues["InputWorkspaces"] = "Size mismatch of group workspaces";
181 } else {
182 RunCombinationHelper combHelper;
183 combHelper.setReferenceProperties(ws[0]);
184 // check if all the others are compatible with the reference
185 std::string compatible = combHelper.checkCompatibility(ws[spec], true);
186 if (!compatible.empty())
187 issues["InputWorkspaces"] =
188 "Workspace " + ws[spec]->getName() + " is not compatible: " + compatible + "\n";
189 }
190 }
191 }
192 int scaleFactorFromPeriod = this->getProperty("ScaleFactorFromPeriod");
193 // Period -1 corresponds to workspace index
194 m_scaleFactorFromPeriod = static_cast<size_t>(--scaleFactorFromPeriod);
195 if (m_scaleFactorFromPeriod >= m_inputWSMatrix.front().size()) {
196 std::stringstream expectedRange;
197 expectedRange << m_inputWSMatrix.front().size() + 1;
198 issues["ScaleFactorFromPeriod"] = "Period index out of range, must be smaller than " + expectedRange.str();
199 }
200 }
201
202 m_startOverlaps = this->getProperty("StartOverlaps");
203 m_endOverlaps = this->getProperty("EndOverlaps");
204 m_params = this->getProperty("Params");
205
206 // Either stitch MatrixWorkspaces or workspaces of the group
207 size_t numStitchableWS =
208 (workspaces.size() == inputWorkspacesStr.size()) ? workspaces.size() : inputWorkspacesStr.size();
209 std::stringstream expectedVal;
210 expectedVal << numStitchableWS - 1;
211 if (!m_startOverlaps.empty() && m_startOverlaps.size() != numStitchableWS - 1)
212 issues["StartOverlaps"] = "Expected " + expectedVal.str() + " value(s)";
213
214 if (m_startOverlaps.size() != m_endOverlaps.size())
215 issues["EndOverlaps"] = "EndOverlaps must have the same number of "
216 "entries as StartOverlaps.";
217
218 m_useManualScaleFactors = this->getProperty("UseManualScaleFactors");
219 m_manualScaleFactors = this->getProperty("ManualScaleFactors");
220 m_useValidDataOnly = this->getProperty("UseValidDataOnly");
221
222 if (!m_manualScaleFactors.empty()) {
223 if (m_manualScaleFactors.size() == 1) {
224 // Single value: fill with list of the same scale factor value
225 m_manualScaleFactors = std::vector<double>(numStitchableWS - 1, m_manualScaleFactors.front());
226 } else if (m_manualScaleFactors.size() != numStitchableWS - 1) {
227 if ((numStitchableWS - 1) == 1)
228 issues["ManualScaleFactors"] = "Must be a single value";
229 else
230 issues["ManualScaleFactors"] =
231 "Must be a single value for all input workspaces or " + expectedVal.str() + " values";
232 }
233 } else { // if not a group, no period scaling possible
234 if (m_useManualScaleFactors && (m_inputWSMatrix.size() == 1))
235 issues["ManualScaleFactors"] = "Must contain scale factors";
236 }
237 } catch (const std::exception &e) {
238 issues["InputWorkspaces"] = std::string(e.what());
239 }
240 }
241 return issues;
242}
243
246 if (static_cast<OptionalBool>(this->getProperty("ScaleRHSWorkspace")) != OptionalBool::Unset) {
247 g_log.warning("The ScaleRHSWorkspace property no longer has any effect. Please see documentation on the "
248 "IndexOfReference parameter and use that instead.");
249 }
250 m_useValidDataOnly = this->getProperty("UseValidDataOnly");
251
252 if (m_inputWSMatrix.size() > 1) { // groups
253 std::vector<std::string> toGroup; // List of workspaces to be grouped
254 std::string groupName = this->getProperty("OutputWorkspace");
255 std::string outName;
256
257 // Determine whether or not we are scaling workspaces using scale
258 // factors from a specific period
259 const bool usingScaleFromPeriod = m_useManualScaleFactors && isDefault("ManualScaleFactors");
260 if (!usingScaleFromPeriod) {
261 for (size_t i = 0; i < m_inputWSMatrix.front().size(); ++i) {
262
263 outName = groupName;
264 std::vector<double> scaleFactors;
266 // Add the resulting workspace to the list to be grouped together
267 toGroup.emplace_back(outName);
268
269 // Add the scalefactors to the list so far
270 m_scaleFactors.insert(m_scaleFactors.end(), scaleFactors.begin(), scaleFactors.end());
271 }
272 } else {
273 // Obtain scale factors for the specified period
274 std::string tempOutName;
275 std::vector<double> periodScaleFactors;
276 constexpr bool storeInADS = false;
277
278 doStitch1DMany(m_scaleFactorFromPeriod, false, tempOutName, periodScaleFactors, m_indexOfReference, storeInADS);
279
280 // Iterate over each period
281 for (size_t i = 0; i < m_inputWSMatrix.front().size(); ++i) {
282 std::vector<MatrixWorkspace_sptr> inMatrix;
283 inMatrix.reserve(m_inputWSMatrix.size());
284
285 std::transform(m_inputWSMatrix.begin(), m_inputWSMatrix.end(), std::back_inserter(inMatrix),
286 [i](const auto &ws) { return ws[i]; });
287
288 Workspace_sptr outStitchedWS;
289 doStitch1D(inMatrix, periodScaleFactors, outStitchedWS);
290 // Add name of stitched workspaces to group list and ADS
291 outName = createChildWorkspaceName(groupName, i);
292 toGroup.emplace_back(outName);
293 AnalysisDataService::Instance().addOrReplace(outName, outStitchedWS);
294 }
295 }
296
297 auto groupAlg = createChildAlgorithm("GroupWorkspaces");
298 groupAlg->initialize();
299 groupAlg->setAlwaysStoreInADS(true);
300 groupAlg->setProperty("InputWorkspaces", toGroup);
301 groupAlg->setProperty("OutputWorkspace", groupName);
302 groupAlg->execute();
303
304 m_outputWorkspace = AnalysisDataService::Instance().retrieveWS<Workspace>(groupName);
305 } else {
307 }
308 // Save output
309 this->setProperty("OutputWorkspace", m_outputWorkspace);
310 this->setProperty("OutScaleFactors", m_scaleFactors);
311}
312
318void Stitch1DMany::doStitch1D(std::vector<MatrixWorkspace_sptr> &toStitch,
319 const std::vector<double> &manualScaleFactors, Workspace_sptr &outWS) {
320
321 auto &lhsWS = toStitch.front();
322 // Support Python list syntax for selecting the last element in the list
323 auto indexOfReference = m_indexOfReference == -1 ? toStitch.size() - 1 : m_indexOfReference;
324
325 for (size_t i = 1; i < toStitch.size(); i++) {
326 auto rhsWS = toStitch[i];
327 // Scale the LHS ws until we have scaled to the reference ws. After that we scale the RHS ws to keep the scaling.
328 auto scaleRHSWorkspace = i > indexOfReference;
329
330 auto alg = createChildAlgorithm("Stitch1D");
331 alg->initialize();
332 alg->setProperty("LHSWorkspace", lhsWS);
333 alg->setProperty("RHSWorkspace", rhsWS);
334 if (m_startOverlaps.size() > i - 1) {
335 alg->setProperty("StartOverlap", m_startOverlaps[i - 1]);
336 alg->setProperty("EndOverlap", m_endOverlaps[i - 1]);
337 }
338 alg->setProperty("Params", m_params);
339 alg->setProperty("ScaleRHSWorkspace", scaleRHSWorkspace);
340 alg->setProperty("UseManualScaleFactor", m_useManualScaleFactors);
341 alg->setProperty("UseValidDataOnly", m_useValidDataOnly);
343 alg->setProperty("ManualScaleFactor", manualScaleFactors[i - 1]);
344 alg->execute();
345
346 lhsWS = alg->getProperty("OutputWorkspace");
347 double outScaleFactor = alg->getProperty("OutScaleFactor");
348 m_scaleFactors.emplace_back(outScaleFactor);
349
350 if (!isChild()) {
351 // Copy each input workspace's history into our output workspace's
352 // history
353 for (const auto &inputWS : toStitch) {
354 lhsWS->history().addHistory(inputWS->getHistory());
355 }
356 }
357 }
358
359 outWS = lhsWS;
360}
361
371void Stitch1DMany::doStitch1DMany(const size_t period, const bool useManualScaleFactors, std::string &outName,
372 std::vector<double> &outScaleFactors, const int indexOfReference,
373 const bool storeInADS) {
374
375 // List of workspaces to stitch
376 std::vector<std::string> toProcess;
377
378 for (const auto &ws : m_inputWSMatrix) {
379 const std::string &wsName = ws[period]->getName();
380 toProcess.emplace_back(wsName);
381 }
382
383 outName = createChildWorkspaceName(outName, period);
384
385 auto alg = createChildAlgorithm("Stitch1DMany");
386 alg->initialize();
387 alg->setAlwaysStoreInADS(storeInADS);
388 alg->setProperty("InputWorkspaces", toProcess);
389 if (!outName.empty())
390 alg->setProperty("OutputWorkspace", outName);
391 alg->setProperty("StartOverlaps", m_startOverlaps);
392 alg->setProperty("EndOverlaps", m_endOverlaps);
393 alg->setProperty("Params", m_params);
394 alg->setProperty("UseManualScaleFactors", useManualScaleFactors);
395 alg->setProperty("UseValidDataOnly", m_useValidDataOnly);
396 if (useManualScaleFactors)
397 alg->setProperty("ManualScaleFactors", m_manualScaleFactors);
398 alg->setProperty("IndexOfReference", indexOfReference);
399 alg->execute();
400
401 outScaleFactors = alg->getProperty("OutScaleFactors");
402}
403
408std::string Stitch1DMany::createChildWorkspaceName(const std::string &groupName, const size_t periodIndex) {
409 if (!m_outputWorkspaceSuffixes.empty())
410 return groupName + "_" + m_outputWorkspaceSuffixes[periodIndex];
411
412 return groupName + "_" + std::to_string(periodIndex + 1);
413}
414
415} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
std::string getName(const IMDDimension &self)
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.
bool isChild() const override
To query whether algorithm is a child.
Kernel::Logger & g_log
Definition Algorithm.h:423
bool isDefault(const std::string &name) const
Base MatrixWorkspace Abstract Class.
Class to hold a set of workspaces.
A property class for workspaces.
Base Workspace Abstract Class.
Definition Workspace.h:29
static std::vector< std::string > unWrapGroups(const std::vector< std::string > &)
Flattens the list of group workspaces (if any) into list of 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.
Stitch1DMany : Stitches multiple Matrix Workspaces together into a single output.
void exec() override
Overwrites Algorithm method.
std::vector< double > m_scaleFactors
std::vector< std::vector< API::MatrixWorkspace_sptr > > m_inputWSMatrix
std::vector< double > m_params
API::Workspace_sptr m_outputWorkspace
std::vector< double > m_endOverlaps
std::vector< std::string > m_outputWorkspaceSuffixes
void doStitch1D(std::vector< API::MatrixWorkspace_sptr > &toStitch, const std::vector< double > &manualScaleFactors, API::Workspace_sptr &outWS)
Performs the Stitch1D algorithm at a specific workspace index.
std::vector< double > m_startOverlaps
std::map< std::string, std::string > validateInputs() override
Validates algorithm inputs.
std::vector< double > m_manualScaleFactors
void doStitch1DMany(const size_t period, const bool useManualScaleFactors, std::string &outName, std::vector< double > &outScaleFactors, const int indexOfReference, const bool storeInADS=true)
Performs the Stitch1DMany algorithm at a specific period.
std::string createChildWorkspaceName(const std::string &groupName, const size_t periodIndex)
Creates a correctly formatted name for a stitched child workspace in a workspace group.
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 warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
OptionalBool : Tri-state bool.
The concrete, templated class for properties.
Same as EnabledWhenProperty, but returns the value for the isVisible() property instead of the isEnab...
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54