Algorithm.cpp 72.9 KB
Newer Older
1
2
3
4
5
6
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
//     NScD Oak Ridge National Laboratory, European Spallation Source
//     & Institut Laue - Langevin
// 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/AlgorithmProxy.h"
12
#include "MantidAPI/AnalysisDataService.h"
13
#include "MantidAPI/DeprecatedAlgorithm.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/DateAndTime.h"
21
#include "MantidKernel/EmptyValues.h"
22
#include "MantidKernel/MultiThreaded.h"
23
#include "MantidKernel/PropertyWithValue.h"
24
#include "MantidKernel/Strings.h"
25
#include "MantidKernel/Timer.h"
Nick Draper's avatar
Nick Draper committed
26
#include "MantidKernel/UsageService.h"
27

28
29
#include "MantidParallel/Communicator.h"

30
#include <boost/weak_ptr.hpp>
31

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

39
#include <json/json.h>
40

41
#include <map>
42

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

46
47
using namespace Mantid::Kernel;

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

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

private:
66
  const std::string &m_value;
67
};
68
} // namespace
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// 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()
    : PropertyManagerOwner(), m_cancel(false), m_parallelException(false),
99
100
      m_log("Algorithm"), g_log(m_log), m_groupSize(0), m_executeAsync(nullptr),
      m_notificationCenter(nullptr), m_progressObserver(nullptr),
101
102
103
104
105
106
107
108
      m_executionState(ExecutionState::UNINITIALIZED),
      m_resultState(ResultState::INCOMPLETE), m_isExecuted(false),
      m_isChildAlgorithm(false), m_recordHistoryForChild(false),
      m_alwaysStoreInADS(true), m_runningAsync(false), m_running(false),
      m_rethrow(false), m_isAlgStartupLoggingEnabled(true),
      m_startChildProgress(0.), m_endChildProgress(0.), m_algorithmID(this),
      m_singleGroup(-1), m_groupsHaveSimilarNames(false),
      m_inputWorkspaceHistories(),
109
      m_communicator(std::make_unique<Parallel::Communicator>()) {}
110
111

/// Virtual destructor
112
Algorithm::~Algorithm() {}
113
114
115
116
117
118

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

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/// Gets the current execution state
ExecutionState Algorithm::executionState() const { return m_executionState; }

/// Sets the current execution state
void Algorithm::setExecutionState(const ExecutionState state) {
  m_executionState = state;
}

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

/// Sets the result execution state
void Algorithm::setResultState(const ResultState state) {
  m_resultState = state;
}

135
136
//---------------------------------------------------------------------------------------------
/// Has the Algorithm already been initialized
137
138
139
bool Algorithm::isInitialized() const {
  return (m_executionState != ExecutionState::UNINITIALIZED);
}
140
141

/// Has the Algorithm already been executed
142
143
144
bool Algorithm::isExecuted() const {
  return ((executionState() == ExecutionState::FINISHED) && (resultState() == ResultState::SUCCESS)) ;
}
145
146
147

//---------------------------------------------------------------------------------------------
/** To query whether algorithm is a child.
148
149
150
 *  @returns true - the algorithm is a child algorithm.  False - this is a full
 * managed algorithm.
 */
151
152
153
bool Algorithm::isChild() const { return m_isChildAlgorithm; }

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

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

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

180
181
182
183
184
/** 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; }

185
186
187
188
189
190
/** 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.
191
bool Algorithm::isRunning() const { return m_running; }
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

//---------------------------------------------------------------------------------------------
/**  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.
216
 */
217
218
219
220
221
222
223
224
225
void Algorithm::progress(double p, const std::string &msg, double estimatedTime,
                         int progressPrecision) {
  notificationCenter().postNotification(
      new ProgressNotification(this, p, msg, estimatedTime, progressPrecision));
}

//---------------------------------------------------------------------------------------------
/// Function to return all of the categories that contain this algorithm
const std::vector<std::string> Algorithm::categories() const {
226
227
228
229
  Mantid::Kernel::StringTokenizer tokenizer(
      category(), categorySeparator(),
      Mantid::Kernel::StringTokenizer::TOK_TRIM |
          Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY);
230

231
  auto res = tokenizer.asVector();
232

233
  const auto *depo = dynamic_cast<const DeprecatedAlgorithm *>(this);
234
  if (depo != nullptr) {
235
    res.emplace_back("Deprecated");
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
  }
  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 {
252
253
254
255
  Mantid::Kernel::StringTokenizer tokenizer(
      this->workspaceMethodOnTypes(), WORKSPACE_TYPES_SEPARATOR,
      Mantid::Kernel::StringTokenizer::TOK_TRIM |
          Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY);
256
  return tokenizer.asVector();
257
258
259
260
261
262
263
264
265
}

/**
 * @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
266
267
268
269
270
271
272
 *  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
 *
 */
273
274
void Algorithm::initialize() {
  // Bypass the initialization if the algorithm has already been initialized.
275
  if (isInitialized())
276
277
278
    return;

  g_log.setName(this->name());
279
  setLoggingOffset(0);
280
281
282
  try {
    try {
      this->init();
283
      setupSkipValidationMasterOnly();
284
285
    } catch (std::runtime_error &) {
      throw;
286
287
    }

288
289
    // Indicate that this Algorithm has been initialized to prevent duplicate
    // attempts.
290
    setExecutionState(ExecutionState::INITIALIZED);
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  } 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.
 */
std::map<std::string, std::string> Algorithm::validateInputs() {
  return std::map<std::string, std::string>();
}

//---------------------------------------------------------------------------------------------
319
320
/**
 * Go through the properties and cache the input/output
321
322
323
324
325
326
 * workspace properties for later use.
 */
void Algorithm::cacheWorkspaceProperties() {
  m_inputWorkspaceProps.clear();
  m_outputWorkspaceProps.clear();
  m_pureOutputWorkspaceProps.clear();
327
328
329
330
331
332
333
  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:
334
      m_inputWorkspaceProps.emplace_back(wsProp);
335
336
      break;
    case Kernel::Direction::InOut:
337
338
      m_inputWorkspaceProps.emplace_back(wsProp);
      m_outputWorkspaceProps.emplace_back(wsProp);
339
340
      break;
    case Kernel::Direction::Output:
341
342
      m_outputWorkspaceProps.emplace_back(wsProp);
      m_pureOutputWorkspaceProps.emplace_back(wsProp);
343
344
345
346
347
348
349
      break;
    default:
      throw std::logic_error(
          "Unexpected property direction found for property " + prop->name() +
          " of algorithm " + this->name());
    }
  }
350
351
}

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
/**
 * 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())) {
      m_inputWorkspaceHistories.reserve(m_inputWorkspaceHistories.size() +
                                        group->size());
      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;
    if (const auto compValidator =
            dynamic_cast<CompositeValidator *>(validator.get()))
      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) {
    if (prop->direction() != Direction::Input &&
        prop->direction() != Direction::InOut)
      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
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433

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

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

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

492
493
494
495
496
497
  // Don't double-unlock workspaces
  m_readLockedWorkspaces.clear();
  m_writeLockedWorkspaces.clear();
}

//---------------------------------------------------------------------------------------------
498
/** Invoced internally in execute()
499
 */
500
501

bool Algorithm::executeInternal() {
502
  Timer timer;
503
  bool algIsExecuted = false;
504
505
  AlgorithmManager::Instance().notifyAlgorithmStarting(this->getAlgorithmID());
  {
506
    auto *depo = dynamic_cast<DeprecatedAlgorithm *>(this);
507
    if (depo != nullptr)
508
509
      getLogger().error(depo->deprecationMsg(this));
  }
510

511
  notificationCenter().postNotification(new StartedNotification(this));
512
  Mantid::Types::Core::DateAndTime startTime;
Nick Draper's avatar
Nick Draper committed
513

514
515
516
517
518
519
520
521
522
523
  // 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
524
525
  constexpr bool resetTimer{true};
  float timingInit = timer.elapsed(resetTimer);
526
527
  if (!validateProperties()) {
    // Reset name on input workspaces to trigger attempt at collection from ADS
528
    const auto &props = getProperties();
Hahn, Steven's avatar
Hahn, Steven committed
529
    for (auto &prop : props) {
530
      auto wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
531
532
      if (wsProp && !(wsProp->getWorkspace())) {
        // Setting it's name to the same one it already had
533
        prop->setValue(prop->value());
534
      }
535
    }
536
537
538
539
540
    // Try the validation again
    if (!validateProperties()) {
      notificationCenter().postNotification(
          new ErrorNotification(this, "Some invalid Properties found"));
      throw std::runtime_error("Some invalid Properties found");
541
    }
542
  }
543
  const float timingPropertyValidation = timer.elapsed(resetTimer);
544

545
546
547
548
  // All properties are now valid - cache workspace properties and histories
  cacheWorkspaceProperties();
  cacheInputWorkspaceHistories();

549
550
551
552
553
554
555
556
557
  // ----- 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) {
    getLogger().error() << "Error in execution of algorithm " << this->name()
LamarMoore's avatar
LamarMoore committed
558
559
                        << "\n"
                        << ex.what() << "\n";
560
561
562
563
564
565
    notificationCenter().postNotification(
        new ErrorNotification(this, ex.what()));
    m_running = false;
    if (m_isChildAlgorithm || m_runningAsync || m_rethrow) {
      m_runningAsync = false;
      throw;
566
    }
567
568
    return false;
  }
569

570
571
  const auto executionMode = getExecutionMode();

572
  timingInit += timer.elapsed(resetTimer);
573
  // ----- Perform validation of the whole set of properties -------------
574
575
  if ((!callProcessGroups) &&
      (executionMode != Parallel::ExecutionMode::MasterOnly ||
576
577
       communicator().rank() == 0)) // for groups this is called on each
                                    // workspace separately
578
579
580
581
582
583
584
  {
    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();
585
586
587
588
      for (auto &error : errors) {
        if (this->existsProperty(error.first))
          errorLog << "Invalid value for " << error.first << ": "
                   << error.second << "\n";
589
590
591
        else {
          numErrors -= 1; // don't count it as an error
          warnLog << "validateInputs() references non-existant property \""
592
                  << error.first << "\"\n";
593
594
        }
      }
595
596
597
598
599
      // Throw because something was invalid
      if (numErrors > 0) {
        notificationCenter().postNotification(
            new ErrorNotification(this, "Some invalid Properties found"));
        throw std::runtime_error("Some invalid Properties found");
600
      }
601
    }
602
  }
603
  const float timingInputValidation = timer.elapsed(resetTimer);
604

605
606
607
608
609
610
611
612
613
614
615
616
  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;
    m_history = boost::make_shared<AlgorithmHistory>(algHist);
  }
617

618
619
620
621
  // ----- Process groups -------------
  // If checkGroups() threw an exception but there ARE group workspaces
  // (means that the group sizes were incompatible)
  if (callProcessGroups) {
622
    return doCallProcessGroups(startTime);
623
  }
624

625
626
  // Read or write locks every input/output workspace
  this->lockWorkspaces();
627
  timingInit += timer.elapsed(resetTimer);
628

629
630
631
  // Invoke exec() method of derived class and catch all uncaught exceptions
  try {
    try {
632
      setExecutionState(ExecutionState::RUNNING);
633
634
      if (!isChild()) {
        m_running = true;
635
636
      }

637
      startTime = Mantid::Types::Core::DateAndTime::getCurrentTime();
638
      // Call the concrete algorithm's exec method
639
      this->exec(executionMode);
640
      registerFeatureUsage();
641
642
      // Check for a cancellation request in case the concrete algorithm doesn't
      interruption_point();
643
      const float timingExec = timer.elapsed(resetTimer);
644
645
646
      // The total runtime including all init steps is used for general logging.
      const float duration = timingInit + timingPropertyValidation +
                             timingInputValidation + timingExec;
647
648
649
      // need it to throw before trying to run fillhistory() on an algorithm
      // which has failed
      if (trackingHistory() && m_history) {
650
        m_history->fillAlgorithmHistory(this, startTime, duration,
651
652
653
                                        Algorithm::g_execCount);
        fillHistory();
        linkHistoryWithLastChild();
654
      }
655

656
657
      // Put the output workspaces into the AnalysisDataService - if requested
      if (m_alwaysStoreInADS)
658
        this->store();
659

660
661
662
      // just cache the value internally, it is set at the very end of this
      // method
      algIsExecuted = true;
663

664
      // Log that execution has completed.
LamarMoore's avatar
LamarMoore committed
665
666
667
668
669
670
671
672
      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");
673
674
      reportCompleted(duration);
    } catch (std::runtime_error &ex) {
675
676
      setExecutionState(ExecutionState::FINISHED);
      setResultState(ResultState::FAILED);
677
678
      this->unlockWorkspaces();
      if (m_isChildAlgorithm || m_runningAsync || m_rethrow)
Nick Draper's avatar
Nick Draper committed
679
        throw;
680
      else {
LamarMoore's avatar
LamarMoore committed
681
682
683
        getLogger().error()
            << "Error in execution of algorithm " << this->name() << '\n'
            << ex.what() << '\n';
684
      }
685
686
687
688
      notificationCenter().postNotification(
          new ErrorNotification(this, ex.what()));
      m_running = false;
    } catch (std::logic_error &ex) {
689
690
      setExecutionState(ExecutionState::FINISHED);
      setResultState(ResultState::FAILED);
691
692
      this->unlockWorkspaces();
      if (m_isChildAlgorithm || m_runningAsync || m_rethrow)
Nick Draper's avatar
Nick Draper committed
693
        throw;
694
      else {
LamarMoore's avatar
LamarMoore committed
695
696
697
        getLogger().error()
            << "Logic Error in execution of algorithm " << this->name() << '\n'
            << ex.what() << '\n';
698
      }
699
700
701
      notificationCenter().postNotification(
          new ErrorNotification(this, ex.what()));
      m_running = false;
702
    }
703
  } catch (CancelException &ex) {
704
705
    setExecutionState(ExecutionState::FINISHED);
    setResultState(ResultState::FAILED);
706
707
    m_runningAsync = false;
    m_running = false;
708
    getLogger().warning() << this->name() << ": Execution cancelled by user.\n";
709
710
711
712
713
714
715
    notificationCenter().postNotification(
        new ErrorNotification(this, ex.what()));
    this->unlockWorkspaces();
    throw;
  }
  // Gaudi also specifically catches GaudiException & std:exception.
  catch (std::exception &ex) {
716
717
    setExecutionState(ExecutionState::FINISHED);
    setResultState(ResultState::FAILED);
718
719
720
721
722
723
    m_runningAsync = false;
    m_running = false;

    notificationCenter().postNotification(
        new ErrorNotification(this, ex.what()));
    getLogger().error() << "Error in execution of algorithm " << this->name()
LamarMoore's avatar
LamarMoore committed
724
725
                        << ":\n"
                        << ex.what() << "\n";
726
727
728
    this->unlockWorkspaces();
    throw;
  }
729

730
731
  catch (...) {
    // Execution failed
732
733
    setExecutionState(ExecutionState::FINISHED);
    setResultState(ResultState::FAILED);
734
735
736
737
738
739
740
741
742
743
    m_runningAsync = false;
    m_running = false;

    notificationCenter().postNotification(
        new ErrorNotification(this, "UNKNOWN Exception is caught in exec()"));
    getLogger().error() << this->name()
                        << ": UNKNOWN Exception is caught in exec()\n";
    this->unlockWorkspaces();
    throw;
  }
744

745
746
747
748
  // Unlock the locked workspaces
  this->unlockWorkspaces();

  // Only gets to here if algorithm ended normally
749
750
  if (algIsExecuted) {
    setExecutionState(ExecutionState::FINISHED);
751
    setResultState(ResultState::SUCCESS);
752
753
754
755
  }
  notificationCenter().postNotification(
      new FinishedNotification(this, algIsExecuted));
  return algIsExecuted;
756
757
758
759
760
761
762
763
764
765
766
767
768
769
}

//---------------------------------------------------------------------------------------------
/** 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;
  }
770

771
772
773
774
775
776
777
778
  if (!executed) {
    throw std::runtime_error("Unable to successfully run ChildAlgorithm " +
                             this->name());
  }
}

//---------------------------------------------------------------------------------------------
/** Stores any output workspaces into the AnalysisDataService
779
780
781
 *  @throw std::runtime_error If unable to successfully store an output
 * workspace
 */
782
783
784
785
786
787
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) {
788
    auto *wsProp = dynamic_cast<IWorkspaceProperty *>(props[i]);
789
790
791
792
793
794
795
796
797
798
    if (wsProp) {
      // check if the workspace is a group, if so remember where it is and add
      // it later
      auto group =
          boost::dynamic_pointer_cast<WorkspaceGroup>(wsProp->getWorkspace());
      if (!group) {
        try {
          wsProp->store();
        } catch (std::runtime_error &) {
          throw;
799
        }
800
      } else {
801
        groupWsIndicies.emplace_back(i);
802
      }
803
    }
804
  }
805

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

//---------------------------------------------------------------------------------------------
/** Create a Child Algorithm.  A call to this method creates a child algorithm
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
 *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
 */
839
840
841
842
843
844
845
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);
846
847
848
849
850
851
852
853
854
855
856
857
858
859
  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. */
void Algorithm::setupAsChildAlgorithm(Algorithm_sptr alg,
                                      const double startProgress,
                                      const double endProgress,
                                      const bool enableLogging) {
860
  // set as a child
Gagik Vardanyan's avatar
Gagik Vardanyan committed
861
  alg->setChild(true);
862
863
864
865
866
867
  alg->setLogging(enableLogging);

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

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

884
885
  if (startProgress >= 0.0 && endProgress > startProgress &&
      endProgress <= 1.0) {
886
887
888
889
    alg->addObserver(this->progressObserver());
    m_startChildProgress = startProgress;
    m_endChildProgress = endProgress;
  }
890

891
892
893
894
895
896
897
  // 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.
  boost::weak_ptr<IAlgorithm> weakPtr(alg);
  PARALLEL_CRITICAL(Algorithm_StoreWeakPtr) {
898
    m_ChildAlgorithms.emplace_back(weakPtr);
899
900
901
902
903
904
905
906
907
908
  }
}

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

/**
 * Serialize this object to a string. The format is
Nick Draper's avatar
Nick Draper committed
909
 * a json formatted string.
910
911
912
 * @returns This object serialized as a string
 */
std::string Algorithm::toString() const {
913
  ::Json::FastWriter writer;
Nick Draper's avatar
Nick Draper committed
914
915
916
917
918

  return writer.write(toJson());
}

/**
919
920
921
 * Serialize this object to a json object)
 * @returns This object serialized as a json object
 */
Nick Draper's avatar
Nick Draper committed
922
923
::Json::Value Algorithm::toJson() const {
  ::Json::Value root;
924
925
926
927
928

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

Nick Draper's avatar
Nick Draper committed
929
  return root;
930
931
932
933
934
935
936
937
938
939
940
}

//--------------------------------------------------------------------------------------------
/** 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) {
941
942
943
944
  ::Json::Value root;
  ::Json::Value jsonMap;
  ::Json::FastWriter writer;

945
946
947
948
949
  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()) {
950
      jsonMap[prop->name()] = prop->value();
951
    }
952
  }
953
954
955
956
957

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

958
  const std::string output = writer.write(root);
959
960
961
  IAlgorithm_sptr alg;

  try {
962
    alg = Algorithm::fromString(output);
963
964
965
966
967
968
969
970
971
972
973
  } catch (std::invalid_argument &) {
    throw std::runtime_error(
        "Could not create algorithm from history. "
        "Is this a child algorithm whose workspaces are not in the ADS?");
  }
  return alg;
}

//--------------------------------------------------------------------------------------------
/** De-serializes the algorithm from a string
 *
974
 * @param input :: An input string in the format. The format is
975
976
 * AlgorithmName.version(prop1=value1,prop2=value2,...). If .version is
 * not found the highest found is used.
977
 * @return A pointer to a managed algorithm object
978
 * @throws std::runtime_error if the algorithm cannot be created
979
 */
980
IAlgorithm_sptr Algorithm::fromString(const std::string &input) {
981
982
983
  ::Json::Value root;
  ::Json::Reader reader;
  if (reader.parse(input, root)) {
984
    return fromJson(root);
985
986
987
988
989
  } else {
    throw std::runtime_error("Cannot create algorithm, invalid string format.");
  }
}

990
991
992
993
994
995
996
997
998
999
1000
/**
 * 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);