Skip to content
Snippets Groups Projects
CalculatePaalmanPings.cpp 13.9 KiB
Newer Older
Dan Nixon's avatar
Dan Nixon committed
#include "MantidAPI/AnalysisDataService.h"
#include "MantidQtCustomInterfaces/Indirect/CalculatePaalmanPings.h"
#include "MantidQtCustomInterfaces/UserInputValidator.h"
#include "MantidQtMantidWidgets/WorkspaceSelector.h"

#include <QLineEdit>
#include <QList>
#include <QValidator>
#include <QDoubleValidator>
#include <QRegExpValidator>

using namespace Mantid::API;

namespace {
Mantid::Kernel::Logger g_log("CalculatePaalmanPings");
}

namespace MantidQt {
namespace CustomInterfaces {
CalculatePaalmanPings::CalculatePaalmanPings(QWidget *parent)
    : CorrectionsTab(parent) {
  m_uiForm.setupUi(parent);

  connect(m_uiForm.dsSample, SIGNAL(dataReady(const QString &)), this,
          SLOT(getBeamWidthFromWorkspace(const QString &)));

  QRegExp regex("[A-Za-z0-9\\-\\(\\)]*");
  QValidator *formulaValidator = new QRegExpValidator(regex, this);
  m_uiForm.leSampleChemicalFormula->setValidator(formulaValidator);
  m_uiForm.leCanChemicalFormula->setValidator(formulaValidator);
}

void CalculatePaalmanPings::setup() { doValidation(true); }

void CalculatePaalmanPings::run() {
  // Get correct corrections algorithm
  QString sampleShape = m_uiForm.cbSampleShape->currentText();
  QString algorithmName =
      sampleShape.replace(" ", "") + "PaalmanPingsCorrection";
  algorithmName = algorithmName.replace(
      "Annulus", "Cylinder"); // Use the cylinder algorithm for annulus

  API::BatchAlgorithmRunner::AlgorithmRuntimeProps absCorProps;
  IAlgorithm_sptr absCorAlgo =
      AlgorithmManager::Instance().create(algorithmName.toStdString());
  absCorAlgo->initialize();

  // Sample details
  QString sampleWsName = m_uiForm.dsSample->getCurrentDataName();
  MatrixWorkspace_sptr sampleWs =
      AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
          sampleWsName.toStdString());

  // If not in wavelength then do conversion
  Mantid::Kernel::Unit_sptr sampleXUnit = sampleWs->getAxis(0)->unit();
  if (sampleXUnit->caption() != "Wavelength") {
    g_log.information(
        "Sample workspace not in wavelength, need to convert to continue.");
    absCorProps["SampleWorkspace"] =
        addConvertUnitsStep(sampleWs, "Wavelength");
  } else {
    absCorProps["SampleWorkspace"] = sampleWsName.toStdString();
  }

  double sampleNumberDensity = m_uiForm.spSampleNumberDensity->value();
  absCorAlgo->setProperty("SampleNumberDensity", sampleNumberDensity);

  QString sampleChemicalFormula = m_uiForm.leSampleChemicalFormula->text();
  absCorAlgo->setProperty("SampleChemicalFormula",
                          sampleChemicalFormula.toStdString());

  addShapeSpecificSampleOptions(absCorAlgo, sampleShape);

  // Can details
  bool useCan = m_uiForm.ckUseCan->isChecked();
  if (useCan) {
    QString canWsName = m_uiForm.dsContainer->getCurrentDataName();
    MatrixWorkspace_sptr canWs =
        AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
            canWsName.toStdString());

    // If not in wavelength then do conversion
    Mantid::Kernel::Unit_sptr canXUnit = canWs->getAxis(0)->unit();
    if (canXUnit->caption() != "Wavelength") {
      g_log.information("Container workspace not in wavelength, need to "
                        "convert to continue.");
      absCorProps["CanWorkspace"] = addConvertUnitsStep(canWs, "Wavelength");
    } else {
      absCorProps["CanWorkspace"] = canWsName.toStdString();
    }

    double canNumberDensity = m_uiForm.spCanNumberDensity->value();
    absCorAlgo->setProperty("CanNumberDensity", canNumberDensity);

    QString canChemicalFormula = m_uiForm.leCanChemicalFormula->text();
    absCorAlgo->setProperty("CanChemicalFormula",
                            canChemicalFormula.toStdString());

    addShapeSpecificCanOptions(absCorAlgo, sampleShape);
  }

  std::string eMode = getEMode(sampleWs);
  absCorAlgo->setProperty("EMode", eMode);
  if (eMode == "Indirect")
    absCorAlgo->setProperty("EFixed", getEFixed(sampleWs));

  // Generate workspace names
  int nameCutIndex = sampleWsName.lastIndexOf("_");
  if (nameCutIndex == -1)
    nameCutIndex = sampleWsName.length();

  QString correctionType;
  switch (m_uiForm.cbSampleShape->currentIndex()) {
  case 0:
    correctionType = "flt";
    break;
  case 1:
    correctionType = "cyl";
    break;
  case 2:
    correctionType = "ann";
    break;
  }

  QString outputWsName =
Dan Nixon's avatar
Dan Nixon committed
      sampleWsName.left(nameCutIndex) + "_" + correctionType + "_abs";
Dan Nixon's avatar
Dan Nixon committed
  absCorAlgo->setProperty("OutputWorkspace", outputWsName.toStdString());

  // Add corrections algorithm to queue
  m_batchAlgoRunner->addAlgorithm(absCorAlgo, absCorProps);

  // Add save algorithms if required
  bool save = m_uiForm.ckSave->isChecked();
  if (save)
    addSaveWorkspaceToQueue(outputWsName);

  // Run algorithm queue
  connect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this,
          SLOT(absCorComplete(bool)));
  m_batchAlgoRunner->executeBatchAsync();

  // Set the result workspace for Python script export
  m_pythonExportWsName = outputWsName.toStdString();
}

bool CalculatePaalmanPings::validate() { return doValidation(); }

/**
 * Does validation on the user input.
 *
 * @param silent Set to true to avoid creating an error message
 * @return True if all user input is valid
 */
bool CalculatePaalmanPings::doValidation(bool silent) {
  UserInputValidator uiv;

  uiv.checkDataSelectorIsValid("Sample", m_uiForm.dsSample);

  // Validate chemical formula
  if (uiv.checkFieldIsNotEmpty("Sample Chemical Formula",
                               m_uiForm.leSampleChemicalFormula,
                               m_uiForm.valSampleChemicalFormula))
    uiv.checkFieldIsValid("Sample Chemical Formula",
                          m_uiForm.leSampleChemicalFormula,
                          m_uiForm.valSampleChemicalFormula);

  bool useCan = m_uiForm.ckUseCan->isChecked();
  if (useCan) {
    uiv.checkDataSelectorIsValid("Can", m_uiForm.dsContainer);

    // Validate chemical formula
    if (uiv.checkFieldIsNotEmpty("Can Chemical Formula",
                                 m_uiForm.leCanChemicalFormula,
                                 m_uiForm.valCanChemicalFormula))
      uiv.checkFieldIsValid("Can Chemical Formula",
                            m_uiForm.leCanChemicalFormula,
                            m_uiForm.valCanChemicalFormula);

    // Ensure sample and container are the same kind of data
    QString sampleWsName = m_uiForm.dsSample->getCurrentDataName();
    QString sampleType = sampleWsName.right(sampleWsName.length() -
                                            sampleWsName.lastIndexOf("_"));
    QString containerWsName = m_uiForm.dsContainer->getCurrentDataName();
    QString containerType = containerWsName.right(
        containerWsName.length() - containerWsName.lastIndexOf("_"));

    g_log.debug() << "Sample type is: " << sampleType.toStdString()
                  << std::endl;
    g_log.debug() << "Can type is: " << containerType.toStdString()
                  << std::endl;

    if (containerType != sampleType)
      uiv.addErrorMessage(
          "Sample and can workspaces must contain the same type of data.");
  }

  // Show error mssage if needed
  if (!uiv.isAllInputValid() && !silent)
    emit showMessageBox(uiv.generateErrorMessage());

  return uiv.isAllInputValid();
}

/**
 * Handles completion of the correction algorithm.
 *
 * @param error True of the algorithm failed
 */
void CalculatePaalmanPings::absCorComplete(bool error) {
  disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this,
             SLOT(absCorComplete(bool)));

  if (error) {
    emit showMessageBox("Absorption correction calculation failed.\nSee "
                        "Results Log for more details.");
    return;
  }

  // Convert the spectrum axis of correction factors to Q
  QString sampleWsName = m_uiForm.dsSample->getCurrentDataName();
  MatrixWorkspace_sptr sampleWs =
      AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
          sampleWsName.toStdString());
  WorkspaceGroup_sptr corrections =
      AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(
          m_pythonExportWsName);
  for (size_t i = 0; i < corrections->size(); i++) {
    MatrixWorkspace_sptr factorWs =
        boost::dynamic_pointer_cast<MatrixWorkspace>(corrections->getItem(i));
    if (!factorWs || !sampleWs)
      continue;

    std::string eMode = getEMode(sampleWs);

    if (eMode == "Indirect") {
      API::BatchAlgorithmRunner::AlgorithmRuntimeProps convertSpecProps;
      IAlgorithm_sptr convertSpecAlgo =
          AlgorithmManager::Instance().create("ConvertSpectrumAxis");
      convertSpecAlgo->initialize();
      convertSpecAlgo->setProperty("InputWorkspace", factorWs);
      convertSpecAlgo->setProperty("OutputWorkspace", factorWs->name());
      convertSpecAlgo->setProperty("Target", "ElasticQ");
      convertSpecAlgo->setProperty("EMode", "Indirect");

      try {
        convertSpecAlgo->setProperty("EFixed", getEFixed(factorWs));
      } catch (std::runtime_error &) {
      }

      m_batchAlgoRunner->addAlgorithm(convertSpecAlgo);
    }
  }

  // Run algorithm queue
  connect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this,
          SLOT(postProcessComplete(bool)));
  m_batchAlgoRunner->executeBatchAsync();
}

/**
 * Handles completion of the post processing algorithms.
 *
 * @param error True of the algorithm failed
 */
void CalculatePaalmanPings::postProcessComplete(bool error) {
  disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this,
             SLOT(postProcessComplete(bool)));

  if (error) {
    emit showMessageBox("Correction factor post processing failed.\nSee "
                        "Results Log for more details.");
    return;
  }

  // Handle Mantid plotting
  QString plotType = m_uiForm.cbPlotOutput->currentText();

  if (plotType == "Both" || plotType == "Wavelength")
    plotSpectrum(QString::fromStdString(m_pythonExportWsName));

  if (plotType == "Both" || plotType == "Angle")
    plotTimeBin(QString::fromStdString(m_pythonExportWsName));
}

void CalculatePaalmanPings::loadSettings(const QSettings &settings) {
  m_uiForm.dsSample->readSettings(settings.group());
  m_uiForm.dsContainer->readSettings(settings.group());
}

/**
 * Gets the beam width from the instrument parameters on a given workspace
 * and update the relevant options on the UI.
 *
 * @param wsName Name of the workspace
 */
void CalculatePaalmanPings::getBeamWidthFromWorkspace(const QString &wsName) {
  auto ws = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
      wsName.toStdString());

  if (!ws) {
    g_log.warning() << "Failed to find workspace " << wsName.toStdString()
                    << std::endl;
    return;
  }

  auto instrument = ws->getInstrument();

  const std::string beamWidthParamName = "Workflow.beam-width";
  if (instrument->hasParameter(beamWidthParamName)) {
    QString beamWidth = QString::fromStdString(
        instrument->getStringParameter(beamWidthParamName)[0]);
    double beamWidthValue = beamWidth.toDouble();

    m_uiForm.spCylBeamWidth->setValue(beamWidthValue);
    m_uiForm.spAnnBeamWidth->setValue(beamWidthValue);
  }

  const std::string beamHeightParamName = "Workflow.beam-height";
  if (instrument->hasParameter(beamHeightParamName)) {
    QString beamHeight = QString::fromStdString(
        instrument->getStringParameter(beamHeightParamName)[0]);
    double beamHeightValue = beamHeight.toDouble();

    m_uiForm.spCylBeamHeight->setValue(beamHeightValue);
    m_uiForm.spAnnBeamHeight->setValue(beamHeightValue);
  }
}

/**
 * Sets algorithm properties specific to the sample for a given shape.
 *
 * @param alg Algorithm to set properties of
 * @param shape Sample shape
 */
void CalculatePaalmanPings::addShapeSpecificSampleOptions(IAlgorithm_sptr alg,
                                                          QString shape) {
  if (shape == "FlatPlate") {
    double sampleThickness = m_uiForm.spFlatSampleThickness->value();
    alg->setProperty("SampleThickness", sampleThickness);

    double sampleAngle = m_uiForm.spFlatSampleAngle->value();
    alg->setProperty("SampleAngle", sampleAngle);
  } else if (shape == "Cylinder") {
    alg->setProperty("SampleInnerRadius", 0.0);

    double sampleOuterRadius = m_uiForm.spCylSampleOuterRadius->value();
    alg->setProperty("SampleOuterRadius", sampleOuterRadius);

    double beamWidth = m_uiForm.spCylBeamWidth->value();
    alg->setProperty("BeamWidth", beamWidth);

    double beamHeight = m_uiForm.spCylBeamHeight->value();
    alg->setProperty("BeamHeight", beamHeight);

    double stepSize = m_uiForm.spCylStepSize->value();
    alg->setProperty("StepSize", stepSize);
  } else if (shape == "Annulus") {
    double sampleInnerRadius = m_uiForm.spAnnSampleInnerRadius->value();
    alg->setProperty("SampleInnerRadius", sampleInnerRadius);

    double sampleOuterRadius = m_uiForm.spAnnSampleOuterRadius->value();
    alg->setProperty("SampleOuterRadius", sampleOuterRadius);

    double beamWidth = m_uiForm.spAnnBeamWidth->value();
    alg->setProperty("BeamWidth", beamWidth);

    double beamHeight = m_uiForm.spAnnBeamHeight->value();
    alg->setProperty("BeamHeight", beamHeight);

    double stepSize = m_uiForm.spAnnStepSize->value();
    alg->setProperty("StepSize", stepSize);
  }
}

/**
 * Sets algorithm properties specific to the container for a given shape.
 *
 * @param alg Algorithm to set properties of
 * @param shape Sample shape
 */
void CalculatePaalmanPings::addShapeSpecificCanOptions(IAlgorithm_sptr alg,
                                                       QString shape) {
  if (shape == "FlatPlate") {
    double canFrontThickness = m_uiForm.spFlatCanFrontThickness->value();
    alg->setProperty("CanFrontThickness", canFrontThickness);

    double canBackThickness = m_uiForm.spFlatCanBackThickness->value();
    alg->setProperty("SampleThickness", canBackThickness);
  } else if (shape == "Cylinder") {
    double canOuterRadius = m_uiForm.spCylCanOuterRadius->value();
    alg->setProperty("CanOuterRadius", canOuterRadius);
  } else if (shape == "Annulus") {
    double canOuterRadius = m_uiForm.spAnnCanOuterRadius->value();
    alg->setProperty("CanOuterRadius", canOuterRadius);
  }
}

} // namespace CustomInterfaces
} // namespace MantidQt