Skip to content
Snippets Groups Projects
Algorithm.cpp 57.9 KiB
Newer Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAPI/Algorithm.h"
#include "MantidAPI/AlgorithmHistory.h"
#include "MantidAPI/AlgorithmProxy.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/DeprecatedAlgorithm.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/MemoryManager.h"
#include "MantidAPI/IWorkspaceProperty.h"
#include "MantidAPI/WorkspaceGroup.h"
Nick Draper's avatar
Nick Draper committed
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/EmptyValues.h"
#include "MantidKernel/DateAndTime.h"
#include "MantidKernel/MultiThreaded.h"
#include "MantidKernel/Strings.h"
#include "MantidKernel/Timer.h"
Nick Draper's avatar
Nick Draper committed
#include "MantidKernel/UsageService.h"

#include <boost/algorithm/string/regex.hpp>
#include <boost/weak_ptr.hpp>
#include <Poco/ActiveMethod.h>
#include <Poco/ActiveResult.h>
#include <Poco/NotificationCenter.h>
#include <Poco/RWLock.h>
Nick Draper's avatar
Nick Draper committed
#include <Poco/StringTokenizer.h>
#include <Poco/Void.h>
Nick Draper's avatar
Nick Draper committed

#include <json/json.h>
namespace Mantid {
namespace API {
namespace {
/// Separator for workspace types in workspaceMethodOnTypes member
const std::string WORKSPACE_TYPES_SEPARATOR = ";";

class WorkspacePropertyValueIs {
public:
  explicit WorkspacePropertyValueIs(const std::string &value)
      : m_value(value){};
  bool operator()(IWorkspaceProperty *property) {
    Property *prop = dynamic_cast<Property *>(property);
    if (!prop)
      return false;
    return prop->value() == m_value;
  }

private:
  const std::string &m_value;
}

// 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),
      m_log("Algorithm"), g_log(m_log), m_groupSize(0), m_executeAsync(NULL),
      m_notificationCenter(NULL), m_progressObserver(NULL),
      m_isInitialized(false), m_isExecuted(false), m_isChildAlgorithm(false),
      m_recordHistoryForChild(false), m_alwaysStoreInADS(false),
      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) {}

/// Virtual destructor
Algorithm::~Algorithm() {
  delete m_notificationCenter;
  delete m_executeAsync;
  delete m_progressObserver;

  // Free up any memory available.
  Mantid::API::MemoryManager::Instance().releaseFreeMemory();
}

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

//---------------------------------------------------------------------------------------------
/// Has the Algorithm already been initialized
bool Algorithm::isInitialized() const { return m_isInitialized; }

/// Has the Algorithm already been executed
bool Algorithm::isExecuted() const { return m_isExecuted; }

//---------------------------------------------------------------------------------------------
/// Set the Algorithm initialized state
void Algorithm::setInitialized() { m_isInitialized = true; }

/** Set the executed flag to the specified state
// Public in Gaudi - don't know why and will leave here unless we find a reason
otherwise
//     Also don't know reason for different return type and argument.
@param state :: New executed state
*/
void Algorithm::setExecuted(bool state) { m_isExecuted = state; }

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

/** To set whether algorithm is a child.
*  @param isChild :: True - the algorithm is a child algorithm.  False - this is
* a full managed algorithm.
*/
void Algorithm::setChild(const bool isChild) { m_isChildAlgorithm = isChild; }

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

/** 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.
bool Algorithm::isRunning() const {
  Poco::FastMutex::ScopedLock _lock(m_mutex);
  return m_running;
}

//---------------------------------------------------------------------------------------------
/**  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.
*/
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 {
  Poco::StringTokenizer tokenizer(category(), categorySeparator(),
                                  Poco::StringTokenizer::TOK_TRIM |
                                      Poco::StringTokenizer::TOK_IGNORE_EMPTY);

  std::vector<std::string> res(tokenizer.begin(), tokenizer.end());
  const DeprecatedAlgorithm *depo =
      dynamic_cast<const DeprecatedAlgorithm *>(this);
  if (depo != NULL) {
    res.push_back("Deprecated");
  }
  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 {
  Poco::StringTokenizer tokenizer(this->workspaceMethodOnTypes(),
                                  WORKSPACE_TYPES_SEPARATOR,
                                  Poco::StringTokenizer::TOK_TRIM |
                                      Poco::StringTokenizer::TOK_IGNORE_EMPTY);
  std::vector<std::string> res;
  res.reserve(tokenizer.count());
  for (auto iter = tokenizer.begin(); iter != tokenizer.end(); ++iter) {
    res.push_back(*iter);
  }
  return res;
}

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

//=============================================================================================
//================================== Initialization
//===========================================
//=============================================================================================

//---------------------------------------------------------------------------------------------
/** Initialization method invoked by the framework. This method is responsible
*  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
*
*/
void Algorithm::initialize() {
  // Bypass the initialization if the algorithm has already been initialized.
  if (m_isInitialized)
    return;

  g_log.setName(this->name());
  try {
    try {
      this->init();
    } catch (std::runtime_error &) {
      throw;
    // Indicate that this Algorithm has been initialized to prevent duplicate
    // attempts.
    setInitialized();
  } 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>();
}

//---------------------------------------------------------------------------------------------
/** Go through the properties and cache the input/output
 * workspace properties for later use.
 */
void Algorithm::cacheWorkspaceProperties() {
  // Cache the list of the in/out workspace properties
  m_inputWorkspaceProps.clear();
  m_outputWorkspaceProps.clear();
  m_pureOutputWorkspaceProps.clear();
  const std::vector<Property *> &props = this->getProperties();
  for (size_t i = 0; i < props.size(); i++) {
    Property *prop = props[i];
    IWorkspaceProperty *wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
    if (wsProp) {
      switch (prop->direction()) {
      case Kernel::Direction::Input:
        m_inputWorkspaceProps.push_back(wsProp);
        break;
      case Kernel::Direction::InOut:
        m_inputWorkspaceProps.push_back(wsProp);
        m_outputWorkspaceProps.push_back(wsProp);
        break;
      case Kernel::Direction::Output:
        m_outputWorkspaceProps.push_back(wsProp);
        m_pureOutputWorkspaceProps.push_back(wsProp);
        break;
      default:
        throw std::logic_error(
            "Unexpected property direction found for property " + prop->name() +
            " of algorithm " + this->name());
    } // is a ws property
  }   // each property
}

//=============================================================================================
//================================== Execution
//================================================
//=============================================================================================

//---------------------------------------------------------------------------------------------
/** 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();
  for (size_t i = 0; i < m_outputWorkspaceProps.size(); i++) {
    Workspace_sptr ws = m_outputWorkspaceProps[i]->getWorkspace();
    if (ws) {
      // The workspace property says to do locking,
      // AND it has NOT already been write-locked
      if (m_outputWorkspaceProps[i]->isLocking() &&
          std::find(m_writeLockedWorkspaces.begin(),
                    m_writeLockedWorkspaces.end(),
                    ws) == m_writeLockedWorkspaces.end()) {
        // Write-lock it if not already
        debugLog << "Write-locking " << ws->getName() << std::endl;
        ws->getLock()->writeLock();
        m_writeLockedWorkspaces.push_back(ws);
  // Next read-lock the input workspaces
  for (size_t i = 0; i < m_inputWorkspaceProps.size(); i++) {
    Workspace_sptr ws = m_inputWorkspaceProps[i]->getWorkspace();
    if (ws) {
      // The workspace property says to do locking,
      // AND it has NOT already been write-locked
      if (m_inputWorkspaceProps[i]->isLocking() &&
          std::find(m_writeLockedWorkspaces.begin(),
                    m_writeLockedWorkspaces.end(),
                    ws) == m_writeLockedWorkspaces.end()) {
        // Read-lock it if not already write-locked
        debugLog << "Read-locking " << ws->getName() << std::endl;
        ws->getLock()->readLock();
        m_readLockedWorkspaces.push_back(ws);
      }
  }
}

//---------------------------------------------------------------------------------------------
/** Unlock any previously locked workspaces
 *
 */
void Algorithm::unlockWorkspaces() {
  // Do not lock workspace for child algos
  if (this->isChild())
    return;
  auto &debugLog = g_log.debug();
  for (size_t i = 0; i < m_writeLockedWorkspaces.size(); i++) {
    Workspace_sptr ws = m_writeLockedWorkspaces[i];
    if (ws) {
      debugLog << "Unlocking " << ws->getName() << std::endl;
      ws->getLock()->unlock();
  }
  for (size_t i = 0; i < m_readLockedWorkspaces.size(); i++) {
    Workspace_sptr ws = m_readLockedWorkspaces[i];
    if (ws) {
      debugLog << "Unlocking " << ws->getName() << std::endl;
      ws->getLock()->unlock();
  // Don't double-unlock workspaces
  m_readLockedWorkspaces.clear();
  m_writeLockedWorkspaces.clear();
}

//---------------------------------------------------------------------------------------------
/** The actions to be performed by the algorithm on a dataset. This method is
*  invoked for top level algorithms by the application manager.
*  This method invokes exec() method.
*  For Child Algorithms either the execute() method or exec() method
*  must be EXPLICITLY invoked by  the parent algorithm.
*
*  @throw runtime_error Thrown if algorithm or Child Algorithm cannot be
*executed
*  @return true if executed successfully.
*/
bool Algorithm::execute() {
  AlgorithmManager::Instance().notifyAlgorithmStarting(this->getAlgorithmID());
  {
    DeprecatedAlgorithm *depo = dynamic_cast<DeprecatedAlgorithm *>(this);
    if (depo != NULL)
      getLogger().error(depo->deprecationMsg(this));
  }
  // Start by freeing up any memory available.
  Mantid::API::MemoryManager::Instance().releaseFreeMemory();
  notificationCenter().postNotification(new StartedNotification(this));
  Mantid::Kernel::DateAndTime start_time;
Nick Draper's avatar
Nick Draper committed

  // Return a failure if the algorithm hasn't been initialized
  if (!isInitialized()) {
    throw std::runtime_error("Algorithm is not initialised:" + this->name());
  }

  // Cache the workspace in/out properties for later use
  cacheWorkspaceProperties();

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

  // Check all properties for validity
  if (!validateProperties()) {
    // Reset name on input workspaces to trigger attempt at collection from ADS
    const std::vector<Property *> &props = getProperties();
    for (unsigned int i = 0; i < props.size(); ++i) {
      IWorkspaceProperty *wsProp = dynamic_cast<IWorkspaceProperty *>(props[i]);
      if (wsProp && !(wsProp->getWorkspace())) {
        // Setting it's name to the same one it already had
        props[i]->setValue(props[i]->value());
    // Try the validation again
    if (!validateProperties()) {
      notificationCenter().postNotification(
          new ErrorNotification(this, "Some invalid Properties found"));
      throw std::runtime_error("Some invalid Properties found");
  // ----- 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()
                        << "\n" << ex.what() << "\n";
    notificationCenter().postNotification(
        new ErrorNotification(this, ex.what()));
    m_running = false;
    if (m_isChildAlgorithm || m_runningAsync || m_rethrow) {
      m_runningAsync = false;
      throw;
  // ----- Perform validation of the whole set of properties -------------
  if (!callProcessGroups) // for groups this is called on each workspace
                          // separately
  {
    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();
      for (auto it = errors.begin(); it != errors.end(); it++) {
        if (this->existsProperty(it->first))
          errorLog << "Invalid value for " << it->first << ": " << it->second
                   << "\n";
        else {
          numErrors -= 1; // don't count it as an error
          warnLog << "validateInputs() references non-existant property \""
                  << it->first << "\"\n";
      // 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");
  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);
  }
  // ----- Process groups -------------
  // If checkGroups() threw an exception but there ARE group workspaces
  // (means that the group sizes were incompatible)
  if (callProcessGroups) {
    // This calls this->execute() again on each member of the group.
    start_time = Mantid::Kernel::DateAndTime::getCurrentTime();
    // Start a timer
    Timer timer;
    // Call the concrete algorithm's exec method
    const bool completed = processGroups();
    // Check for a cancellation request in case the concrete algorithm doesn't
    interruption_point();
    // Get how long this algorithm took to run
    const float duration = timer.elapsed();

    if (completed) {
      // Log that execution has completed.
      reportCompleted(duration,
                      true /*indicat that this is for group processing*/);
    return completed;
  }
  // Read or write locks every input/output workspace
  this->lockWorkspaces();
  // Invoke exec() method of derived class and catch all uncaught exceptions
  try {
    try {
      if (!isChild()) {
        Poco::FastMutex::ScopedLock _lock(m_mutex);
        m_running = true;
      start_time = Mantid::Kernel::DateAndTime::getCurrentTime();
      // Start a timer
      Timer timer;
      // Call the concrete algorithm's exec method
      this->exec();
      registerFeatureUsage();
      // Check for a cancellation request in case the concrete algorithm doesn't
      interruption_point();
      // Get how long this algorithm took to run
      const float duration = timer.elapsed();
      // need it to throw before trying to run fillhistory() on an algorithm
      // which has failed
      if (trackingHistory() && m_history) {
        m_history->fillAlgorithmHistory(this, start_time, duration,
                                        Algorithm::g_execCount);
        fillHistory();
        linkHistoryWithLastChild();
      // Put any output workspaces into the AnalysisDataService - if this is not
      // a child algorithm
      if (!isChild() || m_alwaysStoreInADS)
        this->store();
      // RJT, 19/3/08: Moved this up from below the catch blocks
      setExecuted(true);
      // Log that execution has completed.
      reportCompleted(duration);
    } catch (std::runtime_error &ex) {
      this->unlockWorkspaces();
      if (m_isChildAlgorithm || m_runningAsync || m_rethrow)
Nick Draper's avatar
Nick Draper committed
        throw;
      else {
        getLogger().error() << "Error in execution of algorithm "
Nick Draper's avatar
Nick Draper committed
                            << this->name() << std::endl
                            << ex.what() << std::endl;
      notificationCenter().postNotification(
          new ErrorNotification(this, ex.what()));
      m_running = false;
    } catch (std::logic_error &ex) {
      this->unlockWorkspaces();
      if (m_isChildAlgorithm || m_runningAsync || m_rethrow)
Nick Draper's avatar
Nick Draper committed
        throw;
      else {
        getLogger().error() << "Logic Error in execution of algorithm "
Nick Draper's avatar
Nick Draper committed
                            << this->name() << std::endl
                            << ex.what() << std::endl;
      notificationCenter().postNotification(
          new ErrorNotification(this, ex.what()));
      m_running = false;
  } catch (CancelException &ex) {
    m_runningAsync = false;
    m_running = false;
    getLogger().error() << this->name() << ": Execution terminated by user.\n";
    notificationCenter().postNotification(
        new ErrorNotification(this, ex.what()));
    this->unlockWorkspaces();
    throw;
  }
  // Gaudi also specifically catches GaudiException & std:exception.
  catch (std::exception &ex) {
    setExecuted(false);
    m_runningAsync = false;
    m_running = false;

    notificationCenter().postNotification(
        new ErrorNotification(this, ex.what()));
    getLogger().error() << "Error in execution of algorithm " << this->name()
                        << ":\n" << ex.what() << "\n";
    this->unlockWorkspaces();
    throw;
  }
  catch (...) {
    // Execution failed
    setExecuted(false);
    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;
  }
  // Unlock the locked workspaces
  this->unlockWorkspaces();

  notificationCenter().postNotification(
      new FinishedNotification(this, isExecuted()));
  // Only gets to here if algorithm ended normally
  // Free up any memory available.
  Mantid::API::MemoryManager::Instance().releaseFreeMemory();
  return isExecuted();
}

//---------------------------------------------------------------------------------------------
/** 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;
  }
  if (!executed) {
    throw std::runtime_error("Unable to successfully run ChildAlgorithm " +
                             this->name());
  }
}

//---------------------------------------------------------------------------------------------
/** Stores any output workspaces into the AnalysisDataService
*  @throw std::runtime_error If unable to successfully store an output workspace
*/
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) {
    IWorkspaceProperty *wsProp = dynamic_cast<IWorkspaceProperty *>(props[i]);
    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;
      } else {
        groupWsIndicies.push_back(i);
  // now store workspace groups once their members have been added
  std::vector<int>::const_iterator wsIndex;
  for (wsIndex = groupWsIndicies.begin(); wsIndex != groupWsIndicies.end();
       ++wsIndex) {
    IWorkspaceProperty *wsProp =
        dynamic_cast<IWorkspaceProperty *>(props[*wsIndex]);
    if (wsProp) {
      try {
        wsProp->store();
      } catch (std::runtime_error &) {
        throw;
    }
  }
}

//---------------------------------------------------------------------------------------------
/** Create a Child Algorithm.  A call to this method creates a child algorithm
*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
*/
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);
  // set as a child
  alg->setChild(true);
  alg->setLogging(enableLogging);

  // Initialise the Child Algorithm
  try {
    alg->initialize();
  } catch (std::runtime_error &) {
    throw std::runtime_error("Unable to initialise Child Algorithm '" + name +
                             "'");
  }
  // If output workspaces are nameless, give them a temporary name to satisfy
  // validator
  const std::vector<Property *> &props = alg->getProperties();
  for (unsigned int i = 0; i < props.size(); ++i) {
    auto wsProp = dynamic_cast<IWorkspaceProperty *>(props[i]);
    if (props[i]->direction() == Mantid::Kernel::Direction::Output && wsProp) {
      if (props[i]->value().empty()) {
        props[i]->createTemporaryValue();
  if (startProgress >= 0 && endProgress > startProgress && endProgress <= 1.) {
    alg->addObserver(this->progressObserver());
    m_startChildProgress = startProgress;
    m_endChildProgress = endProgress;
  }
  // 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) {
    m_ChildAlgorithms.push_back(weakPtr);
  }
Nick Draper's avatar
Nick Draper committed

  return alg;
}

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

/**
 * Serialize this object to a string. The format is
Nick Draper's avatar
Nick Draper committed
 * a json formatted string.
 * @returns This object serialized as a string
 */
std::string Algorithm::toString() const {
  ::Json::FastWriter writer;
Nick Draper's avatar
Nick Draper committed

  return writer.write(toJson());
}

/**
* Serialize this object to a json object)
* @returns This object serialized as a json object
*/
::Json::Value Algorithm::toJson() const {
  ::Json::Value root;

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

Nick Draper's avatar
Nick Draper committed
  return root;
}

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

  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()) {
      jsonMap[prop->name()] = prop->value();

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

  const std::string output = writer.write(root);
  IAlgorithm_sptr alg;

  try {
    alg = Algorithm::fromString(output);
  } 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
 *
* @param input :: An input string in the format. The format is
*        AlgorithmName.version(prop1=value1,prop2=value2,...). If .version is
*not found the
*        highest found is used.
* @return A pointer to a managed algorithm object
*/
IAlgorithm_sptr Algorithm::fromString(const std::string &input) {
  ::Json::Value root;
  ::Json::Reader reader;

  if (reader.parse(input, root)) {
    const std::string algName = root["name"].asString();
    int version = 0;
    try {
      version = root["version"].asInt();
Nick Draper's avatar
Nick Draper committed
    } catch (std::runtime_error &) {
      // do nothing - the next test will catch it
Nick Draper's avatar
Nick Draper committed
    if (version == 0)
      version = -1;
    IAlgorithm_sptr alg =
        AlgorithmManager::Instance().createUnmanaged(algName, version);
    // get properties
    alg->setProperties(root["properties"]);
    return alg;
  } else {
    throw std::runtime_error("Cannot create algorithm, invalid string format.");
  }
}

//-------------------------------------------------------------------------
/** Initialize using proxy algorithm.
 * Call the main initialize method and then copy in the property values.
 * @param proxy :: Initialising proxy algorithm  */
void Algorithm::initializeFromProxy(const AlgorithmProxy &proxy) {
  initialize();
  copyPropertiesFrom(proxy);
  m_algorithmID = proxy.getAlgorithmID();
  setLogging(proxy.isLogging());
  setLoggingOffset(proxy.getLoggingOffset());
  setAlgStartupLogging(proxy.getAlgStartupLogging());
  setChild(proxy.isChild());
}

/** Fills History, Algorithm History and Algorithm Parameters
*/
void Algorithm::fillHistory() {
  // this is not a child algorithm. Add the history algorithm to the
  // WorkspaceHistory object.
  if (!isChild()) {
    // Create two vectors to hold a list of pointers to the input & output
    // workspaces (InOut's go in both)
    std::vector<Workspace_sptr> inputWorkspaces, outputWorkspaces;
    std::vector<Workspace_sptr>::iterator outWS;
    std::vector<Workspace_sptr>::const_iterator inWS;

    findWorkspaceProperties(inputWorkspaces, outputWorkspaces);

    // Loop over the output workspaces
    for (outWS = outputWorkspaces.begin(); outWS != outputWorkspaces.end();
         ++outWS) {
      WorkspaceGroup_sptr wsGroup =
          boost::dynamic_pointer_cast<WorkspaceGroup>(*outWS);

      // Loop over the input workspaces, making the call that copies their
      // history to the output ones
      // (Protection against copy to self is in
      // WorkspaceHistory::copyAlgorithmHistory)
      for (inWS = inputWorkspaces.begin(); inWS != inputWorkspaces.end();
           ++inWS) {
        (*outWS)->history().addHistory((*inWS)->getHistory());

        // Add history to each child of output workspace group
        if (wsGroup) {
          for (size_t i = 0; i < wsGroup->size(); i++) {
            wsGroup->getItem(i)->history().addHistory((*inWS)->getHistory());
      // Add the history for the current algorithm to all the output workspaces
      (*outWS)->history().addHistory(m_history);
      // Add history to each child of output workspace group
      if (wsGroup) {
        for (size_t i = 0; i < wsGroup->size(); i++) {
          wsGroup->getItem(i)->history().addHistory(m_history);
Nick Draper's avatar
Nick Draper committed
        }
  }
  // this is a child algorithm, but we still want to keep the history.
  else if (m_recordHistoryForChild && m_parentHistory) {
    m_parentHistory->addChildHistory(m_history);
  }
}

/**
* 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.
*/
void Algorithm::linkHistoryWithLastChild() {
  if (m_recordHistoryForChild) {
    // iterate over the algorithms output workspaces
    const std::vector<Property *> &algProperties = getProperties();
    std::vector<Property *>::const_iterator it;
    for (it = algProperties.begin(); it != algProperties.end(); ++it) {
      const IWorkspaceProperty *outputProp =