Skip to content
Snippets Groups Projects
LoadPreNexus.cpp 13 KiB
Newer Older
#include <exception>
#include <fstream>
#include <Poco/Path.h>
#include <Poco/File.h>
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/NodeIterator.h>
#include <Poco/DOM/NodeFilter.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/DOM/AutoPtr.h>
#include <Poco/SAX/InputSource.h>
#include "MantidAPI/IEventWorkspace.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/RegisterFileLoader.h"
#include "MantidAPI/WorkspaceOpOverloads.h"
#include "MantidDataHandling/LoadPreNexus.h"
#include "MantidKernel/System.h"
#include "MantidKernel/VisibleWhenProperty.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/ListValidator.h"
#include "MantidDataHandling/LoadEventNexus.h"
#include "MantidDataHandling/LoadTOFRawNexus.h"
using namespace Mantid::Kernel;
using namespace Mantid::API;
using std::size_t;
using std::string;
namespace Mantid {
namespace DataHandling {

DECLARE_FILELOADER_ALGORITHM(LoadPreNexus)

static const string RUNINFO_PARAM("Filename");
static const string MAP_PARAM("MappingFilename");

//----------------------------------------------------------------------------------------------
/// @copydoc Mantid::API::IAlgorithm::name()
const std::string LoadPreNexus::name() const { return "LoadPreNexus"; }

/// @copydoc Mantid::API::IAlgorithm::version()
int LoadPreNexus::version() const { return 1; }

/// @copydoc Mantid::API::IAlgorithm::category()
const std::string LoadPreNexus::category() const {
  return "DataHandling\\PreNexus;Workflow\\DataHandling";
}

/**
 * Return the confidence with with this algorithm can load the file
 * @param descriptor A descriptor for the file
 * @returns An integer specifying the confidence level. 0 indicates it will not
 * be used
 */
int LoadPreNexus::confidence(Kernel::FileDescriptor &descriptor) const {
  const std::string &filename = descriptor.filename();
  if (filename.compare(filename.size() - 12, 12, "_runinfo.xml") == 0)
    return 80;
  else
    return 0;
}

//----------------------------------------------------------------------------------------------
/// @copydoc Mantid::API::Algorithm::init()
void LoadPreNexus::init() {
  // runfile to read in
  declareProperty(
      Kernel::make_unique<FileProperty>(RUNINFO_PARAM, "", FileProperty::Load,
                                        "_runinfo.xml"),
      "The name of the runinfo file to read, including its full or relative "
      "path.");

  // copied (by hand) from LoadEventPreNexus2
  declareProperty(
      Kernel::make_unique<FileProperty>(MAP_PARAM, "",
                                        FileProperty::OptionalLoad, ".dat"),
      "File containing the pixel mapping (DAS pixels to pixel IDs) file "
      "(typically INSTRUMENT_TS_YYYY_MM_DD.dat). The filename will be found "
      "automatically if not specified.");
  auto mustBePositive = boost::make_shared<BoundedValidator<int>>();
  mustBePositive->setLower(1);
  declareProperty("ChunkNumber", EMPTY_INT(), mustBePositive,
                  "If loading the file by sections ('chunks'), this is the "
                  "section number of this execution of the algorithm.");
  declareProperty("TotalChunks", EMPTY_INT(), mustBePositive,
                  "If loading the file by sections ('chunks'), this is the "
                  "total number of sections.");
  // TotalChunks is only meaningful if ChunkNumber is set
  // Would be nice to be able to restrict ChunkNumber to be <= TotalChunks at
  // validation
  setPropertySettings("TotalChunks", make_unique<VisibleWhenProperty>(
                                         "ChunkNumber", IS_NOT_DEFAULT));
  std::vector<std::string> propOptions{"Auto", "Serial", "Parallel"};
  declareProperty("UseParallelProcessing", "Auto",
                  boost::make_shared<StringListValidator>(propOptions),
                  "Use multiple cores for loading the data?\n"
                  "  Auto: Use serial loading for small data sets, parallel "
                  "for large data sets.\n"
                  "  Serial: Use a single core.\n"
                  "  Parallel: Use all available cores.");

  declareProperty(make_unique<PropertyWithValue<bool>>("LoadMonitors", true,
                                                       Direction::Input),
                  "Load the monitors from the file.");
  declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
                                                   Direction::Output),
                  "An output workspace.");
}

//----------------------------------------------------------------------------------------------
/// @copydoc Mantid::API::Algorithm::exec()
void LoadPreNexus::exec() {
  string runinfo = this->getPropertyValue(RUNINFO_PARAM);
  string mapfile = this->getPropertyValue(MAP_PARAM);
  int chunkTotal = this->getProperty("TotalChunks");
  int chunkNumber = this->getProperty("ChunkNumber");
  if (isEmpty(chunkTotal) || isEmpty(chunkNumber)) {
    chunkNumber = EMPTY_INT();
    chunkTotal = EMPTY_INT();
  } else {
    if (chunkNumber > chunkTotal)
      throw std::out_of_range("ChunkNumber cannot be larger than TotalChunks");
  string useParallel = this->getProperty("UseParallelProcessing");
  string wsname = this->getProperty("OutputWorkspace");
  bool loadmonitors = this->getProperty("LoadMonitors");

  // determine the event file names
  Progress prog(this, 0., .1, 1);
  vector<string> eventFilenames;
  string dataDir;
  this->parseRuninfo(runinfo, dataDir, eventFilenames);
  prog.doReport("parsed runinfo file");

  // do math for the progress bar
  size_t numFiles = eventFilenames.size() + 1; // extra 1 is nexus logs
  if (loadmonitors)
    numFiles++;
  double prog_start = .1;
  double prog_delta = (1. - prog_start) / static_cast<double>(numFiles);

  // load event files
  string temp_wsname;

  for (size_t i = 0; i < eventFilenames.size(); i++) {
    if (i == 0)
      temp_wsname = wsname;
      temp_wsname = "__" + wsname + "_temp__";

    IAlgorithm_sptr alg = this->createChildAlgorithm(
        "LoadEventPreNexus", prog_start, prog_start + prog_delta);
    alg->setProperty("EventFilename", dataDir + eventFilenames[i]);
    alg->setProperty("MappingFilename", mapfile);
    alg->setProperty("ChunkNumber", chunkNumber);
    alg->setProperty("TotalChunks", chunkTotal);
    alg->setProperty("UseParallelProcessing", useParallel);
    alg->setPropertyValue("OutputWorkspace", temp_wsname);
    alg->executeAsChildAlg();
    prog_start += prog_delta;
    if (i == 0) {
      m_outputWorkspace = alg->getProperty("OutputWorkspace");
    } else {
      IEventWorkspace_sptr tempws = alg->getProperty("OutputWorkspace");
      // clean up properties before adding data
      Run &run = tempws->mutableRun();
      if (run.hasProperty("gd_prtn_chrg"))
        run.removeProperty("gd_prtn_chrg");
      if (run.hasProperty("proton_charge"))
        run.removeProperty("proton_charge");

      m_outputWorkspace += tempws;
  // load the logs
  this->runLoadNexusLogs(runinfo, dataDir, prog_start, prog_start + prog_delta);
  prog_start += prog_delta;
  // publish output workspace
  this->setProperty("OutputWorkspace", m_outputWorkspace);
  // load the monitor
  if (loadmonitors) {
    this->runLoadMonitors(prog_start, 1.);
}

/**
 * Parse the runinfo file to find the names of the neutron event files.
 *
 * @param runinfo Runinfo file with full path.
 * @param dataDir Directory where the runinfo file lives.
 * @param eventFilenames vector of all possible event files. This is filled by
 *the algorithm.
 */
void LoadPreNexus::parseRuninfo(const string &runinfo, string &dataDir,
                                vector<string> &eventFilenames) {
  eventFilenames.clear();

  // Create a Poco Path object for runinfo filename
  Poco::Path runinfoPath(runinfo, Poco::Path::PATH_GUESS);
  // Now lets get the directory
  Poco::Path dirPath(runinfoPath.parent());
  dataDir = dirPath.absolute().toString();
  g_log.debug() << "Data directory \"" << dataDir << "\"\n";

  std::ifstream in(runinfo.c_str());
  Poco::XML::InputSource src(in);

  Poco::XML::DOMParser parser;
  Poco::AutoPtr<Poco::XML::Document> pDoc = parser.parse(&src);

  Poco::XML::NodeIterator it(pDoc, Poco::XML::NodeFilter::SHOW_ELEMENT);
  Poco::XML::Node *pNode = it.nextNode(); // root node
  while (pNode) {
    if (pNode->nodeName() == "RunInfo") // standard name for this type
      pNode = pNode->firstChild();
      while (pNode) {
        if (pNode->nodeName() == "FileList") {
          pNode = pNode->firstChild();
          while (pNode) {
            if (pNode->nodeName() == "DataList") {
              pNode = pNode->firstChild();
              while (pNode) {
                if (pNode->nodeName() == "scattering") {
                  Poco::XML::Element *element =
                      static_cast<Poco::XML::Element *>(pNode);
                  eventFilenames.push_back(element->getAttribute("name"));
                }
                pNode = pNode->nextSibling();
              }
            } else // search for DataList
              pNode = pNode->nextSibling();
        } else // search for FileList
          pNode = pNode->nextSibling();
    } else // search for RunInfo
      pNode = pNode->nextSibling();
  // report the results to the log
  if (eventFilenames.size() == 1) {
    g_log.debug() << "Found 1 event file: \"" << eventFilenames[0] << "\"\n";
  } else {
    g_log.debug() << "Found " << eventFilenames.size() << " event files:";
    for (auto &eventFilename : eventFilenames) {
      g_log.debug() << "\"" << eventFilename << "\" ";
    g_log.debug() << "\n";
}

/**
 * Load logs from a nexus file onto the workspace.
 *
 * @param runinfo Runinfo file with full path.
 * @param dataDir Directory where the runinfo file lives.
 * @param prog_start Starting position for the progress bar.
 * @param prog_stop Ending position for the progress bar.
 */
void LoadPreNexus::runLoadNexusLogs(const string &runinfo,
                                    const string &dataDir,
                                    const double prog_start,
                                    const double prog_stop) {
  // determine the name of the file "inst_run"
  string shortName = runinfo.substr(runinfo.find_last_of("/\\") + 1);
  shortName = shortName.substr(0, shortName.find("_runinfo.xml"));
  g_log.debug() << "SHORTNAME = \"" << shortName << "\"\n";

  // put together a list of possible locations
  vector<string> possibilities;
  possibilities.push_back(dataDir + shortName +
                          "_event.nxs"); // next to runinfo
  possibilities.push_back(dataDir + shortName + "_histo.nxs");
  possibilities.push_back(dataDir + shortName + ".nxs");
  possibilities.push_back(dataDir + "../NeXus/" + shortName +
                          "_event.nxs"); // in NeXus directory
  possibilities.push_back(dataDir + "../NeXus/" + shortName + "_histo.nxs");
  possibilities.push_back(dataDir + "../NeXus/" + shortName + ".nxs");

  // run the algorithm
  bool loadedLogs = false;
  for (auto &possibility : possibilities) {
    if (Poco::File(possibility).exists()) {
      g_log.information() << "Loading logs from \"" << possibility << "\"\n";
      IAlgorithm_sptr alg =
          this->createChildAlgorithm("LoadNexusLogs", prog_start, prog_stop);
      alg->setProperty("Workspace", m_outputWorkspace);
      alg->setProperty("Filename", possibility);
      alg->setProperty("OverwriteLogs", false);
      alg->executeAsChildAlg();
      loadedLogs = true;
      // Reload instrument so SNAP can use log values
      std::string entry_name = LoadTOFRawNexus::getEntryName(possibility);
      LoadEventNexus::runLoadInstrument(possibility, m_outputWorkspace,
                                        entry_name, this);
      break;
  if (!loadedLogs)
    g_log.notice() << "Did not find a nexus file to load logs from\n";
}

/**
 * Load the monitor files.
 *
 * @param prog_start Starting position for the progress bar.
 * @param prog_stop Ending position for the progress bar.
 */
void LoadPreNexus::runLoadMonitors(const double prog_start,
                                   const double prog_stop) {
  std::string mon_wsname = this->getProperty("OutputWorkspace");
  mon_wsname.append("_monitors");

  try {
    IAlgorithm_sptr alg = this->createChildAlgorithm("LoadPreNexusMonitors",
                                                     prog_start, prog_stop);
    alg->setPropertyValue("RunInfoFilename", this->getProperty(RUNINFO_PARAM));
    alg->setPropertyValue("OutputWorkspace", mon_wsname);
    alg->executeAsChildAlg();
    MatrixWorkspace_sptr mons = alg->getProperty("OutputWorkspace");
    this->declareProperty(
        Kernel::make_unique<WorkspaceProperty<>>("MonitorWorkspace", mon_wsname,
                                                 Direction::Output),
        "Monitors from the Event NeXus file");
    this->setProperty("MonitorWorkspace", mons);
    // Add an internal pointer to monitor workspace in the 'main' workspace
    m_outputWorkspace->setMonitorWorkspace(mons);
  } catch (std::exception &e) {
    g_log.warning() << "Failed to load monitors: " << e.what() << "\n";
  }
}

} // namespace Mantid
} // namespace DataHandling