Mantid
Loading...
Searching...
No Matches
WorkflowAlgorithmRunner.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 "MantidKernel/V3D.h"
12
13#include <deque>
14#include <unordered_map>
15
16using namespace Mantid::API;
17using namespace Mantid::Kernel;
18
19namespace Mantid::Algorithms {
20
21namespace PropertyNames {
22const static std::string ALGORITHM("Algorithm");
23const static std::string IO_MAP("InputOutputMap");
24const static std::string SETUP_TABLE("SetupTable");
25} // namespace PropertyNames
26
31bool isHardCodedWorkspaceName(const std::string &s) {
32 if (s.size() < 3) {
33 return false;
34 }
35 const auto f = s.front();
36 const auto b = s.back();
37 return (f == '\'' && b == '\'') || (f == '"' && b == '"');
38}
39
45std::string tidyWorkspaceName(const std::string &s) {
46 if (s.length() < 3) {
47 throw std::runtime_error("Workspace name is too short.");
48 }
49 return std::string(s.cbegin() + 1, s.cend() - 1);
50}
51
61template <typename MAP> void cleanPropertyTable(const ITableWorkspace_sptr &table, const MAP &ioMapping) {
62 // Some output columns may be processed several times, but this should
63 // not be a serious performance hit.
64 for (const auto &ioPair : ioMapping) {
65 Column_sptr inputColumn = table->getColumn(ioPair.first);
66 Column_sptr outputColumn = table->getColumn(ioPair.second);
67 for (size_t i = 0; i < table->rowCount(); ++i) {
68 inputColumn->cell<std::string>(i).clear();
69 auto &outputValue = outputColumn->cell<std::string>(i);
70 if (!isHardCodedWorkspaceName(outputValue)) {
71 outputValue.clear();
72 }
73 }
74 }
75 // Second pass: tidy hard-coded outputs.
76 // This could not be done in first pass as multiple inputs may depend
77 // on single output and then the hard-coded values would be cleared.
78 for (const auto &ioPair : ioMapping) {
79 Column_sptr outputColumn = table->getColumn(ioPair.second);
80 for (size_t i = 0; i < table->rowCount(); ++i) {
81 auto &outputValue = outputColumn->cell<std::string>(i);
82 if (isHardCodedWorkspaceName(outputValue)) {
83 outputValue = tidyWorkspaceName(outputValue);
84 }
85 }
86 }
87}
88
89// Register the algorithm into the algorithm factory.
90DECLARE_ALGORITHM(WorkflowAlgorithmRunner)
91
93 declareProperty(PropertyNames::ALGORITHM, "", std::make_shared<MandatoryValidator<std::string>>(),
94 "Name of the algorithm to run");
95 declareProperty(
97 "Table workspace containing the setup of the runs.");
98 declareProperty(
100 "Table workspace mapping algorithm outputs to inputs.");
101}
102
105 // propertyTable will contain the final properties of the managed algorithms.
106 ITableWorkspace_sptr propertyTable = setupTable->clone();
108 if (ioMappingTable->rowCount() != 1) {
109 throw std::runtime_error("Row count in " + PropertyNames::IO_MAP + " is " +
110 std::to_string(ioMappingTable->rowCount()) + ", not 1.");
111 }
112 // Transform ioMappingTable to a real lookup table (ioMap) and check for
113 // consistency.
114 std::unordered_map<std::string, std::string> ioMap;
115 const auto inputPropertyNames = ioMappingTable->getColumnNames();
116 for (size_t col = 0; col < ioMappingTable->columnCount(); ++col) {
117 const auto &inputPropertyName = ioMappingTable->getColumn(col)->name();
118 const auto &outputPropertyName = ioMappingTable->String(0, col);
119 if (!outputPropertyName.empty()) {
120 if (std::find(inputPropertyNames.cbegin(), inputPropertyNames.cend(), outputPropertyName) !=
121 inputPropertyNames.cend()) {
122 throw std::runtime_error("Property " + outputPropertyName + " linked to " + inputPropertyName +
123 " is also an input property.");
124 }
125 if (ioMap.find(inputPropertyName) != ioMap.end()) {
126 throw std::runtime_error("Cannot assign more than one output to " + inputPropertyName + '.');
127 }
128 ioMap[inputPropertyName] = outputPropertyName;
129 }
130 }
131 // Declare order of execution and fill in propertyTable.
132 cleanPropertyTable(propertyTable, ioMap);
133 std::deque<size_t> queue;
134 for (size_t i = 0; i < setupTable->rowCount(); ++i) {
135 configureRow(setupTable, propertyTable, i, queue, ioMap);
136 }
137
138 // Execute the algorithm in the order specified by queue.
139 const std::string algorithmName = getProperty(PropertyNames::ALGORITHM);
140 auto &algorithmFactory = AlgorithmFactory::Instance();
141 while (!queue.empty()) {
142 const auto row = queue.front();
143 auto algorithm = algorithmFactory.create(algorithmName, algorithmFactory.highestVersion(algorithmName));
144 algorithm->initialize();
145 if (!algorithm->isInitialized()) {
146 throw std::runtime_error("Workflow algorithm failed to initialise.");
147 }
148 // First column in taskTable is for id.
149 for (size_t col = 1; col < propertyTable->columnCount(); ++col) {
150 const auto column = propertyTable->getColumn(col);
151 const auto &propertyName = column->name();
152 const auto &valueType = column->get_type_info();
153 try {
154 if (valueType == typeid(std::string)) {
155 const auto &value = propertyTable->cell<std::string>(row, col);
156 algorithm->setProperty(propertyName, value);
157 } else if (valueType == typeid(int)) {
158 const auto &value = propertyTable->cell<int>(row, col);
159 algorithm->setProperty(propertyName, static_cast<long>(value));
160 } else if (valueType == typeid(size_t)) {
161 const auto &value = propertyTable->cell<size_t>(row, col);
162 algorithm->setProperty(propertyName, value);
163 } else if (valueType == typeid(float) || valueType == typeid(double)) {
164 const auto &value = propertyTable->cell<double>(row, col);
165 algorithm->setProperty(propertyName, value);
166 } else if (valueType == typeid(bool)) {
167 const auto &value = propertyTable->cell<bool>(row, col);
168 algorithm->setProperty(propertyName, value);
169 } else if (valueType == typeid(Kernel::V3D)) {
170 const auto &value = propertyTable->cell<V3D>(row, col);
171 algorithm->setProperty(propertyName, value);
172 } else {
173 throw std::runtime_error("Unimplemented column type in " + PropertyNames::SETUP_TABLE + ": " +
174 valueType.name() + '.');
175 }
176 } catch (std::invalid_argument &e) {
177 throw std::runtime_error("While setting properties for algorithm " + algorithmName + ": " + e.what());
178 }
179 }
180 algorithm->execute();
181 if (!algorithm->isExecuted()) {
182 throw std::runtime_error("Workflow algorithm failed to execute.");
183 }
184 queue.pop_front();
185 }
186}
187
201template <typename QUEUE, typename MAP>
203 const size_t currentRow, QUEUE &queue, const MAP &ioMap,
204 std::shared_ptr<std::unordered_set<size_t>> rowsBeingQueued) const {
205 // This method works recursively with regards to dependency resolution.
206
207 if (currentRow > setupTable->rowCount()) {
208 throw std::runtime_error("Current row " + std::to_string(currentRow) + " out of task table bounds " +
209 std::to_string(setupTable->rowCount()) + '.');
210 }
211
212 // 1. currentRow is already in queue? Nothing to be done, then.
213 // Here we blindly assume that propertyTable has been configured as well.
214 if (std::find(queue.cbegin(), queue.cend(), currentRow) != queue.cend()) {
215 return;
216 }
217
218 // 2. Check if currentRow is being processed already to prevent circular
219 // dependencies. If not, mark the row as such.
220 if (!rowsBeingQueued) {
221 rowsBeingQueued.reset(new std::unordered_set<size_t>());
222 }
223 const auto status = rowsBeingQueued->emplace(currentRow);
224 if (!status.second) {
225 throw std::runtime_error("Circular dependencies!");
226 }
227
228 // 3a. Find the rows on which currentRow depends on. For each of them,
229 // call this method recursively to make sure they are in the queue
230 // before currentRow.
231 // 3b. Configure propertyTable for currentRow by adding the correct
232 // workspace names to input/output columns.
233 for (const auto &ioPair : ioMap) {
234 const auto &outputId = setupTable->getRef<std::string>(ioPair.first, currentRow);
235 if (!outputId.empty()) {
236 if (isHardCodedWorkspaceName(outputId)) {
237 // Handle hard-coded input.
238 propertyTable->getRef<std::string>(ioPair.first, currentRow) = tidyWorkspaceName(outputId);
239 } else {
240 // If input is not hard-coded, we have a dependency.
241 // Find the source row.
242 size_t outputRow = -1;
243 try {
244 setupTable->find(outputId, outputRow, 0);
245 } catch (std::out_of_range &) {
246 throw std::runtime_error("Identifier \"" + outputId + "\" not found in " + PropertyNames::SETUP_TABLE +
247 " (referenced in row " + std::to_string(currentRow) + ", column \"" + ioPair.first +
248 "\").");
249 }
250 // Configure the source row and recursively the rows it depends on.
251 configureRow(setupTable, propertyTable, outputRow, queue, ioMap, rowsBeingQueued);
252 const auto outputCol = ioPair.second;
253 auto outputWorkspaceName = setupTable->getRef<std::string>(outputCol, outputRow);
254 if (outputWorkspaceName.empty()) {
255 throw std::runtime_error("No source workspace name found for " + ioPair.first + '.');
256 }
257 // Handle forced output.
258 if (isHardCodedWorkspaceName(outputWorkspaceName)) {
259 outputWorkspaceName = tidyWorkspaceName(outputWorkspaceName);
260 }
261 propertyTable->getRef<std::string>(ioPair.first, currentRow) = outputWorkspaceName;
262 propertyTable->getRef<std::string>(outputCol, outputRow) = outputWorkspaceName;
263 }
264 }
265 }
266
267 // 4. Dependencies have been taken care of -> add currentRow to
268 // queue.
269 queue.emplace_back(currentRow);
270
271 // 5. Finally, unmark currentRow as being processed.
272 rowsBeingQueued->erase(currentRow);
273}
274
275} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
double value
The value of the point.
Definition FitMW.cpp:51
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
A property class for workspaces.
Controls the data flow and the order of algorithm execution.
void configureRow(API::ITableWorkspace_sptr setupTable, API::ITableWorkspace_sptr propertyTable, const size_t currentRow, QUEUE &queue, const MAP &ioMap, std::shared_ptr< std::unordered_set< size_t > > rowsBeingQueued=nullptr) const
Configures a row in setupTable.
Validator to check that a property is not left empty.
Class for 3D vectors.
Definition V3D.h:35
std::shared_ptr< Column > Column_sptr
Definition Column.h:229
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
static const std::string IO_MAP("InputOutputMap")
static const std::string SETUP_TABLE("SetupTable")
static const std::string ALGORITHM("Algorithm")
std::string tidyWorkspaceName(const std::string &s)
Removes first and last character of a string.
bool isHardCodedWorkspaceName(const std::string &s)
Checks if a string is a hard coded workspace name.
void cleanPropertyTable(const ITableWorkspace_sptr &table, const MAP &ioMapping)
Transforms a setup table to an empty property table.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:54