Skip to content
Snippets Groups Projects
FrameworkManager.cpp 15.6 KiB
Newer Older
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidAPI/InstrumentDataService.h"
#include "MantidAPI/WorkspaceGroup.h"

#include "MantidKernel/Exception.h"
#include "MantidKernel/LibraryManager.h"
#include "MantidKernel/MultiThreaded.h"
#include "MantidKernel/PropertyManagerDataService.h"
#include "MantidKernel/UsageService.h"
#include <boost/algorithm/string/split.hpp>

#include <Poco/ActiveResult.h>
#include <tbb/task_scheduler_init.h>
#include <csignal>
#ifdef MPI_BUILD
#include <boost/mpi.hpp>
#endif

namespace Mantid {
using Kernel::ConfigService;
using Kernel::LibraryManager;
using Kernel::LibraryManagerImpl;
using Kernel::UsageService;
namespace API {
namespace {
/// static logger
Kernel::Logger g_log("FrameworkManager");
/// Key to define the location of the framework plugins
const char *PLUGINS_DIR_KEY = "framework.plugins.directory";
/// Key to define the location of the plugins to exclude from loading
const char *PLUGINS_EXCLUDE_KEY = "framework.plugins.exclude";
/** 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.
}
 * Print current backtrace to the given stream
 * @param os A reference to an output stream
Martyn Gigg's avatar
Martyn Gigg committed
void backtraceToStream(std::ostream &os) {
  void *trace_elems[32];
  int trace_elem_count(backtrace(trace_elems, 32));
  char **stack_syms(backtrace_symbols(trace_elems, trace_elem_count));
  for (int i = 0; i < trace_elem_count; ++i) {
    os << ' ' << stack_syms[i] << '\n';
}

/**
 * Designed as a handler function for std::set_terminate. It prints
 * a header message and then a backtrace to std::cerr. It calls exit
 * with code 1
 */
void terminateHandler() {
  std::cerr << "\n********* UNHANDLED EXCEPTION *********\n";
  try {
    std::rethrow_exception(std::current_exception());
  } catch (const std::exception &exc) {
    std::cerr << "  what(): " << exc.what() << "\n\n";
  } catch (...) {
    std::cerr << "  what(): Unknown exception type. No more information "
                 "available\n\n";
  }
  std::cerr << "Backtrace:\n";
  backtraceToStream(std::cerr);

/**
 * Designed as a SIGSEGV handler for detecting segfaults
 * and printing a backtrace to std::cerr. It exits with
 * code -1
 */
void sigsegvHandler(int) {
  std::cerr << "\n********* SEGMENTATION FAULT *********\n";
  backtraceToStream(std::cerr);
  exit(-1);
}

FrameworkManagerImpl::FrameworkManagerImpl()
    : m_mpi_environment(argc, argv)
  std::set_terminate(terminateHandler);
  std::signal(SIGSEGV, sigsegvHandler);
  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() << '\n';
  disableNexusOutput();
  setNumOMPThreadsToConfigValue();
#ifdef MPI_BUILD
  g_log.notice() << "This MPI process is rank: "
                 << boost::mpi::communicator().rank() << '\n';
  g_log.debug() << "FrameworkManager created.\n";
  asynchronousStartupTasks();
 * Load all plugins from the framework
void FrameworkManagerImpl::loadPlugins() {
  loadPluginsUsingKey(PLUGINS_DIR_KEY, PLUGINS_EXCLUDE_KEY);
}

/**
 * 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);
  static tbb::task_scheduler_init m_init{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
int FrameworkManagerImpl::getNumOMPThreads() const {
/** Clears all memory associated with the AlgorithmManager
 *  and with the Analysis & Instrument data services.
 */
void FrameworkManagerImpl::clear() {
  clearPropertyManagers();
void FrameworkManagerImpl::shutdown() {
Nick Draper's avatar
Nick Draper committed
  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() {
  AnalysisDataService::Instance().clear();
}

/**
 * Clear memory associated with the IDS
 */
void FrameworkManagerImpl::clearInstruments() {
 * Clear memory associated with the PropertyManagers
void FrameworkManagerImpl::clearPropertyManagers() {
  using Kernel::PropertyManagerDataService;
  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->setPropertiesWithString(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) {
    g_log.error() << ex.what() << '\n';
  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
Hahn, Steven's avatar
Hahn, Steven committed
    g_log.error() << "Workspace " << wsName << " could not be found.\n";
/**
 * Load a set of plugins from the path pointed to by the given config key
 * @param locationKey A string containing a key to lookup in the
 * ConfigService
 * @param excludeKey A string
 */
void FrameworkManagerImpl::loadPluginsUsingKey(const std::string &locationKey,
                                               const std::string &excludeKey) {
  const auto &cfgSvc = Kernel::ConfigService::Instance();
  const auto pluginDir = cfgSvc.getString(locationKey);
  if (pluginDir.length() > 0) {
    std::vector<std::string> excludes;
    const auto excludeStr = cfgSvc.getString(excludeKey);
    boost::split(excludes, excludeStr, boost::is_any_of(";"));
    g_log.debug("Loading libraries from '" + pluginDir + "', excluding '" +
                excludeStr + "'");
    LibraryManager::Instance().openLibraries(
        pluginDir, LibraryManagerImpl::NonRecursive, excludes);
  } else {
    g_log.debug("No library directory found in key \"" + locationKey + "\"");
  }
}

/**
 * Set the numeric formatting category of the C locale to classic C.
 */
void FrameworkManagerImpl::setGlobalNumericLocaleToC() {
  // Some languages, for example German, using different decimal separators.
  // By default C/C++ operations attempting to extract numbers from a stream
  // will use the system locale. For those locales where numbers are formatted
  // differently we see issues, particularly with opencascade, where Mantid
  // will hang or throw an exception while trying to parse text.
  //
  // The following tells all numerical extraction operations to use classic
  // C as the locale.
  setlocale(LC_NUMERIC, "C");
}

/// Silence NeXus output
void FrameworkManagerImpl::disableNexusOutput() {
  NXMSetError(nullptr, NexusErrorFunction);
}

/// Starts asynchronous tasks that are done as part of Start-up.
void FrameworkManagerImpl::asynchronousStartupTasks() {
  int instrumentUpdates(0);
  int retVal = Kernel::ConfigService::Instance().getValue(
      "UpdateInstrumentDefinitions.OnStartup", instrumentUpdates);
  if ((retVal == 1) && (instrumentUpdates == 1)) {
    updateInstrumentDefinitions();
  } else {
    g_log.information() << "Instrument updates disabled - cannot update "
                           "instrument definitions.\n";
  }

  int newVersionCheck(0);
  int retValVersionCheck = Kernel::ConfigService::Instance().getValue(
      "CheckMantidVersion.OnStartup", newVersionCheck);
  if ((retValVersionCheck == 1) && (newVersionCheck == 1)) {
    checkIfNewerVersionIsAvailable();
  } else {
    g_log.information() << "Version check disabled.\n";
  }

  setupUsageReporting();
}

Nick Draper's avatar
Nick Draper committed
void FrameworkManagerImpl::setupUsageReporting() {
  int enabled = 0;
  int interval = 0;
  auto &configSvc = ConfigService::Instance();
  int retVal = configSvc.getValue("Usage.BufferCheckInterval", interval);
  auto &usageSvc = UsageService::Instance();
Nick Draper's avatar
Nick Draper committed
  if ((retVal == 1) && (interval > 0)) {
    usageSvc.setInterval(interval);
  }
  retVal = configSvc.getValue("usagereports.enabled", enabled);
  usageSvc.setEnabled((retVal == 1) && (enabled > 0));
  usageSvc.registerStartup();
}

/// Update instrument definitions from github
void FrameworkManagerImpl::updateInstrumentDefinitions() {
  try {
    IAlgorithm *algDownloadInstrument =
        this->createAlgorithm("DownloadInstrument");
    algDownloadInstrument->setAlgStartupLogging(false);
    algDownloadInstrument->executeAsync();
  } catch (Kernel::Exception::NotFoundError &) {
    g_log.debug() << "DowndloadInstrument algorithm is not available - cannot "
                     "update instrument definitions.\n";
  }
}

/// Check if a newer release of Mantid is available
void FrameworkManagerImpl::checkIfNewerVersionIsAvailable() {
  try {
    IAlgorithm *algCheckVersion = this->createAlgorithm("CheckMantidVersion");
    algCheckVersion->setAlgStartupLogging(false);
    algCheckVersion->executeAsync();
  } catch (Kernel::Exception::NotFoundError &) {
    g_log.debug() << "CheckMantidVersion algorithm is not available - cannot "
                     "check if a newer version is available.\n";