Mantid
Loading...
Searching...
No Matches
ConjoinXRuns.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
11#include "MantidAPI/Axis.h"
14#include "MantidAPI/Run.h"
21#include "MantidHistogramData/Histogram.h"
22#include "MantidHistogramData/HistogramDx.h"
23#include "MantidHistogramData/HistogramE.h"
24#include "MantidHistogramData/HistogramX.h"
25#include "MantidHistogramData/HistogramY.h"
26#include "MantidHistogramData/LinearGenerator.h"
32#include "MantidKernel/Unit.h"
34
35namespace Mantid::Algorithms {
36
37using namespace API;
38using namespace Kernel;
39using namespace RunCombinationOptions;
40using namespace DataObjects;
41using namespace HistogramData;
42
43namespace {
44static const std::string INPUT_WORKSPACE_PROPERTY = "InputWorkspaces";
45static const std::string OUTPUT_WORKSPACE_PROPERTY = "OutputWorkspace";
46static const std::string SAMPLE_LOG_X_AXIS_PROPERTY = "SampleLogAsXAxis";
47} // namespace
48
49// Register the algorithm into the AlgorithmFactory
50DECLARE_ALGORITHM(ConjoinXRuns)
51
52const std::string ConjoinXRuns::SUM_MERGE = "conjoin_sample_logs_sum";
53const std::string ConjoinXRuns::TIME_SERIES_MERGE = "conjoin_sample_logs_time_series";
54const std::string ConjoinXRuns::LIST_MERGE = "conjoin_sample_logs_list";
55const std::string ConjoinXRuns::WARN_MERGE = "conjoin_sample_logs_warn";
56const std::string ConjoinXRuns::WARN_MERGE_TOLERANCES = "conjoin_sample_logs_warn_tolerances";
57const std::string ConjoinXRuns::FAIL_MERGE = "conjoin_sample_logs_fail";
58const std::string ConjoinXRuns::FAIL_MERGE_TOLERANCES = "conjoin_sample_logs_fail_tolerances";
59
60//----------------------------------------------------------------------------------------------
61
63const std::string ConjoinXRuns::name() const { return "ConjoinXRuns"; }
64
66int ConjoinXRuns::version() const { return 1; }
67
69const std::string ConjoinXRuns::category() const { return "Transforms\\Merging"; }
70
72const std::string ConjoinXRuns::summary() const {
73 return "Joins the input workspaces horizontally by appending their columns.";
74}
75
76//----------------------------------------------------------------------------------------------
81 std::make_unique<ArrayProperty<std::string>>(INPUT_WORKSPACE_PROPERTY, std::make_shared<ADSValidator>()),
82 "The names of the input workspaces or workspace groups as a list. At "
83 "least two point-data MatrixWorkspaces are "
84 "required, having the same instrument, same number of spectra and "
85 "units.");
86 declareProperty(SAMPLE_LOG_X_AXIS_PROPERTY, "",
87 "The name of the numeric sample log to become the x-axis of the output. "
88 "Empty by default, in which case the x-axis of the input "
89 "workspaces are stitched. "
90 "If specified, this will be the x-axis. It has to be numeric, in which "
91 "case all the input workspaces must have only one point or numeric "
92 "time series, in which case the number "
93 "of elements in the series must match the number of points for each "
94 "workspace.");
95 declareProperty(std::make_unique<WorkspaceProperty<API::Workspace>>(OUTPUT_WORKSPACE_PROPERTY, "", Direction::Output),
96 "The output workspace.");
97
105
106 const std::vector<std::string> failBehaviourOptions = {SKIP_BEHAVIOUR, STOP_BEHAVIOUR};
107 declareProperty("FailBehaviour", SKIP_BEHAVIOUR, std::make_shared<StringListValidator>(failBehaviourOptions),
108 "Choose whether to skip the workspace and continue, or stop and "
109 "throw and error, when encountering a failure on merging.");
111 "LinearizeAxis", false,
112 "Choose to set a linear x-axis starting from 1, can be used only if the workspaces have common bins.");
113}
114
115std::map<std::string, std::string> ConjoinXRuns::validateInputs() {
116 std::map<std::string, std::string> issues;
117
118 const std::vector<std::string> inputs_given = getProperty(INPUT_WORKSPACE_PROPERTY);
119 m_logEntry = getPropertyValue(SAMPLE_LOG_X_AXIS_PROPERTY);
120
121 std::vector<std::string> workspaces;
122 try { // input workspace must be a group or a MatrixWorkspace
123 workspaces = RunCombinationHelper::unWrapGroups(inputs_given);
124 } catch (const std::exception &e) {
125 issues[INPUT_WORKSPACE_PROPERTY] = std::string(e.what());
126 }
127
128 // find if there are grouped workspaces that are not Matrix or not a
129 // point-data
130 for (const auto &input : workspaces) {
132 if (!ws) {
133 issues[INPUT_WORKSPACE_PROPERTY] += "Workspace " + input + " is not a MatrixWorkspace\n";
134 } else if (ws->isHistogramData()) {
135 issues[INPUT_WORKSPACE_PROPERTY] += "Workspace " + ws->getName() + " is not a point-data\n";
136 } else {
137 try {
138 ws->blocksize();
139 } catch (std::length_error &) {
140 issues[INPUT_WORKSPACE_PROPERTY] +=
141 "Workspace " + ws->getName() + " has different number of points per histogram\n";
142 }
143 m_inputWS.emplace_back(ws);
144 }
145 }
146
147 if (m_inputWS.empty()) {
148 issues[INPUT_WORKSPACE_PROPERTY] += "There are no point-data"
149 " MatrixWorkspaces in the input list\n";
150 } else {
151 RunCombinationHelper combHelper;
152 combHelper.setReferenceProperties(m_inputWS.front());
153
154 for (const auto &ws : m_inputWS) {
155 // check if all the others are compatible with the first one
156 std::string compatible = combHelper.checkCompatibility(ws, true);
157 if (!compatible.empty()) {
158 issues[INPUT_WORKSPACE_PROPERTY] += "Workspace " + ws->getName() + " is not compatible: " + compatible + "\n";
159 }
160 // if the log entry is given, validate it
161 const std::string logValid = checkLogEntry(ws);
162 if (!logValid.empty()) {
163 issues[INPUT_WORKSPACE_PROPERTY] += "Invalid sample log entry for " + ws->getName() + ": " + logValid + "\n";
164 }
165 }
166 }
167 if (getProperty("LinearizeAxis")) {
168 auto notCommon = std::find_if(m_inputWS.cbegin(), m_inputWS.cend(), [](auto ws) { return !ws->isCommonBins(); });
169 if (notCommon != m_inputWS.cend()) {
170 issues[INPUT_WORKSPACE_PROPERTY] +=
171 "Workspace " + (*notCommon)->getName() + " is ragged, which is not allowed if linearize axis is requested.\n";
172 }
173 }
174 m_inputWS.clear();
175
176 return issues;
177}
178
179//----------------------------------------------------------------------------------------------
186 std::string result;
187 if (!m_logEntry.empty()) {
188
189 const auto &run = ws->run();
190
191 if (!run.hasProperty(m_logEntry)) {
192 result = "Log entry does not exist";
193 } else {
194 try {
195 run.getLogAsSingleValue(m_logEntry);
196
197 // try if numeric time series, then the size must match to the
198 // blocksize
199 const auto blocksize = static_cast<int>(ws->y(0).size());
200
201 TimeSeriesProperty<double> *timeSeriesDouble(nullptr);
202 TimeSeriesProperty<int> *timeSeriesInt(nullptr);
203 timeSeriesDouble = dynamic_cast<TimeSeriesProperty<double> *>(run.getLogData(m_logEntry));
204 timeSeriesInt = dynamic_cast<TimeSeriesProperty<int> *>(run.getLogData(m_logEntry));
205
206 if (timeSeriesDouble) {
207 if (blocksize != timeSeriesDouble->size()) {
208 result = "Size of the double time series does not match the blocksize";
209 }
210 } else if (timeSeriesInt) {
211 if (blocksize != timeSeriesInt->size()) {
212 result = "Size of the int time series does not match the blocksize";
213 }
214 } else {
215 // if numeric scalar, must have one bin
216 if (ws->blocksize() != 1) {
217 result = "One bin workspaces is required if the log is numeric scalar";
218 }
219 }
220 } catch (std::invalid_argument &) {
221 result = "Log entry must be numeric or numeric time series";
222 }
223 }
224 }
225 return result;
226}
227
228//----------------------------------------------------------------------------------------------
234std::vector<double> ConjoinXRuns::getXAxis(const MatrixWorkspace_sptr &ws, double &xstart) const {
235 std::vector<double> axis;
236 const size_t s = ws->y(0).size();
237 axis.reserve(s);
238 auto &run = ws->run();
239 if (getProperty("LinearizeAxis")) {
240 for (double ix = xstart; ix < xstart + double(s); ++ix) {
241 axis.push_back(ix);
242 }
243 xstart += double(s);
244 } else {
245 // try time series first
246 TimeSeriesProperty<double> *timeSeriesDouble(nullptr);
247 timeSeriesDouble = dynamic_cast<TimeSeriesProperty<double> *>(run.getLogData(m_logEntry));
248 if (timeSeriesDouble) {
249 // try double series
250 axis = timeSeriesDouble->filteredValuesAsVector();
251 } else {
252 // try int series next
253 TimeSeriesProperty<int> *timeSeriesInt(nullptr);
254 timeSeriesInt = dynamic_cast<TimeSeriesProperty<int> *>(run.getLogData(m_logEntry));
255 if (timeSeriesInt) {
256 std::vector<int> intAxis = timeSeriesInt->filteredValuesAsVector();
257 axis = std::vector<double>(intAxis.begin(), intAxis.end());
258 } else {
259 // then scalar
260 axis.emplace_back(run.getPropertyAsSingleValue(m_logEntry));
261 }
262 }
263 }
264
265 return axis;
266}
267
268//----------------------------------------------------------------------------------------------
272 // If this is not a child algorithm add the history
273 if (!isChild()) {
274 // Loop over the input workspaces, making the call that copies their
275 // history to the output one
276 for (auto &inWS : m_inputWS) {
277 m_outWS->history().addHistory(inWS->getHistory());
278 }
279 // Add the history for the current algorithm to the output
280 m_outWS->history().addHistory(m_history);
281 }
282 // this is a child algorithm, but we still want to keep the history.
284 m_parentHistory->addChildHistory(m_history);
285 }
286}
287
288//----------------------------------------------------------------------------------------------
292void ConjoinXRuns::joinSpectrum(int64_t wsIndex) {
293 std::vector<double> spectrum;
294 std::vector<double> errors;
295 std::vector<double> axis;
296 std::vector<double> xerrors;
297 const auto index = static_cast<size_t>(wsIndex);
298 const auto ySize = m_outWS->y(index).size();
299 spectrum.reserve(ySize);
300 errors.reserve(ySize);
301 axis.reserve(ySize);
302 xerrors.reserve(ySize);
303 for (const auto &input : m_inputWS) {
304 const auto &y = input->y(index);
305 spectrum.insert(spectrum.end(), y.begin(), y.end());
306 const auto &e = input->e(index);
307 errors.insert(errors.end(), e.begin(), e.end());
308 if (!m_logEntry.empty() || getProperty("LinearizeAxis")) {
309 const auto &x = m_axisCache[input->getName()];
310 axis.insert(axis.end(), x.begin(), x.end());
311 } else {
312 const auto &x = input->x(index);
313 axis.insert(axis.end(), x.begin(), x.end());
314 }
315 if (input->hasDx(index)) {
316 const auto &dx = input->dx(index);
317 xerrors.insert(xerrors.end(), dx.begin(), dx.end());
318 }
319 }
320 m_outWS->setPoints(index, std::move(axis));
321 m_outWS->setCounts(index, std::move(spectrum));
322 m_outWS->setCountStandardDeviations(index, std::move(errors));
323 if (!xerrors.empty())
324 m_outWS->setPointStandardDeviations(index, std::move(xerrors));
325}
326
327//----------------------------------------------------------------------------------------------
331
332 const std::vector<std::string> inputs_given = getProperty(INPUT_WORKSPACE_PROPERTY);
333 m_logEntry = getPropertyValue(SAMPLE_LOG_X_AXIS_PROPERTY);
334
343 const std::string sampleLogsFailBehaviour = getProperty("FailBehaviour");
344
345 m_inputWS.clear();
346
347 for (const auto &input : RunCombinationHelper::unWrapGroups(inputs_given)) {
349 m_inputWS.emplace_back(ws);
350 }
351
352 auto first = m_inputWS.front();
353
361
362 SampleLogsBehaviour sampleLogsBehaviour = SampleLogsBehaviour(first, g_log, logEntries, parName);
363 auto it = m_inputWS.begin();
364
365 // Temporary workspace to carry the merged sample logs
366 // This is cloned from the first workspace and does not have
367 // the correct size to be the output, since the size is unknown
368 // at this point. We can check only later which ones are going
369 // to be skipped, to compute the size of the output respectively.
370 MatrixWorkspace_sptr temp = first->clone();
371
372 size_t outBlockSize = (*it)->y(0).size();
373 // First sequentially merge the sample logs
374 for (++it; it != m_inputWS.end(); ++it) {
375 // attempt to merge the sample logs
376 try {
377 sampleLogsBehaviour.mergeSampleLogs(*it, temp);
378 sampleLogsBehaviour.setUpdatedSampleLogs(temp);
379 outBlockSize += (*it)->y(0).size();
380 } catch (std::invalid_argument &e) {
381 if (sampleLogsFailBehaviour == SKIP_BEHAVIOUR) {
382 g_log.error() << "Could not join workspace: " << (*it)->getName() << ". Reason: \"" << e.what()
383 << "\". Skipping.\n";
384 sampleLogsBehaviour.resetSampleLogs(temp);
385 // remove the skipped one from the list
386 it = m_inputWS.erase(it);
387 --it;
388 } else {
389 throw std::invalid_argument(e);
390 }
391 }
392 }
393
394 if (m_inputWS.size() == 1) {
395 g_log.warning() << "Nothing left to join [after skipping the workspaces "
396 "that failed to merge the sample logs].";
397 // note, we need to continue still, since
398 // the x-axis might need to be changed
399 }
400
401 if (!m_logEntry.empty() || getProperty("LinearizeAxis")) {
402 double xstart = 0;
403 for (const auto &ws : m_inputWS) {
404 m_axisCache[ws->getName()] = getXAxis(ws, xstart);
405 }
406 }
407
408 // now get the size of the output
409 size_t numSpec = first->getNumberHistograms();
410
411 m_outWS = create<MatrixWorkspace>(*first, Points(outBlockSize));
412
413 // copy over the merged sample logs from the temp
414 m_outWS->mutableRun() = temp->run();
415
416 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, numSpec);
417
418 // Now loop in parallel over all the spectra and join the data
420 for (int64_t index = 0; index < static_cast<int64_t>(numSpec); ++index) {
423 m_progress->report();
425 }
427
428 if (!m_logEntry.empty()) {
429 std::string unit = first->run().getLogData(m_logEntry)->units();
430 try {
431 m_outWS->getAxis(0)->unit() = UnitFactory::Instance().create(unit);
432 } catch (Exception::NotFoundError &) {
433 m_outWS->getAxis(0)->unit() = UnitFactory::Instance().create("Empty");
434 }
435 }
436
437 setProperty("OutputWorkspace", m_outWS);
438 m_inputWS.clear();
439 m_axisCache.clear();
440}
441} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
#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...
Definition: MultiThreaded.h:94
#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.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
std::shared_ptr< AlgorithmHistory > m_parentHistory
Pointer to the parent history object (if set)
Definition: Algorithm.h:454
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
std::shared_ptr< AlgorithmHistory > m_history
Pointer to the history for the algorithm being executed.
Definition: Algorithm.h:447
bool isChild() const override
To query whether algorithm is a child.
Definition: Algorithm.cpp:160
Kernel::Logger & g_log
Definition: Algorithm.h:451
bool isRecordingHistoryForChild()
Definition: Algorithm.h:234
Base MatrixWorkspace Abstract Class.
A property class for workspaces.
const std::string & getName() const override
Get the workspace name.
Definition: Workspace.cpp:58
static const std::string WARN_MERGE
Definition: ConjoinXRuns.h:33
std::vector< double > getXAxis(const API::MatrixWorkspace_sptr &, double &) const
Return the to-be axis of the workspace dependent on the log entry.
static const std::string WARN_MERGE_TOLERANCES
Definition: ConjoinXRuns.h:34
void exec() override
Execute the algorithm.
std::unique_ptr< API::Progress > m_progress
Progress reporting.
Definition: ConjoinXRuns.h:52
static const std::string SUM_MERGE
ConjoinXRuns parameter names of the paramter file for sample log merging.
Definition: ConjoinXRuns.h:30
static const std::string TIME_SERIES_MERGE
Definition: ConjoinXRuns.h:31
std::string m_logEntry
Sample log entry name.
Definition: ConjoinXRuns.h:50
static const std::string FAIL_MERGE
Definition: ConjoinXRuns.h:35
std::list< API::MatrixWorkspace_sptr > m_inputWS
List of input matrix workspaces.
Definition: ConjoinXRuns.h:54
std::string checkLogEntry(const API::MatrixWorkspace_sptr &) const
Check if the log entry is valid.
const std::string category() const override
Algorithm's category for identification.
void fillHistory() override
Makes up the correct history of the output workspace.
const std::string name() const override
Algorithms name for identification.
static const std::string LIST_MERGE
Definition: ConjoinXRuns.h:32
void joinSpectrum(int64_t)
Joins the given spectrum for the list of workspaces.
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
static const std::string FAIL_MERGE_TOLERANCES
Definition: ConjoinXRuns.h:36
int version() const override
Algorithm's version for identification.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
void init() override
Initialize the algorithm's properties.
API::MatrixWorkspace_sptr m_outWS
Output workspace.
Definition: ConjoinXRuns.h:56
std::map< std::string, std::vector< double > > m_axisCache
X-axis cache if sample log is given.
Definition: ConjoinXRuns.h:58
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.
SampleLogsBehaviour : This class holds information relating to the behaviour of the sample log mergin...
void setUpdatedSampleLogs(const API::MatrixWorkspace_sptr &outWS)
Set the values in the map to be the same as those in the output workspace.
void resetSampleLogs(const API::MatrixWorkspace_sptr &ws)
Resets the sample logs in the workspace to the values in the map.
void mergeSampleLogs(const API::MatrixWorkspace_sptr &addeeWS, const API::MatrixWorkspace_sptr &outWS)
Create and update sample logs according to instrument parameters.
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
Exception for when an item is not found in a collection.
Definition: Exception.h:145
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
A specialised Property class for holding a series of time-value pairs.
int size() const override
Returns the number of values at UNIQUE time intervals in the time series.
std::vector< TYPE > filteredValuesAsVector() const
Get filtered values as a vector.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
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.
Definition: MultiThreaded.h:22
@ Output
An output workspace.
Definition: Property.h:54