Skip to content
Snippets Groups Projects
Algorithm.cpp 64.9 KiB
Newer Older
#include "MantidAPI/AlgorithmHistory.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AlgorithmProxy.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/DeprecatedAlgorithm.h"
#include "MantidAPI/IWorkspaceProperty.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidAPI/WorkspaceHistory.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 "MantidParallel/Communicator.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>
#include <MantidKernel/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(nullptr),
      m_notificationCenter(nullptr), m_progressObserver(nullptr),
      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),
      m_communicator(Kernel::make_unique<Parallel::Communicator>()) {}

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

//=============================================================================================
//================================== 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 { 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 {
  Mantid::Kernel::StringTokenizer tokenizer(
      category(), categorySeparator(),
      Mantid::Kernel::StringTokenizer::TOK_TRIM |
          Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY);
  auto res = tokenizer.asVector();
  const DeprecatedAlgorithm *depo =
      dynamic_cast<const DeprecatedAlgorithm *>(this);
    res.emplace_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 {
  Mantid::Kernel::StringTokenizer tokenizer(
      this->workspaceMethodOnTypes(), WORKSPACE_TYPES_SEPARATOR,
      Mantid::Kernel::StringTokenizer::TOK_TRIM |
          Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY);
  return tokenizer.asVector();
}

/**
 * @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 (auto prop : props) {
    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 (auto &outputWorkspaceProp : m_outputWorkspaceProps) {
    Workspace_sptr ws = outputWorkspaceProp->getWorkspace();
    if (ws) {
      // The workspace property says to do locking,
      // AND it has NOT already been write-locked
      if (outputWorkspaceProp->isLocking() &&
          std::find(m_writeLockedWorkspaces.begin(),
                    m_writeLockedWorkspaces.end(),
                    ws) == m_writeLockedWorkspaces.end()) {
        // Write-lock it if not already
        debugLog << "Write-locking " << ws->getName() << '\n';
        ws->getLock()->writeLock();
        m_writeLockedWorkspaces.push_back(ws);
  // Next read-lock the input workspaces
  for (auto &inputWorkspaceProp : m_inputWorkspaceProps) {
    Workspace_sptr ws = inputWorkspaceProp->getWorkspace();
    if (ws) {
      // The workspace property says to do locking,
      // AND it has NOT already been write-locked
      if (inputWorkspaceProp->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() << '\n';
        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 (auto &ws : m_writeLockedWorkspaces) {
      debugLog << "Unlocking " << ws->getName() << '\n';
      ws->getLock()->unlock();
  for (auto &ws : m_readLockedWorkspaces) {
      debugLog << "Unlocking " << ws->getName() << '\n';
      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);
      getLogger().error(depo->deprecationMsg(this));
  }
  notificationCenter().postNotification(new StartedNotification(this));
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();

  Parallel::ExecutionMode executionMode = getExecutionMode();

  // On non-master ranks, there may be input workspace properties that are null. This implies that the storage mode is Parallel::StorageMode::MasterOnly. We skip anything below such as property validation and exit immediately.
  bool nonMasterExecution = (executionMode == Parallel::ExecutionMode::MasterOnly) && (communicator().rank() != 0);
 /* {
    for (const auto &wsProp : m_inputWorkspaceProps) {
      if (auto ws = wsProp->getWorkspace()) {
        if (ws->storageMode() == Parallel::StorageMode::MasterOnly) {
          nonMasterExecution = true;
          break;
  // no logging of input if a child algorithm (except for python child algos)
  if (!m_isChildAlgorithm || m_alwaysStoreInADS)
    logAlgorithmInfo();

  // Check all properties for validity
  // Skip validation for non-master execution (StorageMode::MasterOnly on
  // non-master rank).
  if (!nonMasterExecution && !validateProperties()) {
    // Reset name on input workspaces to trigger attempt at collection from ADS
    const std::vector<Property *> &props = getProperties();
    for (auto &prop : props) {
      IWorkspaceProperty *wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
      if (wsProp && !(wsProp->getWorkspace())) {
        // Setting it's name to the same one it already had
        prop->setValue(prop->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 &error : errors) {
        if (this->existsProperty(error.first))
          errorLog << "Invalid value for " << error.first << ": "
                   << error.second << "\n";
        else {
          numErrors -= 1; // don't count it as an error
          warnLog << "validateInputs() references non-existant property \""
                  << error.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) {
  // 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()) {
        m_running = true;
      startTime = Mantid::Kernel::DateAndTime::getCurrentTime();
      // Start a timer
      Timer timer;
      // Call the concrete algorithm's exec method
      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, startTime, 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 "
Hahn, Steven's avatar
Hahn, Steven committed
                            << this->name() << '\n' << ex.what() << '\n';
      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 "
Hahn, Steven's avatar
Hahn, Steven committed
                            << this->name() << '\n' << ex.what() << '\n';
      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
  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 (auto prop : props) {
    auto wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
    if (prop->direction() == Mantid::Kernel::Direction::Output && wsProp) {
      if (prop->value().empty()) {
        prop->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 =
          dynamic_cast<IWorkspaceProperty *>(*it);
      if (outputProp) {
        // Check we actually have a workspace, it may have been optional
        Workspace_sptr workspace = outputProp->getWorkspace();
        if (!workspace)
          continue;

        // Check it's an output workspace
        if ((*it)->direction() == Kernel::Direction::Output ||
            (*it)->direction() == Kernel::Direction::InOut) {
          bool linked = false;
          // find child histories with anonymous output workspaces
          auto childHistories = m_history->getChildHistories();
          auto childIter = childHistories.rbegin();
          for (; childIter != childHistories.rend() && !linked; ++childIter) {
            auto props = (*childIter)->getProperties();
            auto propIter = props.begin();
            for (; propIter != props.end() && !linked; ++propIter) {
              // check we have a workspace property