Skip to content
Snippets Groups Projects
FrameworkManager.cpp 14 KiB
Newer Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidAPI/InstrumentDataService.h"
#include "MantidAPI/MemoryManager.h"
#include "MantidAPI/PropertyManagerDataService.h"
#include "MantidAPI/WorkspaceGroup.h"
Nick Draper's avatar
Nick Draper committed
#include "MantidKernel/UsageService.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/LibraryManager.h"
#include "MantidKernel/MultiThreaded.h"

#include <Poco/ActiveResult.h>

#ifdef MPI_BUILD
#include <boost/mpi.hpp>
#endif

namespace Mantid {
namespace API {
namespace {
/// static logger
Kernel::Logger g_log("FrameworkManager");
/// Key that that defines the location of the framework plugins
const char *PLUGINS_DIR_KEY = "plugins.directory";
}
/** This is a function called every time NeXuS raises an error.
 * This swallows the errors and outputs nothing.
 *
 * @param data :: data passed in NXMSetError (will be NULL)
 * @param text :: text of the error.
 */
void NexusErrorFunction(void *data, char *text) {
  UNUSED_ARG(data);
  UNUSED_ARG(text);
  // Do nothing.
}
FrameworkManagerImpl::FrameworkManagerImpl()
    : m_mpi_environment()
  // Mantid only understands English...
  setGlobalLocaleToAscii();
  // Setup memory allocation scheme
  Kernel::MemoryOptions::initAllocatorOptions();
  WSADATA wsaData;
  WSAStartup(MAKEWORD(2, 2), &wsaData);
#if defined(_MSC_VER) && _MSC_VER < 1900
  // This causes the exponent to consist of two digits.
  // VC++ >=1900 use standards conforming behaviour and only
  // uses the number of digits required
  g_log.notice() << Mantid::welcomeMessage() << std::endl;
  loadPluginsUsingKey(PLUGINS_DIR_KEY);
  disableNexusOutput();
  setNumOMPThreadsToConfigValue();
#ifdef MPI_BUILD
  g_log.notice() << "This MPI process is rank: "
                 << boost::mpi::communicator().rank() << std::endl;
  g_log.debug() << "FrameworkManager created." << std::endl;
  AsynchronousStartupTasks();
}

/// Destructor
FrameworkManagerImpl::~FrameworkManagerImpl() {}

/// Starts asynchronous tasks that are done as part of Start-up.
void FrameworkManagerImpl::AsynchronousStartupTasks() {
  int updateInstrumentDefinitions = 0;
  int retVal = Kernel::ConfigService::Instance().getValue(
      "UpdateInstrumentDefinitions.OnStartup", updateInstrumentDefinitions);
  if ((retVal == 1) && (updateInstrumentDefinitions == 1)) {
    UpdateInstrumentDefinitions();
  } else {
    g_log.information()
        << "Instrument updates disabled - cannot update instrument definitions."
        << std::endl;
  int checkIfNewerVersionIsAvailable = 0;
  int retValVersionCheck = Kernel::ConfigService::Instance().getValue(
      "CheckMantidVersion.OnStartup", checkIfNewerVersionIsAvailable);
  if ((retValVersionCheck == 1) && (checkIfNewerVersionIsAvailable == 1)) {
    CheckIfNewerVersionIsAvailable();
  } else {
    g_log.information() << "Version check disabled." << std::endl;
Nick Draper's avatar
Nick Draper committed
  setupUsageReporting();
/// Update instrument definitions from github
void FrameworkManagerImpl::UpdateInstrumentDefinitions() {
  try {
    IAlgorithm *algDownloadInstrument =
        this->createAlgorithm("DownloadInstrument");
    algDownloadInstrument->setAlgStartupLogging(false);
    Poco::ActiveResult<bool> result = algDownloadInstrument->executeAsync();
  } catch (Kernel::Exception::NotFoundError &) {
    g_log.debug() << "DowndloadInstrument algorithm is not available - cannot "
                     "update instrument definitions." << std::endl;
NickDraper's avatar
NickDraper committed
/// Check if a newer release of Mantid is available
void FrameworkManagerImpl::CheckIfNewerVersionIsAvailable() {
  try {
    IAlgorithm *algCheckVersion = this->createAlgorithm("CheckMantidVersion");
    algCheckVersion->setAlgStartupLogging(false);
    Poco::ActiveResult<bool> result = algCheckVersion->executeAsync();
  } catch (Kernel::Exception::NotFoundError &) {
    g_log.debug() << "CheckMantidVersion algorithm is not available - cannot "
NickDraper's avatar
NickDraper committed
                     "check if a newer version is available." << std::endl;
/**
 * Load a set of plugins from the path pointed to by the given config key
 * @param key :: A string containing a key to lookup in the ConfigService
 */
void FrameworkManagerImpl::loadPluginsUsingKey(const std::string &key) {
  Kernel::ConfigServiceImpl &config = Kernel::ConfigService::Instance();
  std::string pluginDir = config.getString(key);
  if (pluginDir.length() > 0) {
    g_log.debug("Loading libraries from \"" + pluginDir + "\"");
    Kernel::LibraryManager::Instance().OpenAllLibraries(pluginDir, false);
    g_log.debug("No library directory found in key \"" + key + "\"");
 * Set the global locale for all C++ stream operations to use simple ASCII
 * characters.
 * If the system supports it UTF-8 encoding will be used, otherwise the
void FrameworkManagerImpl::setGlobalLocaleToAscii() {
  // This ensures that all subsequent stream operations interpret everything as
  // simple
  // ASCII. On systems in the UK and US having this as the system default is not
  // an issue.
  // However, systems that have their encoding set differently can see
  // unexpected behavour when
  // translating from string->numeral values. One example is floating-point
  // interpretation in
  // German where a comma is used instead of a period.
  std::locale::global(std::locale::classic());
}

void FrameworkManagerImpl::disableNexusOutput() {
  NXMSetError(NULL, NexusErrorFunction);
}

/**
 * Set the number of OpenMP cores to use based on the config value
 */
void FrameworkManagerImpl::setNumOMPThreadsToConfigValue() {
  // Set the number of threads to use for this process
  int maxCores(0);
  int retVal = Kernel::ConfigService::Instance().getValue(
      "MultiThreaded.MaxCores", maxCores);
  if (retVal > 0 && maxCores > 0) {
    setNumOMPThreads(maxCores);
  }
}

/**
 * Set the number of OpenMP cores to use based on the config value
 * @param nthreads :: The maximum number of threads to use
 */
void FrameworkManagerImpl::setNumOMPThreads(const int nthreads) {
  g_log.debug() << "Setting maximum number of threads to " << nthreads << "\n";
  PARALLEL_SET_NUM_THREADS(nthreads);
}

/**
 * Returns the number of OpenMP threads that will be used
 * @returns The number of OpenMP threads that will be used in the next parallel
 * call
int FrameworkManagerImpl::getNumOMPThreads() const {
/** Clears all memory associated with the AlgorithmManager
 *  and with the Analysis & Instrument data services.
 */
void FrameworkManagerImpl::clear() {
  clearPropertyManagers();
Nick Draper's avatar
Nick Draper committed
void FrameworkManagerImpl::shutdown()
{
  Kernel::UsageService::Instance().shutdown();
/**
 * Clear memory associated with the AlgorithmManager
 */
void FrameworkManagerImpl::clearAlgorithms() {
  AlgorithmManager::Instance().clear();
}

/**
 * Clear memory associated with the ADS
 */
void FrameworkManagerImpl::clearData() {
  Mantid::API::MemoryManager::Instance().releaseFreeMemory();
void FrameworkManagerImpl::clearInstruments() {
 * Clear memory associated with the PropertyManagers
void FrameworkManagerImpl::clearPropertyManagers() {
  PropertyManagerDataService::Instance().clear();
}

/** Creates and initialises an instance of an algorithm
 *  @param algName :: The name of the algorithm required
 *  @param version :: The version of the algorithm
 *  @return A pointer to the created algorithm.
 *          WARNING! DO NOT DELETE THIS POINTER, because it is owned
 *          by a shared pointer in the AlgorithmManager.
 *  @throw NotFoundError Thrown if algorithm requested is not registered
 */
IAlgorithm *FrameworkManagerImpl::createAlgorithm(const std::string &algName,
                                                  const int &version) {
  IAlgorithm *alg = AlgorithmManager::Instance().create(algName, version).get();
  return alg;
}

/** Creates an instance of an algorithm and sets the properties provided
 *  @param algName :: The name of the algorithm required
 *  @param propertiesArray :: A single string containing properties in the
 *                         form "Property1=Value1;Property2=Value2;..."
 *  @param version :: The version of the algorithm
 *  @return A pointer to the created algorithm
 *          WARNING! DO NOT DELETE THIS POINTER, because it is owned
 *          by a shared pointer in the AlgorithmManager.
 *  @throw NotFoundError Thrown if algorithm requested is not registered
 *  @throw std::invalid_argument Thrown if properties string is ill-formed
 */
IAlgorithm *
FrameworkManagerImpl::createAlgorithm(const std::string &algName,
                                      const std::string &propertiesArray,
                                      const int &version) {
  // Use the previous method to create the algorithm
  IAlgorithm *alg = AlgorithmManager::Instance()
                        .create(algName, version)
                        .get(); // createAlgorithm(algName);
  alg->setPropertiesWithSimpleString(propertiesArray);
  return alg;
}

/** Creates an instance of an algorithm, sets the properties provided and
 *       then executes it.
 *  @param algName :: The name of the algorithm required
 *  @param propertiesArray :: A single string containing properties in the
 *                         form "Property1=Value1;Property2=Value2;..."
 *  @param version :: The version of the algorithm
 *  @return A pointer to the executed algorithm
 *          WARNING! DO NOT DELETE THIS POINTER, because it is owned
 *          by a shared pointer in the AlgorithmManager.
 *  @throw NotFoundError Thrown if algorithm requested is not registered
 *  @throw std::invalid_argument Thrown if properties string is ill-formed
 *  @throw runtime_error Thrown if algorithm cannot be executed
 */
IAlgorithm *FrameworkManagerImpl::exec(const std::string &algName,
                                       const std::string &propertiesArray,
                                       const int &version) {
  // Make use of the previous method for algorithm creation and property setting
  IAlgorithm *alg = createAlgorithm(algName, propertiesArray, version);

/** Run any algorithm with a variable number of parameters
 *
 * @param algorithmName
 * @param count :: number of arguments given.
 * @return the algorithm created
 */
IAlgorithm_sptr FrameworkManagerImpl::exec(const std::string &algorithmName,
                                           int count, ...) {
  if (count % 2 == 1) {
    throw std::runtime_error(
        "Must have an even number of parameter/value string arguments");
  // Create the algorithm
  IAlgorithm_sptr alg =
      AlgorithmManager::Instance().createUnmanaged(algorithmName, -1);
  alg->initialize();
  if (!alg->isInitialized())
    throw std::runtime_error(algorithmName + " was not initialized.");

  va_list Params;
  va_start(Params, count);
  for (int i = 0; i < count; i += 2) {
    std::string paramName = va_arg(Params, const char *);
    std::string paramValue = va_arg(Params, const char *);
    alg->setPropertyValue(paramName, paramValue);
  }
  va_end(Params);

  alg->execute();
  return alg;
}

/** Returns a shared pointer to the workspace requested
 *
 *  @throw NotFoundError If workspace is not registered with analysis data
 *service
Workspace *FrameworkManagerImpl::getWorkspace(const std::string &wsName) {
    space = AnalysisDataService::Instance().retrieve(wsName).get();
  } catch (Kernel::Exception::NotFoundError &) {
    throw Kernel::Exception::NotFoundError("Unable to retrieve workspace",
                                           wsName);
  }
  return space;
}

/** Removes and deletes a workspace from the data service store.
 *
 *  @param wsName :: The user-given name for the workspace
 *  @return true if the workspace was found and deleted
 *  @throw NotFoundError Thrown if workspace cannot be found
 */
bool FrameworkManagerImpl::deleteWorkspace(const std::string &wsName) {
  bool retVal = false;
  boost::shared_ptr<Workspace> ws_sptr;
    ws_sptr = AnalysisDataService::Instance().retrieve(wsName);
  } catch (Kernel::Exception::NotFoundError &ex) {
  boost::shared_ptr<WorkspaceGroup> ws_grpsptr =
      boost::dynamic_pointer_cast<WorkspaceGroup>(ws_sptr);
  if (ws_grpsptr) {
    AnalysisDataService::Instance().deepRemoveGroup(wsName);
  // Make sure we drop the references so the memory will get freed when we
  // expect it to
  ws_sptr.reset();
  ws_grpsptr.reset();
    AnalysisDataService::Instance().remove(wsName);
    retVal = true;
  } catch (Kernel::Exception::NotFoundError &) {
    // workspace was not found
    g_log.error() << "Workspace " << wsName << " could not be found."
                  << std::endl;
  Mantid::API::MemoryManager::Instance().releaseFreeMemory();
Nick Draper's avatar
Nick Draper committed
void FrameworkManagerImpl::setupUsageReporting() {
  int enabled = 0;
  int interval = 0;
  int retVal = Kernel::ConfigService::Instance().getValue("Usage.BufferCheckInterval", interval);
  if ((retVal == 1) && (interval > 0)) {
    Kernel::UsageService::Instance().setInterval(interval);
  }
  retVal = Kernel::ConfigService::Instance().getValue("usagereports.enabled", enabled);
Nick Draper's avatar
Nick Draper committed
  Kernel::UsageService::Instance().setEnabled((retVal == 1) && (enabled > 0));
Nick Draper's avatar
Nick Draper committed
  Kernel::UsageService::Instance().registerStartup();
}