13#include <unordered_map>
14#include <unordered_set>
40 void setDependantTask(
const std::string &task,
const std::string &output_name =
"",
const std::string &alias =
"",
41 const size_t dependantTaskSet = 0) {
43 throw std::runtime_error(
"Dependant task set index " +
std::to_string(dependantTaskSet) +
44 " is out of range for task " +
m_name);
45 if (output_name.empty()) {
55 if (!missingTasks.empty()) {
56 throw std::runtime_error(
57 "Cannot execute task " +
m_name +
" as the following dependent tasks outputs are not available: " +
58 std::accumulate(std::next(missingTasks.begin()), missingTasks.end(), missingTasks.front(),
59 [](
const std::string &a,
const std::string &b) { return a +
", " + b; }));
65 m_parent->g_log.debug(
"Finished executing task: " +
m_name +
"\n");
82 void outputWorkspace(std::shared_ptr<MatrixWorkspace> ws,
const std::string &outputName) {
84 m_parent->m_algorithmTaskOutputs[
m_name][outputName] = std::move(ws);
100 std::vector<std::unordered_map<std::string, std::vector<std::pair<std::string, std::string>>>>
m_dependantTasks;
116 const auto &taskName = item.first;
117 auto &outputs = item.second;
118 auto it = std::find_if(
m_parent->m_stagedAlgorithmTasks.cbegin(),
m_parent->m_stagedAlgorithmTasks.cend(),
119 [&taskName](std::shared_ptr<AlgorithmTask> task) { return task->name() == taskName; });
120 if (it ==
m_parent->m_stagedAlgorithmTasks.cend())
122 if (outputs.empty()) {
124 const auto &expectedOutputs = (*it)->getExpectedOutputs();
125 std::transform(expectedOutputs.begin(), expectedOutputs.end(), std::back_inserter(outputs),
126 [](
const auto &output) { return std::pair<std::string, std::string>{output, output}; });
133 if (m_fulfilledDependantTaskSets.size() == 1) {
134 m_activeDependantTaskSet = m_fulfilledDependantTaskSets.front();
136 }
else if (m_fulfilledDependantTaskSets.size() == 0) {
143 size_t closestTaskSet = 0;
144 int closestDistance = std::numeric_limits<int>::max();
145 for (
auto taskSet : m_fulfilledDependantTaskSets) {
146 for (
const auto &task : m_dependantTasks[taskSet]) {
147 const auto &taskName = task.first;
150 int distance = (int)myIndex - (
int)
index;
152 if ((distance < closestDistance) && distance > 0) {
153 closestDistance = distance;
154 closestTaskSet = taskSet;
158 m_activeDependantTaskSet = closestTaskSet;
163 if (!m_parent->m_algorithmTaskOutputs.contains(m_name))
164 throw std::runtime_error(
"No output from task " + m_name +
" found after task execution");
165 std::vector<std::string> missingOutput;
166 const auto &taskOutputs = m_parent->m_algorithmTaskOutputs[m_name];
167 std::copy_if(m_expectedOutputs.begin(), m_expectedOutputs.end(), std::back_inserter(missingOutput),
168 [&taskOutputs](
const auto &output) { return !taskOutputs.contains(output); });
169 if (!missingOutput.empty()) {
170 throw std::runtime_error(
171 "Expected outputs from task " + m_name +
" not found after task execution: " +
172 std::accumulate(std::next(missingOutput.begin()), missingOutput.end(), missingOutput.front(),
173 [](
const std::string &a,
const std::string &b) { return a +
", " + b; }));
183 std::vector<std::string> missingTasksAll;
185 for (
size_t i = 0; i < m_dependantTasks.size(); ++i) {
187 std::vector<std::string> missingTasks;
188 const auto &missingTask = populateDependantTasks(i);
189 if (!missingTask.empty()) {
191 " unfulfillable as required tasks not staged: " + missingTask);
193 for (
const auto &[taskName, outputs] : m_dependantTasks[i]) {
194 if (!m_parent->m_algorithmTaskOutputs.contains(taskName)) {
195 missingTasks.push_back(
"Task set: " +
std::to_string(i) +
" Task name: " + taskName +
": ALL OUTPUTS");
197 for (
const auto &output : outputs) {
198 if (!m_parent->m_algorithmTaskOutputs[taskName].contains(output.first)) {
199 missingTasks.push_back(
"Task set: " +
std::to_string(i) +
" Task name: " + taskName +
": " +
203 addDependantTaskOutput(output.second, m_parent->m_algorithmTaskOutputs[taskName][output.first], i);
211 if (missingTasks.empty()) {
212 m_fulfilledDependantTaskSets.push_back(i);
214 missingTasksAll.insert(missingTasksAll.cend(), missingTasks.cbegin(), missingTasks.cend());
217 if (m_fulfilledDependantTaskSets.empty())
218 return missingTasksAll;
223 const size_t taskSetIndex) {
224 m_dependantOutputs[taskSetIndex][outputName] = std::move(ws);
233 if (!m_mutableInput) {
234 auto cloneAlg = createChildAlgorithm(
"CloneWorkspace");
235 cloneAlg->setProperty(
"InputWorkspace", inputWS);
238 inputWS = std::dynamic_pointer_cast<MatrixWorkspace>(clone);
240 tasks[0]->initAsFirstTask(std::move(inputWS));
241 m_stagedAlgorithmTasks = std::move(tasks);
245 m_taskExecutionOrder =
246 isDefault(
"TaskExecutionOrder") ? constructTaskExecutionOrder() : getProperty(
"TaskExecutionOrder");
247 std::vector<std::shared_ptr<AlgorithmTask>> tasksToStage(m_taskExecutionOrder.size());
248 validateTaskExecutionOrder();
249 for (
auto &task : m_AlgorithmTasks) {
250 task->setTaskExecutionOrder(&m_taskExecutionOrder);
251 auto it = std::find(m_taskExecutionOrder.begin(), m_taskExecutionOrder.end(), task->name());
252 if (it != m_taskExecutionOrder.end()) {
253 std::size_t
index = std::distance(m_taskExecutionOrder.begin(), it);
254 tasksToStage[
index] = task;
257 stageAlgorithmTasks(std::move(tasksToStage));
261 std::unordered_set<std::string> testSet;
262 testSet.reserve(m_taskExecutionOrder.size());
263 for (
const auto &task : m_taskExecutionOrder) {
264 auto it = std::find_if(m_AlgorithmTasks.cbegin(), m_AlgorithmTasks.cend(),
265 [&](
const auto &algorithmTask) { return algorithmTask->name() == task; });
266 if (it == m_AlgorithmTasks.cend())
267 throw std::runtime_error(
"Invalid task specified in TaskExecutionOrder: " + task);
268 auto [taskIt, inserted] = testSet.insert(task);
270 throw std::runtime_error(
"Duplicate task specified in TaskExecutionOrder, this is not yet supported: " + task);
274 void execTasks(
const std::string &diagWorkspacePrefix =
"") {
275 configureAlgorithmTasks();
279 for (
size_t i = 0; i < m_stagedAlgorithmTasks.size(); ++i) {
280 const auto &task = m_stagedAlgorithmTasks[i];
282 if (!diagWorkspacePrefix.empty()) {
283 const auto &taskOutput = m_algorithmTaskOutputs.at(task->name());
284 for (
const auto &output : taskOutput) {
285 const auto &outputName = output.first;
286 outputDebugWorkspace(output.second, diagWorkspacePrefix,
"_" + outputName, step);
291 if (i == m_stagedAlgorithmTasks.size() - 1)
292 setProperty(
"OutputWorkspace", m_algorithmTaskOutputs.at(task->name()).at(task->getSelectedOutput()));
302 for (
auto &task : m_stagedAlgorithmTasks) {
305 m_algorithmTaskOutputs.clear();
312 AnalysisDataService::Instance().addOrReplace(wsName +
"_" +
std::to_string(step) + wsSuffix, cloneWS);
315 template <
typename... TaskTypes>
317 static_assert((std::is_base_of_v<AlgorithmTask, TaskTypes> && ...),
"All TaskTypes must derive from AlgorithmTask");
318 m_AlgorithmTasks.clear();
319 m_AlgorithmTasks.reserve(
sizeof...(TaskTypes));
320 auto *parent =
static_cast<T *
>(
this);
321 (m_AlgorithmTasks.emplace_back(std::make_shared<TaskTypes>(parent)), ...);
322 declareProperty(
"TaskExecutionOrder", defaultTaskExecutionOrder,
"The tasks to execute, in execution order.");
328 std::vector<std::string> teo = getProperty(
"TaskExecutionOrder");
337 std::unordered_map<std::string, std::unordered_map<std::string, std::shared_ptr<MatrixWorkspace>>>
std::map< DeltaEMode::Type, std::string > index
Data processor algorithm to be used as a parent to workflow algorithms.
void initAsFirstTask(std::shared_ptr< MatrixWorkspace > inputWS)
std::vector< size_t > m_fulfilledDependantTaskSets
std::vector< std::unordered_map< std::string, std::vector< std::pair< std::string, std::string > > > > m_dependantTasks
std::string m_selectedOutput
const std::vector< std::string > * m_taskExecutionOrder
virtual void executeImpl()=0
const std::string & getSelectedOutput() const
size_t m_activeDependantTaskSet
void setTaskExecutionOrder(const std::vector< std::string > *taskExecutionOrder)
void setSelectedOutput(const std::string &output, const bool overwrite=false)
std::shared_ptr< MatrixWorkspace > getDependantWorkspace(const std::string &outputAlias)
AlgorithmTask(T *parent, const std::string &name)
std::vector< std::unordered_map< std::string, std::shared_ptr< MatrixWorkspace > > > m_dependantOutputs
size_t addDependantTaskSet()
void setDependantTask(const std::string &task, const std::string &output_name="", const std::string &alias="", const size_t dependantTaskSet=0)
void addDependantTaskOutput(const std::string &outputName, std::shared_ptr< MatrixWorkspace > ws, const size_t taskSetIndex)
void checkExpectedOutputs()
void setExpectedOutputs(const std::vector< std::string > &expectedOutputs)
std::vector< std::string > evaluateDependentTasks()
const std::vector< std::string > & getExpectedOutputs() const
const std::string & name() const
std::vector< std::string > m_expectedOutputs
void outputWorkspace(std::shared_ptr< MatrixWorkspace > ws, const std::string &outputName)
std::string populateDependantTasks(const size_t taskSetIndex)
std::unordered_map< std::string, std::unordered_map< std::string, std::shared_ptr< MatrixWorkspace > > > m_algorithmTaskOutputs
void execTasks(const std::string &diagWorkspacePrefix="")
std::vector< std::shared_ptr< AlgorithmTask > > m_stagedAlgorithmTasks
void initTaskBasedAlgorithm(const std::vector< std::string > &defaultTaskExecutionOrder={})
std::vector< std::shared_ptr< AlgorithmTask > > m_AlgorithmTasks
std::vector< std::string > m_taskExecutionOrder
void outputDebugWorkspace(const MatrixWorkspace_sptr &ws, const std::string &wsName, const std::string &wsSuffix, const int step)
void setMutableInput(const bool inputIsMutable)
virtual std::vector< std::string > constructTaskExecutionOrder()
void validateTaskExecutionOrder()
void configureAlgorithmTasks()
void stageAlgorithmTasks(std::vector< std::shared_ptr< AlgorithmTask > > tasks)
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::string to_string(const wide_integer< Bits, Signed > &n)