Skip to content
Snippets Groups Projects
Algorithm.h 19.6 KiB
Newer Older
#ifndef MANTID_API_ALGORITHM_H_
#define MANTID_API_ALGORITHM_H_

#include "MantidAPI/IAlgorithm.h"
#include "MantidKernel/PropertyManagerOwner.h"

// -- These headers will (most-likely) be used by every inheriting algorithm
#include "MantidAPI/AlgorithmFactory.h" //for the factory macro
#include "MantidAPI/Progress.h"
#include "MantidAPI/WorkspaceProperty.h"
#include "MantidAPI/WorkspaceOpOverloads.h"
#include "MantidKernel/MultiThreaded.h"
#include "MantidKernel/EmptyValues.h"

#include "MantidParallel/ExecutionMode.h"
#include "MantidParallel/StorageMode.h"

namespace boost {
template <class T> class weak_ptr;
namespace Poco {
template <class R, class A, class O, class S> class ActiveMethod;
template <class O> class ActiveStarter;
class NotificationCenter;
template <class C, class N> class NObserver;
class Void;
Nick Draper's avatar
Nick Draper committed
namespace Json {
class Value;
}

namespace Mantid {
#ifdef MPI_EXPERIMENTAL
namespace Parallel {
class Communicator;
}
#endif
namespace API {
//----------------------------------------------------------------------
// Forward Declaration
//----------------------------------------------------------------------
class AlgorithmProxy;

/**
 Base class from which all concrete algorithm classes should be derived.
 In order for a concrete algorithm class to do anything
 useful the methods init() & exec()  should be overridden.

 Further text from Gaudi file.......
 The base class provides utility methods for accessing
 standard services (event data service etc.); for declaring
 properties which may be configured by the job options
 service; and for creating Child Algorithms.
 The only base class functionality which may be used in the
 constructor of a concrete algorithm is the declaration of
 member variables as properties. All other functionality,
 i.e. the use of services and the creation of Child Algorithms,
 may be used only in initialise() and afterwards (see the
 Gaudi user guide).

 @author Russell Taylor, Tessella Support Services plc
 @author Based on the Gaudi class of the same name (see
 http://proj-gaudi.web.cern.ch/proj-gaudi/)
 Copyright &copy; 2007-10 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
 National Laboratory & European Spallation Source

 This file is part of Mantid.

 Mantid is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.

 Mantid is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

 File change history is stored at: <https://github.com/mantidproject/mantid>.
 Code Documentation is available at: <http://doxygen.mantidproject.org>
 */
class MANTID_API_DLL Algorithm : public IAlgorithm,
                                 public Kernel::PropertyManagerOwner {
public:
  /// Base class for algorithm notifications
  class AlgorithmNotification : public Poco::Notification {
    AlgorithmNotification(const Algorithm *const alg)
        : Poco::Notification(), m_algorithm(alg) {} ///< Constructor
    const IAlgorithm *algorithm() const {
      return m_algorithm;
    } ///< The algorithm
    const IAlgorithm *const m_algorithm; ///< The algorithm
  };

  /// StartedNotification is sent when the algorithm begins execution.
  class StartedNotification : public AlgorithmNotification {
    StartedNotification(const Algorithm *const alg)
        : AlgorithmNotification(alg) {} ///< Constructor
    std::string name() const override {
      return "StartedNotification";
    } ///< class name
  };

  /// FinishedNotification is sent after the algorithm finishes its execution
  class FinishedNotification : public AlgorithmNotification {
    FinishedNotification(const Algorithm *const alg, bool res)
        : AlgorithmNotification(alg), success(res) {} ///< Constructor
    std::string name() const override {
      return "FinishedNotification";
    }             ///< class name
    bool success; ///< true if the finished algorithm was successful or false if
  };

  /// An algorithm can report its progress by sending ProgressNotification. Use
  /// Algorithm::progress(double) function to send a progress notification.
  class ProgressNotification : public AlgorithmNotification {
    ProgressNotification(const Algorithm *const alg, double p,
                         const std::string &msg, double estimatedTime,
                         int progressPrecision)
        : AlgorithmNotification(alg), progress(p), message(msg),
          estimatedTime(estimatedTime), progressPrecision(progressPrecision) {}
    std::string name() const override {
      return "ProgressNotification";
    }                      ///< class name
    double progress;       ///< Current progress. Value must be between 0 and 1.
    std::string message;   ///< Message sent with notification
    double estimatedTime;  ///<Estimated time to completion
    int progressPrecision; ///<Digits of precision to the progress (after the
  /// ErrorNotification is sent when an exception is caught during execution of
  /// the algorithm.
  class ErrorNotification : public AlgorithmNotification {
    ErrorNotification(const Algorithm *const alg, const std::string &str)
        : AlgorithmNotification(alg), what(str) {}
    std::string name() const override {
      return "ErrorNotification";
    }                 ///< class name
    std::string what; ///< message string
  /// CancelException is thrown to cancel execution of the algorithm. Use
  /// Algorithm::cancel() to
  /// terminate an algorithm. The execution will only be stopped if
  /// Algorithm::exec() method calls
  /// periodically Algorithm::interuption_point() which checks if
  /// Algorithm::cancel() has been called
  /// and throws CancelException if needed.
  class CancelException : public std::exception {
    const char *what() const noexcept override { return outMessage.c_str(); }
    std::string outMessage{"Algorithm terminated"};
  //============================================================================
  Algorithm(const Algorithm &) = delete;
  Algorithm &operator=(const Algorithm &) = delete;
  ~Algorithm() override;

  /** @name Algorithm Information */
  /// function to return a name of the algorithm, must be overridden in all
  /// algorithms
  const std::string name() const override = 0;
  /// function to return a version of the algorithm, must be overridden in all
  /// algorithms
  int version() const override = 0;
  /// function returns a summary message that will be displayed in the default
  /// GUI, and in the help.
  const std::string summary() const override = 0;
  /// function to return a category of the algorithm. A default implementation
  /// is provided
  const std::string category() const override { return "Misc"; }
Nick Draper's avatar
Nick Draper committed
  /// Function to return all of the categories that contain this algorithm
  const std::vector<std::string> categories() const override;
  /// Function to return the separator token for the category string. A default
  /// implementation ';' is provided
  const std::string categorySeparator() const override { return ";"; }
  /// function to return any aliases to the algorithm;  A default implementation
  /// is provided
  const std::string alias() const override { return ""; }
  const std::string workspaceMethodName() const override;
  const std::vector<std::string> workspaceMethodOn() const override;
  const std::string workspaceMethodInputProperty() const override;
  /// Algorithm ID. Unmanaged algorithms return 0 (or NULL?) values. Managed
  /// ones have non-zero.
  AlgorithmID getAlgorithmID() const override { return m_algorithmID; }
  /** @name IAlgorithm methods */
  void initialize() override;
  bool execute() override;
  void executeAsChildAlg() override;
  std::map<std::string, std::string> validateInputs() override;
  bool isInitialized() const override;
  bool isExecuted() const override;
  bool isRunning() const override;
  using Kernel::PropertyManagerOwner::getProperty;

  bool isChild() const override;
  void setChild(const bool isChild) override;
  void enableHistoryRecordingForChild(const bool on) override;
  bool isRecordingHistoryForChild() { return m_recordHistoryForChild; }
  void setAlwaysStoreInADS(const bool doStore) override;
  void setRethrows(const bool rethrow) override;
  /** @name Asynchronous Execution */
  Poco::ActiveResult<bool> executeAsync() override;
  void addObserver(const Poco::AbstractObserver &observer) const override;
  void removeObserver(const Poco::AbstractObserver &observer) const override;
  /// Raises the cancel flag.
  void cancel() override;
  /// Returns the cancellation state
  bool getCancel() const { return m_cancel; }
  /// Returns a reference to the logger.
  Kernel::Logger &getLogger() const { return g_log; }
  /// Logging can be disabled by passing a value of false
  void setLogging(const bool value) override { g_log.setEnabled(value); }
  /// returns the status of logging, True = enabled
  bool isLogging() const override { return g_log.getEnabled(); }
Peterson, Peter's avatar
Peterson, Peter committed
  /* Sets the logging priority offset. Values are subtracted from the log level.
   *
   * Example value=1 will turn warning into notice
   * Example value=-1 will turn notice into warning
   */
  void setLoggingOffset(const int value) override {
    g_log.setLevelOffset(value);
  }
  /// returns the logging priority offset
  int getLoggingOffset() const override { return g_log.getLevelOffset(); }
  /// disable Logging of start and end messages
  void setAlgStartupLogging(const bool enabled) override;
  /// get the state of Logging of start and end messages
  bool getAlgStartupLogging() const override;
  /// setting the child start progress
  void setChildStartProgress(const double startProgress) const override {
    m_startChildProgress = startProgress;
  }
  /// setting the child end progress
  void setChildEndProgress(const double endProgress) const override {
    m_endChildProgress = endProgress;
  }
  /** @name Serialization functions */
  //@{
  /// Serialize an object to a string
  std::string toString() const override;
Nick Draper's avatar
Nick Draper committed
  /// Serialize an object to a json object
  ::Json::Value toJson() const;
  /// De-serialize an object from a string
  static IAlgorithm_sptr fromString(const std::string &input);
  static IAlgorithm_sptr fromHistory(const AlgorithmHistory &history);
  virtual boost::shared_ptr<Algorithm> createChildAlgorithm(
      const std::string &name, const double startProgress = -1.,
      const double endProgress = -1., const bool enableLogging = true,
      const int &version = -1);
  /// set whether we wish to track the child algorithm's history and pass it the
  /// parent object to fill.
  void trackAlgorithmHistory(boost::shared_ptr<AlgorithmHistory> parentHist);

  typedef std::vector<boost::shared_ptr<Workspace>> WorkspaceVector;
  void findWorkspaceProperties(WorkspaceVector &inputWorkspaces,
                               WorkspaceVector &outputWorkspaces) const;

  // ------------------ For WorkspaceGroups ------------------------------------
  virtual bool checkGroups();

  virtual bool processGroups();

  void copyNonWorkspaceProperties(IAlgorithm *alg, int periodNum);
#ifdef MPI_EXPERIMENTAL
  const Parallel::Communicator &communicator() const;
  void setCommunicator(const Parallel::Communicator &communicator);
#endif

  /// Virtual method - must be overridden by concrete algorithm
  virtual void init() = 0;
  /// Virtual method - must be overridden by concrete algorithm
  virtual void exec() = 0;
  void exec(Parallel::ExecutionMode executionMode);
  virtual void execDistributed();
  virtual void execMasterOnly();
  virtual void execNonMaster();

  virtual Parallel::ExecutionMode getParallelExecutionMode(
      const std::map<std::string, Parallel::StorageMode> &storageModes) const;

  virtual Parallel::StorageMode
  getStorageModeForOutputWorkspace(const std::string &propertyName) const;

  Parallel::ExecutionMode
  getCorrespondingExecutionMode(Parallel::StorageMode storageMode) const;
  void propagateWorkspaceStorageMode() const;

  /// Returns a semi-colon separated list of workspace types to attach this
  /// algorithm
  virtual const std::string workspaceMethodOnTypes() const { return ""; }

  void cacheWorkspaceProperties();
  void initializeFromProxy(const AlgorithmProxy &);

  void setInitialized();
  void setExecuted(bool state);

  /** @name Progress Reporting functions */
  void progress(double p, const std::string &msg = "",
                double estimatedTime = 0.0, int progressPrecision = 0);
  /// Return a reference to the algorithm's notification dispatcher
  Poco::NotificationCenter &notificationCenter() const;
  /// Observation slot for child algorithm progress notification messages, these
  /// are scaled and then signalled for this algorithm.
  void handleChildProgressNotification(
      const Poco::AutoPtr<ProgressNotification> &pNf);
  /// Return a reference to the algorithm's object that is reporting progress
  const Poco::AbstractObserver &progressObserver() const;

  /// checks that the value was not set by users, uses the value in empty
  /// double/int.
  template <typename NumT> static bool isEmpty(const NumT toCheck);
  /// checks the property is a workspace property
  bool isWorkspaceProperty(const Kernel::Property *const prop) const;
  /// get whether we are tracking the history for this algorithm,
  bool trackingHistory();
  /// Copy workspace history for input workspaces to output workspaces and
  /// record the history for ths algorithm
  virtual void fillHistory();
  /// Set to true to stop execution
  /// Set if an exception is thrown, and not caught, within a parallel region
  std::atomic<bool> m_parallelException;
  friend class WorkspaceHistory; // Allow workspace history loading to adjust
                                 // g_execCount
  static size_t
      g_execCount; ///< Counter to keep track of algorithm execution order
  virtual void setOtherProperties(IAlgorithm *alg,
                                  const std::string &propertyName,
                                  const std::string &propertyValue,
                                  int periodNum);

  /// All the WorkspaceProperties that are Input or InOut. Set in execute()
  std::vector<IWorkspaceProperty *> m_inputWorkspaceProps;
  /// Pointer to the history for the algorithm being executed
  boost::shared_ptr<AlgorithmHistory> m_history;
  /// Logger for this algorithm
  Kernel::Logger m_log;
  Kernel::Logger &g_log;

  /// Pointer to the parent history object (if set)
  boost::shared_ptr<AlgorithmHistory> m_parentHistory;

  /// One vector of workspaces for each input workspace property
  std::vector<WorkspaceVector> m_groups;
  /// Size of the group(s) being processed
  size_t m_groupSize;
  /// distinguish between base processGroups() and overriden/algorithm specific
  /// versions
  bool m_usingBaseProcessGroups = false;
  void lockWorkspaces();
  void unlockWorkspaces();

  void linkHistoryWithLastChild();
  void logAlgorithmInfo() const;
  bool executeAsyncImpl(const Poco::Void &i);
  bool doCallProcessGroups(Mantid::Kernel::DateAndTime &start_time);

  // Report that the algorithm has completed.
  void reportCompleted(const double &duration,
                       const bool groupProcessing = false);
  void registerFeatureUsage() const;
  Parallel::ExecutionMode getExecutionMode() const;
  std::map<std::string, Parallel::StorageMode>
  getInputWorkspaceStorageModes() const;

  // --------------------- Private Members -----------------------------------
  /// Poco::ActiveMethod used to implement asynchronous execution.
  Poco::ActiveMethod<bool, Poco::Void, Algorithm,
                     Poco::ActiveStarter<Algorithm>> *m_executeAsync;
  /// Sends notifications to observers. Observers can subscribe to
  /// notificationCenter
  /// using Poco::NotificationCenter::addObserver(...);
  mutable Poco::NotificationCenter *m_notificationCenter;
  /// Child algorithm progress observer
  mutable Poco::NObserver<Algorithm, ProgressNotification> *m_progressObserver;
  bool m_isInitialized;         ///< Algorithm has been initialized flag
  bool m_isExecuted;            ///< Algorithm is executed flag
  bool m_isChildAlgorithm;      ///< Algorithm is a child algorithm
  bool m_recordHistoryForChild; ///< Flag to indicate whether history should be
  /// recorded. Applicable to child algs only
  bool m_alwaysStoreInADS; ///< Always store in the ADS, even for child algos
  bool m_runningAsync;     ///< Algorithm is running asynchronously
  std::atomic<bool> m_running; ///< Algorithm is running
  bool m_rethrow; ///< Algorithm should rethrow exceptions while executing
  bool m_isAlgStartupLoggingEnabled; /// Whether to log alg startup and
                                     /// closedown messages from the base class
                                     /// (default = true)
  mutable double m_startChildProgress; ///< Keeps value for algorithm's progress
  /// at start of an Child Algorithm
  mutable double m_endChildProgress; ///< Keeps value for algorithm's progress
  /// at Child Algorithm's finish
  AlgorithmID m_algorithmID; ///< Algorithm ID for managed algorithms
  std::vector<boost::weak_ptr<IAlgorithm>> m_ChildAlgorithms; ///< A list of
  /// weak pointers
  /// to any child
  /// algorithms
  /// created
  /// Vector of all the workspaces that have been read-locked
  WorkspaceVector m_readLockedWorkspaces;
  /// Vector of all the workspaces that have been write-locked
  WorkspaceVector m_writeLockedWorkspaces;
  /// All the WorkspaceProperties that are Output or InOut. Set in execute()
  std::vector<IWorkspaceProperty *> m_outputWorkspaceProps;
  /// All the WorkspaceProperties that are Output (not inOut). Set in execute()
  std::vector<IWorkspaceProperty *> m_pureOutputWorkspaceProps;
  /// Pointer to the WorkspaceGroup (if any) for each input workspace property
  std::vector<boost::shared_ptr<WorkspaceGroup>> m_groupWorkspaces;
  /// If only one input is a group, this is its index. -1 if they are all groups
  int m_singleGroup;
  /// All the groups have similar names (group_1, group_2 etc.)
  bool m_groupsHaveSimilarNames;

#ifdef MPI_EXPERIMENTAL
  std::unique_ptr<Parallel::Communicator> m_communicator;
#endif
/// Typedef for a shared pointer to an Algorithm
typedef boost::shared_ptr<Algorithm> Algorithm_sptr;
/* Used to register classes into the factory. creates a global object in an
 * anonymous namespace. The object itself does nothing, but the comma operator
 * is used in the call to its constructor to effect a call to the factory's
 * subscribe method.
 */
#define DECLARE_ALGORITHM(classname)                                           \
  namespace {                                                                  \
  Mantid::Kernel::RegistrationHelper register_alg_##classname((                \
      (Mantid::API::AlgorithmFactory::Instance().subscribe<classname>()), 0)); \
  }