Algorithm.cpp 73.1 KB
Newer Older
1
2
3
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4
5
//   NScD Oak Ridge National Laboratory, European Spallation Source,
//   Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6
// SPDX - License - Identifier: GPL - 3.0 +
7
#include "MantidAPI/Algorithm.h"
8
#include "MantidAPI/ADSValidator.h"
9
#include "MantidAPI/AlgorithmHistory.h"
10
#include "MantidAPI/AlgorithmManager.h"
11
#include "MantidAPI/AnalysisDataService.h"
12
#include "MantidAPI/DeprecatedAlgorithm.h"
13
#include "MantidAPI/DeprecatedAlias.h"
14
#include "MantidAPI/IWorkspaceProperty.h"
15
#include "MantidAPI/WorkspaceGroup.h"
16
#include "MantidAPI/WorkspaceHistory.h"
17

18
#include "MantidKernel/CompositeValidator.h"
Nick Draper's avatar
Nick Draper committed
19
#include "MantidKernel/ConfigService.h"
20
#include "MantidKernel/EmptyValues.h"
21
#include "MantidKernel/MultiThreaded.h"
22
#include "MantidKernel/PropertyWithValue.h"
23
#include "MantidKernel/Strings.h"
24
#include "MantidKernel/Timer.h"
Nick Draper's avatar
Nick Draper committed
25
#include "MantidKernel/UsageService.h"
26

27
28
#include "MantidParallel/Communicator.h"

29
#include "MantidKernel/StringTokenizer.h"
30
31
32
#include <Poco/ActiveMethod.h>
#include <Poco/ActiveResult.h>
#include <Poco/NotificationCenter.h>
33
#include <Poco/RWLock.h>
34
#include <Poco/Void.h>
Nick Draper's avatar
Nick Draper committed
35

36
#include <json/json.h>
37

38
#include <map>
39
#include <memory>
David Fairbrother's avatar
David Fairbrother committed
40
#include <utility>
41

LamarMoore's avatar
LamarMoore committed
42
43
44
// Index property handling template definitions
#include "MantidAPI/Algorithm.tcc"

45
46
using namespace Mantid::Kernel;

47
48
49
50
51
namespace Mantid {
namespace API {
namespace {
/// Separator for workspace types in workspaceMethodOnTypes member
const std::string WORKSPACE_TYPES_SEPARATOR = ";";
52

Nick Draper's avatar
Nick Draper committed
53
54
/// The minimum number of seconds after execution that the algorithm should be
/// kept alive before garbage collection
55
56
const size_t DELAY_BEFORE_GC = 5;

57
58
class WorkspacePropertyValueIs {
public:
59
  explicit WorkspacePropertyValueIs(const std::string &value) : m_value(value) {}
Dan Nixon's avatar
Dan Nixon committed
60
  bool operator()(IWorkspaceProperty *property) {
61
    auto *prop = dynamic_cast<Property *>(property);
62
63
64
65
66
67
    if (!prop)
      return false;
    return prop->value() == m_value;
  }

private:
68
  const std::string &m_value;
69
};
70
71
72
73
74
75
76
77
78

template <typename T> struct RunOnFinish {
  RunOnFinish(T &&task) : m_onfinsh(std::move(task)) {}
  ~RunOnFinish() { m_onfinsh(); }

private:
  T m_onfinsh;
};

79
} // namespace
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

// Doxygen can't handle member specialization at the moment:
// https://bugzilla.gnome.org/show_bug.cgi?id=406027
// so we have to ignore them
///@cond
template <typename NumT> bool Algorithm::isEmpty(const NumT toCheck) {
  return static_cast<int>(toCheck) == EMPTY_INT();
}

template <> MANTID_API_DLL bool Algorithm::isEmpty(const double toCheck) {
  return std::abs((toCheck - EMPTY_DBL()) / (EMPTY_DBL())) < 1e-8;
}

// concrete instantiations
template MANTID_API_DLL bool Algorithm::isEmpty<int>(const int);
template MANTID_API_DLL bool Algorithm::isEmpty<int64_t>(const int64_t);
template MANTID_API_DLL bool Algorithm::isEmpty<std::size_t>(const std::size_t);
///@endcond

//=============================================================================================
//================================== Constructors/Destructors
//=================================
//=============================================================================================

/// Initialize static algorithm counter
size_t Algorithm::g_execCount = 0;

/// Constructor
Algorithm::Algorithm()
109
110
111
112
113
114
    : PropertyManagerOwner(), m_cancel(false), m_parallelException(false), m_log("Algorithm"), g_log(m_log),
      m_groupSize(0), m_executeAsync(nullptr), m_notificationCenter(nullptr), m_progressObserver(nullptr),
      m_executionState(ExecutionState::Uninitialized), m_resultState(ResultState::NotFinished),
      m_isChildAlgorithm(false), m_recordHistoryForChild(false), m_alwaysStoreInADS(true), m_runningAsync(false),
      m_rethrow(false), m_isAlgStartupLoggingEnabled(true), m_startChildProgress(0.), m_endChildProgress(0.),
      m_algorithmID(this), m_singleGroup(-1), m_groupsHaveSimilarNames(false), m_inputWorkspaceHistories(),
115
      m_communicator(std::make_unique<Parallel::Communicator>()) {}
116
117

/// Virtual destructor
118
Algorithm::~Algorithm() {}
119
120
121
122
123
124

//=============================================================================================
//================================== Simple Getters/Setters
//===================================
//=============================================================================================

125
126
127
128
/// Gets the current execution state
ExecutionState Algorithm::executionState() const { return m_executionState; }

/// Sets the current execution state
129
void Algorithm::setExecutionState(const ExecutionState state) { m_executionState = state; }
130
131
132
133
134

/// Gets the current result State
ResultState Algorithm::resultState() const { return m_resultState; }

/// Sets the result execution state
135
/// if set to Success or Failed will also set the execution state to finished
136
void Algorithm::setResultState(const ResultState state) {
137
138
139
  if (state != ResultState::NotFinished) {
    setExecutionState(ExecutionState::Finished);
  }
140
141
142
  m_resultState = state;
}

143
144
//---------------------------------------------------------------------------------------------
/// Has the Algorithm already been initialized
145
bool Algorithm::isInitialized() const { return (m_executionState != ExecutionState::Uninitialized); }
146

Nick Draper's avatar
Nick Draper committed
147
/// Has the Algorithm already been executed successfully
148
bool Algorithm::isExecuted() const {
149
  return ((executionState() == ExecutionState::Finished) && (resultState() == ResultState::Success));
150
}
151
152
153

//---------------------------------------------------------------------------------------------
/** To query whether algorithm is a child.
154
155
156
 *  @returns true - the algorithm is a child algorithm.  False - this is a full
 * managed algorithm.
 */
157
158
159
bool Algorithm::isChild() const { return m_isChildAlgorithm; }

/** To set whether algorithm is a child.
160
161
162
 *  @param isChild :: True - the algorithm is a child algorithm.  False - this
 * is a full managed algorithm.
 */
163
void Algorithm::setChild(const bool isChild) {
Gagik Vardanyan's avatar
Gagik Vardanyan committed
164
165
  m_isChildAlgorithm = isChild;
  this->setAlwaysStoreInADS(!isChild);
166
}
167
168
169
170
171
172

/**
 * Change the state of the history recording flag. Only applicable for
 * child algorithms.
 * @param on :: The new state of the flag
 */
173
void Algorithm::enableHistoryRecordingForChild(const bool on) { m_recordHistoryForChild = on; }
174
175
176
177
178
179

/** Do we ALWAYS store in the AnalysisDataService? This is set to true
 * for python algorithms' child algorithms
 *
 * @param doStore :: always store in ADS
 */
180
void Algorithm::setAlwaysStoreInADS(const bool doStore) { m_alwaysStoreInADS = doStore; }
181

182
183
184
185
186
/** Returns true if we always store in the AnalysisDataService.
 *  @return true if output is saved to the AnalysisDataService.
 */
bool Algorithm::getAlwaysStoreInADS() const { return m_alwaysStoreInADS; }

187
188
189
190
191
192
/** Set whether the algorithm will rethrow exceptions
 * @param rethrow :: true if you want to rethrow exception.
 */
void Algorithm::setRethrows(const bool rethrow) { this->m_rethrow = rethrow; }

/// True if the algorithm is running.
193
bool Algorithm::isRunning() const { return (executionState() == ExecutionState::Running); }
194

195
/// True if the algorithm is ready for garbage collection.
Nick Draper's avatar
Nick Draper committed
196
197
198
199
200
201
bool Algorithm::isReadyForGarbageCollection() const {
  if ((executionState() == ExecutionState::Finished) &&
      (Mantid::Types::Core::DateAndTime::getCurrentTime() > m_gcTime)) {
    return true;
  }
  return false;
202
203
}

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//---------------------------------------------------------------------------------------------
/**  Add an observer to a notification
@param observer :: Reference to the observer to add
*/
void Algorithm::addObserver(const Poco::AbstractObserver &observer) const {
  notificationCenter().addObserver(observer);
}

/**  Remove an observer
@param observer :: Reference to the observer to remove
*/
void Algorithm::removeObserver(const Poco::AbstractObserver &observer) const {
  notificationCenter().removeObserver(observer);
}

//---------------------------------------------------------------------------------------------
/** Sends ProgressNotification.
 * @param p :: Reported progress,  must be between 0 (just started) and 1
 * (finished)
 * @param msg :: Optional message string
 * @param estimatedTime :: Optional estimated time to completion
 * @param progressPrecision :: optional, int number of digits after the decimal
 * point to show.
227
 */
228
229
void Algorithm::progress(double p, const std::string &msg, double estimatedTime, int progressPrecision) {
  notificationCenter().postNotification(new ProgressNotification(this, p, msg, estimatedTime, progressPrecision));
230
231
232
233
234
}

//---------------------------------------------------------------------------------------------
/// Function to return all of the categories that contain this algorithm
const std::vector<std::string> Algorithm::categories() const {
235
236
237
  Mantid::Kernel::StringTokenizer tokenizer(category(), categorySeparator(),
                                            Mantid::Kernel::StringTokenizer::TOK_TRIM |
                                                Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY);
238

239
  auto res = tokenizer.asVector();
240

241
  const auto *depo = dynamic_cast<const DeprecatedAlgorithm *>(this);
242
  if (depo != nullptr) {
243
    res.emplace_back("Deprecated");
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  }
  return res;
}

/**
 * @return A string giving the method name that should be attached to a
 * workspace
 */
const std::string Algorithm::workspaceMethodName() const { return ""; }

/**
 *
 * @return A list of workspace class names that should have the
 *workspaceMethodName attached
 */
const std::vector<std::string> Algorithm::workspaceMethodOn() const {
260
261
262
  Mantid::Kernel::StringTokenizer tokenizer(this->workspaceMethodOnTypes(), WORKSPACE_TYPES_SEPARATOR,
                                            Mantid::Kernel::StringTokenizer::TOK_TRIM |
                                                Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY);
263
  return tokenizer.asVector();
264
265
266
267
268
269
270
271
272
}

/**
 * @return The name of the property that the calling object will be passed to.
 */
const std::string Algorithm::workspaceMethodInputProperty() const { return ""; }

//---------------------------------------------------------------------------------------------
/** Initialization method invoked by the framework. This method is responsible
273
274
275
276
277
278
279
 *  for any bookkeeping of initialization required by the framework itself.
 *  It will in turn invoke the init() method of the derived algorithm,
 *  and of any Child Algorithms which it creates.
 *  @throw runtime_error Thrown if algorithm or Child Algorithm cannot be
 *initialised
 *
 */
280
281
void Algorithm::initialize() {
  // Bypass the initialization if the algorithm has already been initialized.
282
  if (isInitialized())
283
284
285
    return;

  g_log.setName(this->name());
286
  setLoggingOffset(0);
287
288
289
  try {
    try {
      this->init();
290
      setupSkipValidationMasterOnly();
291
292
    } catch (std::runtime_error &) {
      throw;
293
294
    }

295
296
    // Indicate that this Algorithm has been initialized to prevent duplicate
    // attempts.
297
    setExecutionState(ExecutionState::Initialized);
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  } catch (std::runtime_error &) {
    throw;
  }
  // Unpleasant catch-all! Along with this, Gaudi version catches GaudiException
  // & std::exception
  // but doesn't really do anything except (print fatal) messages.
  catch (...) {
    // Gaudi: A call to the auditor service is here
    // (1) perform the printout
    getLogger().fatal("UNKNOWN Exception is caught in initialize()");
    throw;
  }
}

//---------------------------------------------------------------------------------------------
/** Perform validation of ALL the input properties of the algorithm.
 * This is to be overridden by specific algorithms.
 * It will be called in dialogs after parsing all inputs and setting the
 * properties, but BEFORE executing.
 *
 * @return a map where: Key = string name of the the property;
            Value = string describing the problem with the property.
 */
321
std::map<std::string, std::string> Algorithm::validateInputs() { return std::map<std::string, std::string>(); }
322
323

//---------------------------------------------------------------------------------------------
324
325
/**
 * Go through the properties and cache the input/output
326
327
328
329
330
331
 * workspace properties for later use.
 */
void Algorithm::cacheWorkspaceProperties() {
  m_inputWorkspaceProps.clear();
  m_outputWorkspaceProps.clear();
  m_pureOutputWorkspaceProps.clear();
332
333
334
335
336
337
338
  const auto &props = this->getProperties();
  for (const auto &prop : props) {
    auto wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
    if (!wsProp)
      continue;
    switch (prop->direction()) {
    case Kernel::Direction::Input:
339
      m_inputWorkspaceProps.emplace_back(wsProp);
340
341
      break;
    case Kernel::Direction::InOut:
342
343
      m_inputWorkspaceProps.emplace_back(wsProp);
      m_outputWorkspaceProps.emplace_back(wsProp);
344
345
      break;
    case Kernel::Direction::Output:
346
347
      m_outputWorkspaceProps.emplace_back(wsProp);
      m_pureOutputWorkspaceProps.emplace_back(wsProp);
348
349
      break;
    default:
350
351
      throw std::logic_error("Unexpected property direction found for property " + prop->name() + " of algorithm " +
                             this->name());
352
353
    }
  }
354
355
}

356
357
358
359
360
361
362
363
364
365
/**
 * Cache the histories of any input workspaces so they can be copied over after
 * algorithm completion.
 */
void Algorithm::cacheInputWorkspaceHistories() {
  if (!trackingHistory())
    return;

  auto cacheHistories = [this](const Workspace_sptr &ws) {
    if (auto group = dynamic_cast<const WorkspaceGroup *>(ws.get())) {
366
      m_inputWorkspaceHistories.reserve(m_inputWorkspaceHistories.size() + group->size());
367
368
369
370
371
372
373
374
375
376
377
378
379
      for (const auto &memberWS : *group) {
        m_inputWorkspaceHistories.emplace_back(memberWS);
      }
    } else {
      m_inputWorkspaceHistories.emplace_back(ws);
    }
  };
  using ArrayPropertyString = ArrayProperty<std::string>;
  auto isADSValidator = [](const IValidator_sptr &validator) -> bool {
    if (!validator)
      return false;
    if (dynamic_cast<ADSValidator *>(validator.get()))
      return true;
380
    if (const auto compValidator = dynamic_cast<CompositeValidator *>(validator.get()))
381
382
383
384
385
386
387
388
389
390
391
392
      return compValidator->contains<ADSValidator>();

    return false;
  };

  // Look over all properties so we can catch an string array properties
  // with an ADSValidator. ADSValidator indicates that the strings
  // point to workspace names so we want to pick up the history from these too.
  const auto &ads = AnalysisDataService::Instance();
  m_inputWorkspaceHistories.clear();
  const auto &props = this->getProperties();
  for (const auto &prop : props) {
393
    if (prop->direction() != Direction::Input && prop->direction() != Direction::InOut)
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
      continue;

    if (auto wsProp = dynamic_cast<IWorkspaceProperty *>(prop)) {
      if (auto ws = wsProp->getWorkspace()) {
        cacheHistories(ws);
      } else {
        Workspace_sptr wsFromADS;
        try {
          wsFromADS = ads.retrieve(prop->value());
        } catch (Exception::NotFoundError &) {
          continue;
        }
        cacheHistories(wsFromADS);
      }
    } else if (auto strArrayProp = dynamic_cast<ArrayPropertyString *>(prop)) {
      if (!isADSValidator(strArrayProp->getValidator()))
        continue;
      const auto &wsNames((*strArrayProp)());
      for (const auto &name : wsNames) {
        cacheHistories(ads.retrieve(name));
      }
    }
  }
} // namespace API
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434

//---------------------------------------------------------------------------------------------
/** Go through the workspace properties of this algorithm
 * and lock the workspaces for reading or writing.
 *
 */
void Algorithm::lockWorkspaces() {
  // Do not lock workspace for child algos
  if (this->isChild())
    return;

  if (!m_readLockedWorkspaces.empty() || !m_writeLockedWorkspaces.empty())
    throw std::logic_error("Algorithm::lockWorkspaces(): The workspaces have "
                           "already been locked!");

  // First, Write-lock the output workspaces
  auto &debugLog = g_log.debug();
Hahn, Steven's avatar
Hahn, Steven committed
435
436
  for (auto &outputWorkspaceProp : m_outputWorkspaceProps) {
    Workspace_sptr ws = outputWorkspaceProp->getWorkspace();
437
438
439
    if (ws) {
      // The workspace property says to do locking,
      // AND it has NOT already been write-locked
440
441
      if (outputWorkspaceProp->isLocking() && std::find(m_writeLockedWorkspaces.begin(), m_writeLockedWorkspaces.end(),
                                                        ws) == m_writeLockedWorkspaces.end()) {
442
        // Write-lock it if not already
443
        debugLog << "Write-locking " << ws->getName() << '\n';
444
        ws->getLock()->writeLock();
445
        m_writeLockedWorkspaces.emplace_back(ws);
446
447
      }
    }
448
  }
449

450
  // Next read-lock the input workspaces
Hahn, Steven's avatar
Hahn, Steven committed
451
452
  for (auto &inputWorkspaceProp : m_inputWorkspaceProps) {
    Workspace_sptr ws = inputWorkspaceProp->getWorkspace();
453
454
455
    if (ws) {
      // The workspace property says to do locking,
      // AND it has NOT already been write-locked
456
457
      if (inputWorkspaceProp->isLocking() && std::find(m_writeLockedWorkspaces.begin(), m_writeLockedWorkspaces.end(),
                                                       ws) == m_writeLockedWorkspaces.end()) {
458
        // Read-lock it if not already write-locked
459
        debugLog << "Read-locking " << ws->getName() << '\n';
460
        ws->getLock()->readLock();
461
        m_readLockedWorkspaces.emplace_back(ws);
462
      }
463
    }
464
465
466
467
468
469
470
471
472
473
474
475
  }
}

//---------------------------------------------------------------------------------------------
/** Unlock any previously locked workspaces
 *
 */
void Algorithm::unlockWorkspaces() {
  // Do not lock workspace for child algos
  if (this->isChild())
    return;
  auto &debugLog = g_log.debug();
476
  for (auto &ws : m_writeLockedWorkspaces) {
477
    if (ws) {
478
      debugLog << "Unlocking " << ws->getName() << '\n';
479
      ws->getLock()->unlock();
480
    }
481
  }
482
  for (auto &ws : m_readLockedWorkspaces) {
483
    if (ws) {
484
      debugLog << "Unlocking " << ws->getName() << '\n';
485
      ws->getLock()->unlock();
486
    }
487
  }
488

489
490
491
492
493
  // Don't double-unlock workspaces
  m_readLockedWorkspaces.clear();
  m_writeLockedWorkspaces.clear();
}

494
495
496
497
498
499
500
501
502
503
504
505
506
/**
 * Clear any internal workspace handles so that workspaces will be deleted
 * promptly after a managed algorithm finishes
 */
void Algorithm::clearWorkspaceCaches() {
  m_groupWorkspaces.clear();
  m_inputWorkspaceHistories.clear();
  m_inputWorkspaceProps.clear();
  m_outputWorkspaceProps.clear();
  m_pureOutputWorkspaceProps.clear();
  m_unrolledInputWorkspaces.clear();
}

507
//---------------------------------------------------------------------------------------------
508
/** Invoced internally in execute()
509
 */
510
511

bool Algorithm::executeInternal() {
512
  Timer timer;
513
  bool algIsExecuted = false;
514
  AlgorithmManager::Instance().notifyAlgorithmStarting(this->getAlgorithmID());
515
516

  // runtime check for deprecation warning
517
  {
518
    auto *depo = dynamic_cast<DeprecatedAlgorithm *>(this);
519
    if (depo != nullptr)
520
521
      getLogger().error(depo->deprecationMsg(this));
  }
522

523
524
525
526
527
528
529
  // runtime check for deprecated alias warning
  {
    auto *da_alg = dynamic_cast<DeprecatedAlias *>(this);
    if ((da_alg != nullptr) && (this->calledByAlias))
      getLogger().warning(da_alg->deprecationMessage(this));
  }

530
531
532
533
534
  // Register clean up tasks that should happen regardless of the route
  // out of the algorithm. These tasks will get run after this method
  // finishes.
  RunOnFinish onFinish([this]() { this->clearWorkspaceCaches(); });

535
  notificationCenter().postNotification(new StartedNotification(this));
536
  Mantid::Types::Core::DateAndTime startTime;
Nick Draper's avatar
Nick Draper committed
537

538
539
540
541
542
543
544
545
546
547
  // Return a failure if the algorithm hasn't been initialized
  if (!isInitialized()) {
    throw std::runtime_error("Algorithm is not initialised:" + this->name());
  }

  // no logging of input if a child algorithm (except for python child algos)
  if (!m_isChildAlgorithm || m_alwaysStoreInADS)
    logAlgorithmInfo();

  // Check all properties for validity
548
549
  constexpr bool resetTimer{true};
  float timingInit = timer.elapsed(resetTimer);
550
551
  if (!validateProperties()) {
    // Reset name on input workspaces to trigger attempt at collection from ADS
552
    const auto &props = getProperties();
Hahn, Steven's avatar
Hahn, Steven committed
553
    for (auto &prop : props) {
554
      auto wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
555
556
      if (wsProp && !(wsProp->getWorkspace())) {
        // Setting it's name to the same one it already had
557
        prop->setValue(prop->value());
558
      }
559
    }
560
561
    // Try the validation again
    if (!validateProperties()) {
562
      notificationCenter().postNotification(new ErrorNotification(this, "Some invalid Properties found"));
563
      throw std::runtime_error("Some invalid Properties found");
564
    }
565
  }
566
  const float timingPropertyValidation = timer.elapsed(resetTimer);
567

568
569
570
571
  // All properties are now valid - cache workspace properties and histories
  cacheWorkspaceProperties();
  cacheInputWorkspaceHistories();

572
573
574
575
576
577
578
579
  // ----- Check for processing groups -------------
  // default true so that it has the right value at the check below the catch
  // block should checkGroups throw
  bool callProcessGroups = true;
  try {
    // Checking the input is a group. Throws if the sizes are wrong
    callProcessGroups = this->checkGroups();
  } catch (std::exception &ex) {
580
581
    getLogger().error() << "Error in execution of algorithm " << this->name() << "\n" << ex.what() << "\n";
    notificationCenter().postNotification(new ErrorNotification(this, ex.what()));
582
    setResultState(ResultState::Failed);
583
584
585
    if (m_isChildAlgorithm || m_runningAsync || m_rethrow) {
      m_runningAsync = false;
      throw;
586
    }
587
588
    return false;
  }
589

590
591
  const auto executionMode = getExecutionMode();

592
  timingInit += timer.elapsed(resetTimer);
593
  // ----- Perform validation of the whole set of properties -------------
594
595
596
  if ((!callProcessGroups) && (executionMode != Parallel::ExecutionMode::MasterOnly ||
                               communicator().rank() == 0)) // for groups this is called on each
                                                            // workspace separately
597
598
599
600
601
602
603
  {
    std::map<std::string, std::string> errors = this->validateInputs();
    if (!errors.empty()) {
      size_t numErrors = errors.size();
      // Log each issue
      auto &errorLog = getLogger().error();
      auto &warnLog = getLogger().warning();
604
605
      for (auto &error : errors) {
        if (this->existsProperty(error.first))
606
          errorLog << "Invalid value for " << error.first << ": " << error.second << "\n";
607
608
        else {
          numErrors -= 1; // don't count it as an error
609
          warnLog << "validateInputs() references non-existant property \"" << error.first << "\"\n";
610
611
        }
      }
612
613
      // Throw because something was invalid
      if (numErrors > 0) {
614
615
616
617
618
619
        std::stringstream msg;
        msg << "Some invalid Properties found: [ ";
        for (auto &error : errors) {
          msg << error.first << " ";
        }
        msg << "]";
620
        notificationCenter().postNotification(new ErrorNotification(this, "Some invalid Properties found"));
621
        throw std::runtime_error(msg.str());
622
      }
623
    }
624
  }
625
  const float timingInputValidation = timer.elapsed(resetTimer);
626

627
628
629
630
631
632
633
634
635
636
  if (trackingHistory()) {
    // count used for defining the algorithm execution order
    // If history is being recorded we need to count this as a separate
    // algorithm
    // as the history compares histories by their execution number
    ++Algorithm::g_execCount;

    // populate history record before execution so we can record child
    // algorithms in it
    AlgorithmHistory algHist;
637
    m_history = std::make_shared<AlgorithmHistory>(algHist);
638
  }
639

640
641
642
643
  // ----- Process groups -------------
  // If checkGroups() threw an exception but there ARE group workspaces
  // (means that the group sizes were incompatible)
  if (callProcessGroups) {
644
    return doCallProcessGroups(startTime);
645
  }
646

647
648
  // Read or write locks every input/output workspace
  this->lockWorkspaces();
649
  timingInit += timer.elapsed(resetTimer);
650

651
652
653
  // Invoke exec() method of derived class and catch all uncaught exceptions
  try {
    try {
654
      setExecutionState(ExecutionState::Running);
655

656
      startTime = Mantid::Types::Core::DateAndTime::getCurrentTime();
657
      // Call the concrete algorithm's exec method
658
      this->exec(executionMode);
659
      registerFeatureUsage();
660
661
      // Check for a cancellation request in case the concrete algorithm doesn't
      interruption_point();
662
      const float timingExec = timer.elapsed(resetTimer);
663
      // The total runtime including all init steps is used for general logging.
664
      const float duration = timingInit + timingPropertyValidation + timingInputValidation + timingExec;
665
666
667
      // need it to throw before trying to run fillhistory() on an algorithm
      // which has failed
      if (trackingHistory() && m_history) {
668
        m_history->fillAlgorithmHistory(this, startTime, duration, Algorithm::g_execCount);
669
670
        fillHistory();
        linkHistoryWithLastChild();
671
      }
672

673
674
      // Put the output workspaces into the AnalysisDataService - if requested
      if (m_alwaysStoreInADS)
675
        this->store();
676

677
678
679
      // just cache the value internally, it is set at the very end of this
      // method
      algIsExecuted = true;
680

681
      // Log that execution has completed.
682
683
684
685
      getLogger().debug("Time to validate properties: " + std::to_string(timingPropertyValidation) + " seconds\n" +
                        "Time for other input validation: " + std::to_string(timingInputValidation) + " seconds\n" +
                        "Time for other initialization: " + std::to_string(timingInit) + " seconds\n" +
                        "Time to run exec: " + std::to_string(timingExec) + " seconds\n");
686
687
      reportCompleted(duration);
    } catch (std::runtime_error &ex) {
688
689
      m_gcTime = Mantid::Types::Core::DateAndTime::getCurrentTime() +=
          (Mantid::Types::Core::DateAndTime::ONE_SECOND * DELAY_BEFORE_GC);
690
      setResultState(ResultState::Failed);
691
      notificationCenter().postNotification(new ErrorNotification(this, ex.what()));
692
693
      this->unlockWorkspaces();
      if (m_isChildAlgorithm || m_runningAsync || m_rethrow)
Nick Draper's avatar
Nick Draper committed
694
        throw;
695
      else {
696
        getLogger().error() << "Error in execution of algorithm " << this->name() << '\n' << ex.what() << '\n';
697
      }
698
699

    } catch (std::logic_error &ex) {
700
701
      m_gcTime = Mantid::Types::Core::DateAndTime::getCurrentTime() +=
          (Mantid::Types::Core::DateAndTime::ONE_SECOND * DELAY_BEFORE_GC);
702
      notificationCenter().postNotification(new ErrorNotification(this, ex.what()));
703
      setResultState(ResultState::Failed);
704
705
      this->unlockWorkspaces();
      if (m_isChildAlgorithm || m_runningAsync || m_rethrow)
Nick Draper's avatar
Nick Draper committed
706
        throw;
707
      else {
708
        getLogger().error() << "Logic Error in execution of algorithm " << this->name() << '\n' << ex.what() << '\n';
709
710
      }
    }
711
712
  } catch (CancelException &ex) {
    m_runningAsync = false;
713
    getLogger().warning() << this->name() << ": Execution cancelled by user.\n";
714
715
716
    m_gcTime = Mantid::Types::Core::DateAndTime::getCurrentTime() +=
        (Mantid::Types::Core::DateAndTime::ONE_SECOND * DELAY_BEFORE_GC);
    setResultState(ResultState::Failed);
717
    notificationCenter().postNotification(new ErrorNotification(this, ex.what()));
718
    this->unlockWorkspaces();
719

720
721
722
723
724
    throw;
  }
  // Gaudi also specifically catches GaudiException & std:exception.
  catch (std::exception &ex) {
    m_runningAsync = false;
725
    getLogger().error() << "Error in execution of algorithm " << this->name() << ":\n" << ex.what() << "\n";
726
727
    m_gcTime = Mantid::Types::Core::DateAndTime::getCurrentTime() +=
        (Mantid::Types::Core::DateAndTime::ONE_SECOND * DELAY_BEFORE_GC);
728
    setResultState(ResultState::Failed);
729
    notificationCenter().postNotification(new ErrorNotification(this, ex.what()));
730
    this->unlockWorkspaces();
731

732
733
    throw;
  }
734

735
  catch (...) {
Nick Draper's avatar
Nick Draper committed
736
    // Execution failed with an unknown exception object
737
738
    m_runningAsync = false;

739
740
741
    m_gcTime = Mantid::Types::Core::DateAndTime::getCurrentTime() +=
        (Mantid::Types::Core::DateAndTime::ONE_SECOND * DELAY_BEFORE_GC);
    setResultState(ResultState::Failed);
742
743
    notificationCenter().postNotification(new ErrorNotification(this, "UNKNOWN Exception is caught in exec()"));
    getLogger().error() << this->name() << ": UNKNOWN Exception is caught in exec()\n";
744
    this->unlockWorkspaces();
745

746
747
    throw;
  }
748

749
750
  this->unlockWorkspaces();

751
752
  m_gcTime = Mantid::Types::Core::DateAndTime::getCurrentTime() +=
      (Mantid::Types::Core::DateAndTime::ONE_SECOND * DELAY_BEFORE_GC);
753
  if (algIsExecuted) {
754
    setResultState(ResultState::Success);
755
  }
756
  // Only gets to here if algorithm ended normally
757
  notificationCenter().postNotification(new FinishedNotification(this, isExecuted()));
758

759
  return isExecuted();
760
761
762
763
764
765
766
767
768
769
770
771
772
773
}

//---------------------------------------------------------------------------------------------
/** Execute as a Child Algorithm.
 * This runs execute() but catches errors so as to log the name
 * of the failed Child Algorithm, if it fails.
 */
void Algorithm::executeAsChildAlg() {
  bool executed = false;
  try {
    executed = execute();
  } catch (std::runtime_error &) {
    throw;
  }
774

775
  if (!executed) {
776
    throw std::runtime_error("Unable to successfully run ChildAlgorithm " + this->name());
777
778
779
780
781
  }
}

//---------------------------------------------------------------------------------------------
/** Stores any output workspaces into the AnalysisDataService
782
783
784
 *  @throw std::runtime_error If unable to successfully store an output
 * workspace
 */
785
786
787
788
789
790
void Algorithm::store() {
  const std::vector<Property *> &props = getProperties();
  std::vector<int> groupWsIndicies;

  // add any regular/child workspaces first, then add the groups
  for (unsigned int i = 0; i < props.size(); ++i) {
791
    auto *wsProp = dynamic_cast<IWorkspaceProperty *>(props[i]);
792
793
794
    if (wsProp) {
      // check if the workspace is a group, if so remember where it is and add
      // it later
795
      auto group = std::dynamic_pointer_cast<WorkspaceGroup>(wsProp->getWorkspace());
796
797
798
799
800
      if (!group) {
        try {
          wsProp->store();
        } catch (std::runtime_error &) {
          throw;
801
        }
802
      } else {
803
        groupWsIndicies.emplace_back(i);
804
      }
805
    }
806
  }
807

808
809
  // now store workspace groups once their members have been added
  std::vector<int>::const_iterator wsIndex;
810
  for (wsIndex = groupWsIndicies.begin(); wsIndex != groupWsIndicies.end(); ++wsIndex) {
811
    auto *wsProp = dynamic_cast<IWorkspaceProperty *>(props[*wsIndex]);
812
813
814
815
816
    if (wsProp) {
      try {
        wsProp->store();
      } catch (std::runtime_error &) {
        throw;
817
      }
818
819
820
821
822
823
    }
  }
}

//---------------------------------------------------------------------------------------------
/** Create a Child Algorithm.  A call to this method creates a child algorithm
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
 *object.
 *  Using this mechanism instead of creating daughter
 *  algorithms directly via the new operator is prefered since then
 *  the framework can take care of all of the necessary book-keeping.
 *
 *  @param name ::           The concrete algorithm class of the Child Algorithm
 *  @param startProgress ::  The percentage progress value of the overall
 *algorithm where this child algorithm starts
 *  @param endProgress ::    The percentage progress value of the overall
 *algorithm where this child algorithm ends
 *  @param enableLogging ::  Set to false to disable logging from the child
 *algorithm
 *  @param version ::        The version of the child algorithm to create. By
 *default gives the latest version.
 *  @return shared pointer to the newly created algorithm object
 */
840
841
842
Algorithm_sptr Algorithm::createChildAlgorithm(const std::string &name, const double startProgress,
                                               const double endProgress, const bool enableLogging, const int &version) {
  Algorithm_sptr alg = AlgorithmManager::Instance().createUnmanaged(name, version);
843
844
845
846
847
848
849
850
851
852
  setupAsChildAlgorithm(alg, startProgress, endProgress, enableLogging);
  return alg;
}

/** Setup algorithm as child algorithm.
 *
 * Used internally by createChildAlgorithm. Arguments are as documented there.
 * Can also be used manually for algorithms created otherwise. This allows
 * running algorithms that are not declared into the factory as child
 * algorithms. */
853
void Algorithm::setupAsChildAlgorithm(const Algorithm_sptr &alg, const double startProgress, const double endProgress,
854
                                      const bool enableLogging) {
855
  // set as a child
Gagik Vardanyan's avatar
Gagik Vardanyan committed
856
  alg->setChild(true);
857
858
859
860
861
862
  alg->setLogging(enableLogging);

  // Initialise the Child Algorithm
  try {
    alg->initialize();
  } catch (std::runtime_error &) {
863
    throw std::runtime_error("Unable to initialise Child Algorithm '" + alg->name() + "'");
864
  }
865

866
867
868
  // If output workspaces are nameless, give them a temporary name to satisfy
  // validator
  const std::vector<Property *> &props = alg->getProperties();
869
870
871
  for (auto prop : props) {
    auto wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
    if (prop->direction() == Mantid::Kernel::Direction::Output && wsProp) {
872
      if (prop->value().empty() && !wsProp->isOptional()) {
873
        prop->createTemporaryValue();
874
      }
875
876
    }
  }
877

878
  if (startProgress >= 0.0 && endProgress > startProgress && endProgress <= 1.0) {
879
880
881
882
    alg->addObserver(this->progressObserver());
    m_startChildProgress = startProgress;
    m_endChildProgress = endProgress;
  }
883

884
885
886
887
888
  // Before we return the shared pointer, use it to create a weak pointer and
  // keep that in a vector.
  // It will be used this to pass on cancellation requests
  // It must be protected by a critical block so that Child Algorithms can run
  // in parallel safely.
889
  std::weak_ptr<IAlgorithm> weakPtr(alg);
890
  PARALLEL_CRITICAL(Algorithm_StoreWeakPtr) { m_ChildAlgorithms.emplace_back(weakPtr); }
891
892
893
894
895
896
897
898
899
}

//=============================================================================================
//================================== Algorithm History
//========================================
//=============================================================================================

/**
 * Serialize this object to a string. The format is
Nick Draper's avatar
Nick Draper committed
900
 * a json formatted string.
901
902
903
 * @returns This object serialized as a string
 */
std::string Algorithm::toString() const {
904
  ::Json::FastWriter writer;
Nick Draper's avatar
Nick Draper committed
905
906
907
908
909

  return writer.write(toJson());
}

/**
910
911
912
 * Serialize this object to a json object)
 * @returns This object serialized as a json object
 */
Nick Draper's avatar
Nick Draper committed
913
914
::Json::Value Algorithm::toJson() const {
  ::Json::Value root;
915
916
917
918
919

  root["name"] = name();
  root["version"] = this->version();
  root["properties"] = Kernel::PropertyManagerOwner::asJson(false);

Nick Draper's avatar
Nick Draper committed
920
  return root;
921
922
923
924
925
926
927
928
929
930
931
}

//--------------------------------------------------------------------------------------------
/** Construct an object from a history entry.
 *
 * This creates the algorithm and sets all of its properties using the history.
 *
 * @param history :: AlgorithmHistory object
 * @return a shared pointer to the created algorithm.
 */
IAlgorithm_sptr Algorithm::fromHistory(const AlgorithmHistory &history) {
932
933
934
935
  ::Json::Value root;
  ::Json::Value jsonMap;
  ::Json::FastWriter writer;

936
937
938
939
940
  auto props = history.getProperties();
  const size_t numProps(props.size());
  for (size_t i = 0; i < numProps; ++i) {
    PropertyHistory_sptr prop = props[i];
    if (!prop->isDefault()) {
941
      jsonMap[prop->name()] = prop->value();
942
    }
943
  }
944
945
946
947
948

  root["name"] = history.name();
  root["version"] = history.version();
  root["properties"] = jsonMap;

949
  const std::string output = writer.write(root);
950
951
952
  IAlgorithm_sptr alg;

  try {
953
    alg = Algorithm::fromString(output);
954
  } catch (std::invalid_argument &) {
955
956
    throw std::runtime_error("Could not create algorithm from history. "
                             "Is this a child algorithm whose workspaces are not in the ADS?");
957
958
959
960
961
962
963
  }
  return alg;
}

//--------------------------------------------------------------------------------------------
/** De-serializes the algorithm from a string
 *
964
 * @param input :: An input string in the format. The format is
965
966
 * AlgorithmName.version(prop1=value1,prop2=value2,...). If .version is
 * not found the highest found is used.
967
 * @return A pointer to a managed algorithm object
968
 * @throws std::runtime_error if the algorithm cannot be created
969
 */
970
IAlgorithm_sptr Algorithm::fromString(const std::string &input) {
971
972
973
  ::Json::Value root;
  ::Json::Reader reader;
  if (reader.parse(input, root)) {
974
    return fromJson(root);
975
976
977
978
979
  } else {
    throw std::runtime_error("Cannot create algorithm, invalid string format.");
  }
}

980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
/**
 * De-serializes the algorithm from a Json object
 * @param serialized A reference to Json::Value that contains a serialized
 * algorithm object
 * @return A new algorithm object
 * @throws std::runtime_error if the algorithm cannot be created
 */
IAlgorithm_sptr Algorithm::fromJson(const Json::Value &serialized) {
  const std::string algName = serialized["name"].asString();
  const int version = serialized.get("version", -1).asInt();
  auto alg = AlgorithmManager::Instance().createUnmanaged(algName, version);
  alg->initialize();
  alg->setProperties(serialized["properties"]);
  return alg;
}

996
/** Fills History, Algorithm History and Algorithm Parameters
997
 */
998
void Algorithm::fillHistory() {
999
  WorkspaceVector outputWorkspaces;
1000
  if (!isChild()) {
1001
    findWorkspaces(outputWorkspaces, Direction::Output);
1002
  }
1003
  fillHistory(outputWorkspaces);
1004
1005
1006
}

/**
1007
1008
1009
1010
1011
1012
1013
 * Link the name of the output workspaces on this parent algorithm.
 * with the last child algorithm executed to ensure they match in the history.
 *
 * This solves the case where child algorithms use a temporary name and this
 * name needs to match the output name of the parent algorithm so the history
 *can be re-run.
 */
1014
void Algorithm::linkHistoryWithLastChild() {
1015
1016
  if (!m_recordHistoryForChild)
    return;
1017

1018
1019
1020
  // iterate over the algorithms output workspaces
  const auto &algProperties = getProperties();
  for (const auto &prop : algProperties) {
1021
    if (prop->direction() != Kernel::Direction::Output && prop->direction() != Kernel::Direction::InOut)
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037