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