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