Skip to content
Snippets Groups Projects
MuonFitPropertyBrowser.cpp 57.2 KiB
Newer Older
#include "MantidQtWidgets/Common/MuonFitPropertyBrowser.h"
#include "MantidQtWidgets/Common/PropertyHandler.h"
#include "MantidAPI/FunctionFactory.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/ITableWorkspace.h"
#include "MantidAPI/TableRow.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidKernel/VectorHelper.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/StringEditorFactory.h"
#include "MantidQtWidgets/Common/MuonFitDataSelector.h"
#include "MantidAPI/MultiDomainFunction.h"
// Suppress a warning coming out of code that isn't ours
#if defined(__INTEL_COMPILER)
#pragma warning disable 1125
#elif defined(__GNUC__)
#if (__GNUC__ >= 4 && __GNUC_MINOR__ >= 6)
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
#include "MantidQtWidgets/Common/QtPropertyBrowser/DoubleEditorFactory.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/qteditorfactory.h"
#if defined(__INTEL_COMPILER)
#pragma warning enable 1125
#elif defined(__GNUC__)
#if (__GNUC__ >= 4 && __GNUC_MINOR__ >= 6)
#pragma GCC diagnostic pop
#endif
#endif

#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidAPI/CompositeFunction.h"
#include "MantidAPI/Expression.h"
#include "MantidAPI/IBackgroundFunction.h"
#include "MantidAPI/IPeakFunction.h"

#include "MantidQtWidgets/Common/QtPropertyBrowser/qttreepropertybrowser.h"
#include "MantidQtWidgets/Common/QtPropertyBrowser/qtpropertymanager.h"
#include <Poco/ActiveResult.h>

#include <QSettings>
#include <QMessageBox>
#include <QFormLayout>

#include <QLayout>
#include <QLabel>
#include <QPushButton>
#include <QMenu>
#include <QSignalMapper>

#include <QCheckBox>

namespace {
Mantid::Kernel::Logger g_log("MuonFitPropertyBrowser");
const QString CUSTOM_LABEL{"Custom"};
const QString ALL_GROUPS_LABEL{"All Groups"};
const QString ALL_PAIRS_LABEL{"All Pairs"};
const QString ALL_PERIODS_LABEL{"All Periods"};
namespace MantidQt {
namespace MantidWidgets {
using namespace Mantid::API;
const std::string MuonFitPropertyBrowser::SIMULTANEOUS_PREFIX{"MuonSimulFit_"};

/**
 * Constructor
 * @param parent :: The parent widget - must be an ApplicationWindow
 * @param mantidui :: The UI form for MantidPlot
*/
MuonFitPropertyBrowser::MuonFitPropertyBrowser(QWidget *parent,
                                               QObject *mantidui)
    : FitPropertyBrowser(parent, mantidui), m_widgetSplitter(nullptr),
      m_mainSplitter(nullptr) {}

/**
* Initialise the muon fit property browser.
*/
void MuonFitPropertyBrowser::init() {
  QWidget *w = new QWidget(this);

  QSettings settings;
  settings.beginGroup("Mantid/FitBrowser");

  /* Create function group */
  QtProperty *functionsGroup = m_groupManager->addProperty("Functions");
  QtProperty *settingsGroup(nullptr);
  // Seperates the data and the settings into two seperate categories
  settingsGroup = m_groupManager->addProperty("Data");

  QSettings multiFitSettings;
  multiFitSettings.beginGroup("");

  /* Create function group */
  QtProperty *multiFitSettingsGroup(nullptr);

  // Seperates the data and the settings into two seperate categories
  multiFitSettingsGroup = m_groupManager->addProperty("Data");

  // Have slightly different names as requested by the muon scientists.
  m_startX =
      addDoubleProperty(QString("Start (%1s)").arg(QChar(0x03BC))); //(mu);
  m_endX = addDoubleProperty(QString("End (%1s)").arg(QChar(0x03BC)));

Anthony Lim's avatar
Anthony Lim committed
  m_normalization = m_enumManager->addProperty("Normalization");
  m_keepNorm = m_boolManager->addProperty("Fix Normalization");
  bool keepNorm = settings.value("Fix Normalization", QVariant(false)).toBool();
  m_boolManager->setValue(m_keepNorm, keepNorm);

  m_workspace = m_enumManager->addProperty("Workspace");
  m_workspaceIndex = m_intManager->addProperty("Workspace Index");
  m_output = m_stringManager->addProperty("Output");
  m_minimizer = m_enumManager->addProperty("Minimizer");
  m_minimizers << "Levenberg-Marquardt"
               << "Simplex"
               << "Conjugate gradient (Fletcher-Reeves imp.)"
               << "Conjugate gradient (Polak-Ribiere imp.)"
               << "BFGS";
  m_enumManager->setEnumNames(m_minimizer, m_minimizers);
  m_costFunction = m_enumManager->addProperty("Cost function");
  m_costFunctions << "Least squares"
                  << "Ignore positive peaks";
  m_enumManager->setEnumNames(m_costFunction, m_costFunctions);

  m_plotDiff = m_boolManager->addProperty("Plot Difference");
  bool plotDiff = settings.value("Plot Difference", QVariant(true)).toBool();
  m_boolManager->setValue(m_plotDiff, plotDiff);

  m_evaluationType = m_enumManager->addProperty("Evaluate Function As");
  m_evaluationType->setToolTip(
      "Consider using Histogram fit which may produce more accurate results.");
  m_evaluationTypes << "CentrePoint"
                    << "Histogram";
  m_enumManager->setEnumNames(m_evaluationType, m_evaluationTypes);
  int evaluationType =
      settings.value(m_evaluationType->propertyName(), 0).toInt();
  m_enumManager->setValue(m_evaluationType, evaluationType);

  settingsGroup->addSubProperty(m_workspace);
  settingsGroup->addSubProperty(m_workspaceIndex);
  settingsGroup->addSubProperty(m_startX);
Anthony Lim's avatar
Anthony Lim committed
  settingsGroup->addSubProperty(m_endX);
  settingsGroup->addSubProperty(m_normalization);
  settingsGroup->addSubProperty(m_keepNorm);

  // Disable "Browse" button - use case is that first run will always be the one
  // selected on front tab. User will type in the runs they want rather than
  // using the Browse button. (If they want to "Browse" they can use front tab).
  multiFitSettingsGroup->addSubProperty(m_startX);
  multiFitSettingsGroup->addSubProperty(m_endX);
  m_groupsToFit = m_enumManager->addProperty("Groups/Pairs to fit");
Anthony Lim's avatar
Anthony Lim committed
  m_groupsToFitOptions << ALL_GROUPS_LABEL << ALL_PAIRS_LABEL << CUSTOM_LABEL;
  m_showGroupValue << "groups";
  m_showGroup = m_enumManager->addProperty("Selected Groups");
  m_enumManager->setEnumNames(m_groupsToFit, m_groupsToFitOptions);
  multiFitSettingsGroup->addSubProperty(m_groupsToFit);
  multiFitSettingsGroup->addSubProperty(m_showGroup);
Anthony Lim's avatar
Anthony Lim committed
  m_enumManager->setEnumNames(m_showGroup, m_showGroupValue);
  QString tmp = "fwd";
  addGroupCheckbox(tmp);
  tmp = "bwd";
  addGroupCheckbox(tmp);
  m_periodsToFit = m_enumManager->addProperty("Periods to fit");
  m_periodsToFitOptions << ALL_PERIODS_LABEL << "1"
                        << "2" << CUSTOM_LABEL;
  m_showPeriodValue << "1";
  m_showPeriods = m_enumManager->addProperty("Selected Periods");
  m_enumManager->setEnumNames(m_periodsToFit, m_periodsToFitOptions);
  multiFitSettingsGroup->addSubProperty(m_periodsToFit);
  multiFitSettingsGroup->addSubProperty(m_showPeriods);
  m_enumManager->setEnumNames(m_showPeriods, m_showPeriodValue);
  multiFitSettingsGroup->addSubProperty(m_normalization);

Anthony Lim's avatar
Anthony Lim committed
  /* Create editors and assign them to the managers */
  createEditors(w);

  updateDecimals();
  m_functionsGroup = m_browser->addProperty(functionsGroup);
  m_settingsGroup = m_browser->addProperty(settingsGroup);
  m_multiFitSettingsGroup = m_browser->addProperty(multiFitSettingsGroup);
  connect(m_browser, SIGNAL(currentItemChanged(QtBrowserItem *)), this,
Anthony Lim's avatar
Anthony Lim committed
          SLOT(currentItemChanged(QtBrowserItem *)));
Anthony Lim's avatar
Anthony Lim committed
  m_btnGroup = new QGroupBox(tr("Reselect Data"));
  QHBoxLayout *btnLayout = new QHBoxLayout;
  m_reselectGroupBtn = new QPushButton("Groups/Pairs");
  m_reselectPeriodBtn = new QPushButton("Periods");
  m_generateBtn = new QPushButton("Combine Periods");
  m_groupWindow = new QDialog(this);
  m_periodWindow = new QDialog(this);
  m_comboWindow = new QDialog(this);
  m_reselectGroupBtn->setEnabled(false);
  m_reselectPeriodBtn->setEnabled(false);
Anthony Lim's avatar
Anthony Lim committed
  connect(m_reselectGroupBtn, SIGNAL(released()), this,
          SLOT(groupBtnPressed()));
  connect(m_reselectPeriodBtn, SIGNAL(released()), this,
          SLOT(periodBtnPressed()));
  connect(m_generateBtn, SIGNAL(released()), this, SLOT(generateBtnPressed()));

  btnLayout->addWidget(m_reselectGroupBtn);
  btnLayout->addWidget(m_reselectPeriodBtn);
  btnLayout->addWidget(m_generateBtn);

  m_btnGroup->setLayout(btnLayout);
  // Don't show "Function" or "Data" sections as they have separate widgets
  m_browser->setItemVisible(m_functionsGroup, false);
  m_browser->setItemVisible(m_settingsGroup, false);
Anthony Lim's avatar
Anthony Lim committed
  m_browser->setItemVisible(m_multiFitSettingsGroup, true);
  // Custom settings that are specific and asked for by the muon scientists.
  QtProperty *customSettingsGroup = m_groupManager->addProperty("Settings");
  m_rawData = m_boolManager->addProperty("Fit To Raw Data");
  bool data = settings.value("Fit To Raw Data", QVariant(false)).toBool();
  m_boolManager->setValue(m_rawData, data);

  m_showParamErrors = m_boolManager->addProperty("Show Parameter Errors");
  // XXX: showParamErrors is true by default for Muons
  bool showParamErrors =
      settings.value(m_showParamErrors->propertyName(), true).toBool();
  m_boolManager->setValue(m_showParamErrors, showParamErrors);
  m_parameterManager->setErrorsEnabled(showParamErrors);

  m_TFAsymmMode = m_boolManager->addProperty("TF Asymmetry Mode");
Anthony Lim's avatar
Anthony Lim committed
  bool TFAsymmMode =
      settings.value("TF Asymmetry Mode", QVariant(false)).toBool();
  m_boolManager->setValue(m_TFAsymmMode, TFAsymmMode);

  customSettingsGroup->addSubProperty(m_minimizer);
  customSettingsGroup->addSubProperty(m_TFAsymmMode);
  customSettingsGroup->addSubProperty(m_plotDiff);
  customSettingsGroup->addSubProperty(m_rawData);
  customSettingsGroup->addSubProperty(m_showParamErrors);
  customSettingsGroup->addSubProperty(m_evaluationType);
  m_customSettingsGroup = m_browser->addProperty(customSettingsGroup);

  // Initialise the layout.
  initBasicLayout(w);
Tom Perkins's avatar
Tom Perkins committed
  // Create an empty splitter that can hold extra widgets
  m_widgetSplitter = new QSplitter(Qt::Vertical, w);
Tom Perkins's avatar
Tom Perkins committed
  m_widgetSplitter->setSizePolicy(QSizePolicy::Policy::Expanding,
                                  QSizePolicy::Policy::Expanding);

  // This splitter separates the "extra widgets" region from the browser
  m_mainSplitter = new QSplitter(Qt::Vertical, w);
  m_mainSplitter->insertWidget(0, m_widgetSplitter);
  m_mainSplitter->insertWidget(1, m_browser);
  m_mainSplitter->setStretchFactor(0, 1);
  m_mainSplitter->setStretchFactor(1, 0);

  // Insert after the buttons
  auto parentLayout = qobject_cast<QVBoxLayout *>(w->layout());
  if (parentLayout) {
    const int index = parentLayout->count() - 1;
    constexpr int stretchFactor = 10; // so these widgets get any extra space
    parentLayout->insertWidget(index, m_mainSplitter, stretchFactor);
    parentLayout->setSpacing(0);
    parentLayout->setMargin(0);
    parentLayout->setContentsMargins(0, 0, 0, 0);
Anthony Lim's avatar
Anthony Lim committed
    parentLayout->insertWidget(index + 1, m_btnGroup);
  // Update tooltips when function structure is (or might've been) changed in
  // any way
  connect(this, SIGNAL(functionChanged()), SLOT(updateStructureTooltips()));
Anthony Lim's avatar
Anthony Lim committed
  // disable TFAsymm mode by default
  setTFAsymmMode(TFAsymmMode);
Anthony Lim's avatar
Anthony Lim committed
// Set up the execution of the muon fit menu
void MuonFitPropertyBrowser::executeFitMenu(const QString &item) {
Anthony Lim's avatar
Anthony Lim committed
  if (item == "TFAsymm") {
Anthony Lim's avatar
Anthony Lim committed
    doTFAsymmFit();
Anthony Lim's avatar
Anthony Lim committed
  } else {
    FitPropertyBrowser::executeFitMenu(item);
  }
// Create group/pair selection pop up
Anthony Lim's avatar
Anthony Lim committed
void MuonFitPropertyBrowser::groupBtnPressed() { genGroupWindow(); }
// Create period selection pop up
Anthony Lim's avatar
Anthony Lim committed
void MuonFitPropertyBrowser::periodBtnPressed() { genPeriodWindow(); }
// Create combination selection pop up
Anthony Lim's avatar
Anthony Lim committed
void MuonFitPropertyBrowser::generateBtnPressed() { genCombinePeriodWindow(); }
pulate the fit button.
* This initialization includes:
*   1. SIGNALs/SLOTs when properties change.
*   2. Actions and associated SIGNALs/SLOTs.
* @param fitMapper the QMap to the fit mapper
* @param fitMenu the QMenu for the fit button
Anthony Lim's avatar
Anthony Lim committed
void MuonFitPropertyBrowser::populateFitMenuButton(QSignalMapper *fitMapper,
                                                   QMenu *fitMenu) {

  m_fitActionTFAsymm = new QAction("TF Asymmetry Fit", this);
  fitMapper->setMapping(m_fitActionTFAsymm, "TFAsymm");
Anthony Lim's avatar
Anthony Lim committed

  FitPropertyBrowser::populateFitMenuButton(fitMapper, fitMenu);
  connect(m_fitActionTFAsymm, SIGNAL(triggered()), fitMapper, SLOT(map()));
  fitMenu->addSeparator();
  fitMenu->addAction(m_fitActionTFAsymm);
/// Enable/disable the Fit button;
void MuonFitPropertyBrowser::setFitEnabled(bool yes) {
Anthony Lim's avatar
Anthony Lim committed
  m_fitActionFit->setEnabled(yes);
  m_fitActionSeqFit->setEnabled(yes);
Anthony Lim's avatar
Anthony Lim committed
  // only allow TFAsymm fit if not keeping norm
  if (!m_boolManager->value(m_keepNorm) && yes) {
Anthony Lim's avatar
Anthony Lim committed
    m_fitActionTFAsymm->setEnabled(yes);
Anthony Lim's avatar
Anthony Lim committed
  } else {
    m_fitActionTFAsymm->setEnabled(false);
}
/**
* Set the input workspace name
*/
void MuonFitPropertyBrowser::setWorkspaceName(const QString &wsName) {
  int i = m_workspaceNames.indexOf(wsName);
  if (i < 0) {
    // workspace may not be found because add notification hasn't been processed
    // yet
    populateWorkspaceNames();
    i = m_workspaceNames.indexOf(wsName);
  }
  if (i >= 0)
    m_enumManager->setValue(m_workspace, i);
/** Called when a dropdown menu is changed
Anthony Lim's avatar
Anthony Lim committed
* @param prop :: A pointer to the function name property
*/
void MuonFitPropertyBrowser::enumChanged(QtProperty *prop) {
Anthony Lim's avatar
Anthony Lim committed
  if (m_workspaceNames.empty()) {
    if (this->isVisible()) {
      g_log.error("No Data available. Please load Some data.");
    }
Anthony Lim's avatar
Anthony Lim committed
    return;
Anthony Lim's avatar
Anthony Lim committed
  if (!m_changeSlotsEnabled)
    return;
  if (prop == m_groupsToFit) {
    int j = m_enumManager->value(m_groupsToFit);
    QString option = m_groupsToFitOptions[j];
    if (option == ALL_GROUPS_LABEL) {
Anthony Lim's avatar
Anthony Lim committed
      setAllGroups();
      m_reselectGroupBtn->setEnabled(false);
    } else if (option == ALL_PAIRS_LABEL) {
Anthony Lim's avatar
Anthony Lim committed
      setAllPairs();
      m_reselectGroupBtn->setEnabled(false);
    } else if (option == CUSTOM_LABEL) {
Anthony Lim's avatar
Anthony Lim committed
      m_reselectGroupBtn->setEnabled(true);
      genGroupWindow();
    }
    updateGroupDisplay();

  } else if (prop == m_periodsToFit) {
    int j = m_enumManager->value(m_periodsToFit);
    QString option = m_periodsToFitOptions[j];
    if (option == CUSTOM_LABEL) {
Anthony Lim's avatar
Anthony Lim committed
      m_reselectPeriodBtn->setEnabled(true);
      genPeriodWindow();
    } else if (option == ALL_PERIODS_LABEL) {
Anthony Lim's avatar
Anthony Lim committed
      setAllPeriods();
      m_reselectPeriodBtn->setEnabled(false);
Anthony Lim's avatar
Anthony Lim committed
    } else {
      for (auto iter = m_periodBoxes.constBegin();
           iter != m_periodBoxes.constEnd(); ++iter) {
        if (option == iter.key()) {
Anthony Lim's avatar
Anthony Lim committed
          m_boolManager->setValue(iter.value(), true);
        } else {
          m_boolManager->setValue(iter.value(), false);
        }
        m_reselectPeriodBtn->setEnabled(false);
      }
    }
    updatePeriodDisplay();
Anthony Lim's avatar
Anthony Lim committed
  } else if (prop == m_workspace) {
    // make sure the output is updated
    FitPropertyBrowser::enumChanged(prop);
    int j = m_enumManager->value(m_workspace);
    std::string option = m_workspaceNames[j].toStdString();
    setOutputName(option);
	// only do this if in single fit mode
    if (m_periodBoxes.size() > 1 && !m_browser->isItemVisible(m_multiFitSettingsGroup)) {
Anthony Lim's avatar
Anthony Lim committed
      size_t end = 0;
      // assumed structure of name
      // isolate the period
      for (int k = 0; k < 4; k++) {
        end = option.find_first_of(";");
        option = option.substr(end + 1, option.size());
      }
      end = option.find_first_of(";");
      QString selectedPeriod = QString::fromStdString(option.substr(0, end));
      // turn on only the relevant box
      for (auto iter = m_periodBoxes.constBegin();
           iter != m_periodBoxes.constEnd(); ++iter) {
        m_boolManager->setValue(iter.value(), selectedPeriod == iter.key());
      }
    }
Anthony Lim's avatar
Anthony Lim committed
  } else {
Anthony Lim's avatar
Anthony Lim committed
    FitPropertyBrowser::enumChanged(prop);
  }
Anthony Lim's avatar
Anthony Lim committed
/** Sets the display for
* selected groups
*/
void MuonFitPropertyBrowser::updateGroupDisplay() {
Anthony Lim's avatar
Anthony Lim committed
  m_showGroupValue.clear();
  auto tmp = getChosenGroups().join(",").toStdString();
Anthony Lim's avatar
Anthony Lim committed
  m_showGroupValue << getChosenGroups().join(",");
  m_enumManager->setEnumNames(m_showGroup, m_showGroupValue);
  m_multiFitSettingsGroup->property()->addSubProperty(m_showGroup);
}
/** Sets the display for
* selected periods
*/
void MuonFitPropertyBrowser::updatePeriodDisplay() {
Anthony Lim's avatar
Anthony Lim committed
  m_showPeriodValue.clear();
  auto tmp = getChosenPeriods();
  tmp.replaceInStrings(QRegExp(","), "+");
  m_showPeriodValue << tmp.join(",");
  m_enumManager->setEnumNames(m_showPeriods, m_showPeriodValue);
  if (m_periodsToFitOptions.size() > 1) {
    m_multiFitSettingsGroup->property()->addSubProperty(m_showPeriods);
  }
/** Called when a double property changed
 * @param prop :: A pointer to the property
void MuonFitPropertyBrowser::doubleChanged(QtProperty *prop) {
  if (!m_changeSlotsEnabled)
    return;

  double value = m_doubleManager->value(prop);
    // call setWorkspace to change maxX in functions
    setWorkspace(m_compositeFunction);
    getHandler()->setAttribute(QString("Start (%1s)").arg(QChar(0x03BC)),
                               value); // (mu)
    emit startXChanged(startX());
    emit xRangeChanged(startX(), endX());
    return;
  } else if (prop == m_endX) {
    // call setWorkspace to change minX in functions
    setWorkspace(m_compositeFunction);
    getHandler()->setAttribute(QString("End (%1s)").arg(QChar(0x03BC)), value);
    emit endXChanged(endX());
    emit xRangeChanged(startX(), endX());
    return;
Anthony Lim's avatar
Anthony Lim committed
  } else { // check if it is a constraint
    MantidQt::MantidWidgets::PropertyHandler *h =
        getHandler()->findHandler(prop);
    if (!h)
      return;

    QtProperty *parProp = h->getParameterProperty(prop);
    if (parProp) {
      if (prop->propertyName() == "LowerBound") {
        double loBound = m_doubleManager->value(prop);
        h->addConstraint(parProp, true, false, loBound, 0);
      } else if (prop->propertyName() == "UpperBound") {
        double upBound = m_doubleManager->value(prop);
        h->addConstraint(parProp, false, true, 0, upBound);
    } else { // it could be an attribute
/** @returns the normalization
*/
double MuonFitPropertyBrowser::normalization() const {
Anthony Lim's avatar
Anthony Lim committed
  return readNormalization()[0];
}
void MuonFitPropertyBrowser::setNormalization() {
  setNormalization(workspaceName());
}
/**
* @param name :: the ws name to get normalization for
* @returns the normalization
*/
void MuonFitPropertyBrowser::setNormalization(const std::string name) {
Anthony Lim's avatar
Anthony Lim committed
  m_normalizationValue.clear();
Anthony Lim's avatar
Anthony Lim committed
  QString label;
  auto norms = readMultipleNormalization();
  std::string tmp = name;
  // stored with ; instead of spaces
  std::replace(tmp.begin(), tmp.end(), ' ', ';');
  auto it = norms.find(tmp);
  if (it == norms.end()) {
    label = QString::fromStdString("N/A");
  } else {
    label = QString::number(it->second);
  }
  m_normalizationValue.append(label);
Anthony Lim's avatar
Anthony Lim committed
  m_enumManager->setEnumNames(m_normalization, m_normalizationValue);
/** Called when a bool property changed
 * @param prop :: A pointer to the property
 */
void MuonFitPropertyBrowser::boolChanged(QtProperty *prop) {
  if (prop == m_rawData) {
    const bool val = m_boolManager->value(prop);
    emit fitRawDataClicked(val);
  if (prop == m_TFAsymmMode) {
Anthony Lim's avatar
Anthony Lim committed
    const bool val = m_boolManager->value(prop);
    setTFAsymmMode(val);
Anthony Lim's avatar
Anthony Lim committed
  if (prop == m_keepNorm) {
    const bool val = m_boolManager->value(prop);
    if (val) { // record data for later
      double norm = readNormalization()[0];
      ITableWorkspace_sptr table = WorkspaceFactory::Instance().createTable();
      AnalysisDataService::Instance().addOrReplace("__keepNorm__", table);
      table->addColumn("double", "norm");
      table->addColumn("int", "spectra");
      TableRow row = table->appendRow();
      row << norm << 0;
      // remove TFAsymm fit
      m_fitActionTFAsymm->setEnabled(false);

    } else { // remove data so it is not used later
      AnalysisDataService::Instance().remove("__keepNorm__");

      // if fit is enabled so should TFAsymm
      if (m_fitActionSeqFit->isEnabled()) {
        m_fitActionTFAsymm->setEnabled(true);
      }
    }
Anthony Lim's avatar
Anthony Lim committed
    // search map for group/pair change
    bool done = false;
    for (auto iter = m_groupBoxes.constBegin(); iter != m_groupBoxes.constEnd();
         ++iter) {
      if (iter.value() == prop) {
        done = true;
        updateGroupDisplay();
        emit groupBoxClicked();
      }
    }
    // search map for period change
    if (done == false) {
      for (auto iter = m_periodBoxes.constBegin();
           iter != m_periodBoxes.constEnd(); ++iter) {
        if (iter.value() == prop) {
          done = true;
          updatePeriodDisplay();
          emit periodBoxClicked();
        }
      }
    }
    if (done == false) {
      // defer to parent class
      FitPropertyBrowser::boolChanged(prop);
    }
/**
*Get the registered function names
*/
void MuonFitPropertyBrowser::populateFunctionNames() {
  const std::vector<std::string> names = FunctionFactory::Instance().getKeys();
  m_registeredFunctions.clear();
  m_registeredPeaks.clear();
  m_registeredBackgrounds.clear();
  for (size_t i = 0; i < names.size(); i++) {
    std::string fnName = names[i];
    QString qfnName = QString::fromStdString(fnName);
    if (qfnName == "MultiBG")
      continue;

    auto f = FunctionFactory::Instance().createFunction(fnName);
    const std::vector<std::string> categories = f->categories();
    bool muon = false;
    for (size_t j = 0; j < categories.size(); ++j) {
      if ((categories[j] == "Muon") || (categories[j] == "General") ||
          (categories[j] == "Background"))
      m_registeredFunctions << qfnName;
    }
    IPeakFunction *pf = dynamic_cast<IPeakFunction *>(f.get());
    // CompositeFunction* cf = dynamic_cast<CompositeFunction*>(f.get());
    if (pf) {
      m_registeredPeaks << qfnName;
    } else if (dynamic_cast<IBackgroundFunction *>(f.get())) {
      m_registeredBackgrounds << qfnName;
      m_registeredOther << qfnName;
    }
  }
}
/**
* Creates an instance of Fit algorithm, sets its properties and launches it.
*/
void MuonFitPropertyBrowser::doTFAsymmFit() {
Anthony Lim's avatar
Anthony Lim committed
  std::string wsName = workspaceName();
Anthony Lim's avatar
Anthony Lim committed
  if (wsName.empty()) {
    QMessageBox::critical(this, "Mantid - Error", "Workspace name is not set");
    return;
  }
Anthony Lim's avatar
Anthony Lim committed
  std::vector<double> normVec;
  auto norms = readMultipleNormalization();
Anthony Lim's avatar
Anthony Lim committed
  // TFAsymm calculation -> there is already some estimated data
  // rescale WS to normalized counts:
  const int nWorkspaces = static_cast<int>(m_workspacesToFit.size());
  if (nWorkspaces > 1) {
Anthony Lim's avatar
Anthony Lim committed
    emit functionUpdateRequested();
Anthony Lim's avatar
Anthony Lim committed
  for (int i = 0; i < nWorkspaces; i++) {
    rescaleWS(norms, m_workspacesToFit[i], 1.0);
    std::string tmp = m_workspacesToFit[i];
    std::replace(tmp.begin(), tmp.end(), ' ', ';');
    // The order of the input is the same
    // as the order of the workspace list
    // create a vec of norms in the same order
    auto it = norms.find(tmp);
Anthony Lim's avatar
Anthony Lim committed
    if (it != norms.end()) {
      normVec.push_back(it->second);
    } else { // if raw data cannot be found
      // use the binned data as initial norm
      tmp = tmp.substr(0, tmp.size() - 4);
      it = norms.find(tmp);
      normVec.push_back(it->second);
    }
Anthony Lim's avatar
Anthony Lim committed
  }
  try {
Anthony Lim's avatar
Anthony Lim committed
    m_initialParameters.resize(compositeFunction()->nParams());
    for (size_t i = 0; i < compositeFunction()->nParams(); i++) {
      m_initialParameters[i] = compositeFunction()->getParameter(i);
    }
    m_fitActionUndoFit->setEnabled(true);

    IAlgorithm_sptr alg = AlgorithmManager::Instance().create("Fit");
Anthony Lim's avatar
Anthony Lim committed
    alg->initialize();
Anthony Lim's avatar
Anthony Lim committed
    if (m_compositeFunction->name() == "MultiBG") {
      alg->setPropertyValue("Function", "");
Anthony Lim's avatar
Anthony Lim committed
    } else if (m_compositeFunction->nFunctions() > 1) {
Anthony Lim's avatar
Anthony Lim committed
      IFunction_sptr userFunc = getFittingFunction();
      auto TFAsymmFunc = getTFAsymmFitFunction(userFunc, normVec);
      alg->setProperty("Function", TFAsymmFunc);
Anthony Lim's avatar
Anthony Lim committed
    } else {
      IFunction_sptr userFunc = m_compositeFunction->getFunction(0);
      auto TFAsymmFunc = getTFAsymmFitFunction(userFunc, normVec);
      alg->setProperty("Function", TFAsymmFunc);
Anthony Lim's avatar
Anthony Lim committed
    }
    if (rawData()) {
      alg->setPropertyValue("InputWorkspace", wsName + "_Raw");
    } else {
      alg->setPropertyValue("InputWorkspace", wsName);
Anthony Lim's avatar
Anthony Lim committed
    }
    alg->setProperty("WorkspaceIndex", workspaceIndex());
    alg->setProperty("StartX", startX());
    alg->setProperty("EndX", endX());
Anthony Lim's avatar
Anthony Lim committed
    alg->setPropertyValue("Minimizer", minimizer());
Anthony Lim's avatar
Anthony Lim committed
    alg->setPropertyValue("CostFunction", costFunction());
Anthony Lim's avatar
Anthony Lim committed

    // If we are doing a simultaneous fit, set this up here
    const int nWorkspaces = static_cast<int>(m_workspacesToFit.size());
    if (nWorkspaces > 1) {
      alg->setPropertyValue("InputWorkspace", m_workspacesToFit[0]);
      // Remove existing results with the same name
      if (AnalysisDataService::Instance().doesExist(outputName())) {
        AnalysisDataService::Instance().deepRemoveGroup(outputName());
Anthony Lim's avatar
Anthony Lim committed
      for (int i = 1; i < nWorkspaces; i++) {
        std::string suffix = boost::lexical_cast<std::string>(i);
        alg->setPropertyValue("InputWorkspace_" + suffix, m_workspacesToFit[i]);
        alg->setProperty("WorkspaceIndex_" + suffix, workspaceIndex());
        alg->setProperty("StartX_" + suffix, startX());
        alg->setProperty("EndX_" + suffix, endX());
      }
    } else {
      setSingleFitLabel(wsName);
Anthony Lim's avatar
Anthony Lim committed
    alg->setPropertyValue("Output", outputName());

Anthony Lim's avatar
Anthony Lim committed
    observeFinish(alg);
Anthony Lim's avatar
Anthony Lim committed
    alg->execute();
    // get norms
    std::vector<double> newNorms;
    IFunction_sptr outputFunction = alg->getProperty("Function");
    for (int j = 0; j < nWorkspaces; j++) {
Anthony Lim's avatar
Anthony Lim committed
      std::string paramName = "f" + std::to_string(j);
Anthony Lim's avatar
Anthony Lim committed
      paramName += ".f0.f0.A0";
Anthony Lim's avatar
Anthony Lim committed
      newNorms.push_back(outputFunction->getParameter(paramName));
      std::string tmpWSName = m_workspacesToFit[j];
      if (rawData()) { // store norms without the raw
        tmpWSName = tmpWSName.substr(0, tmpWSName.size() - 4);
      }
      auto tmpWSNameNoRaw = tmpWSName;
      std::replace(tmpWSName.begin(), tmpWSName.end(), ' ', ';');
      auto it = norms.find(tmpWSName);
      it->second = newNorms[newNorms.size() - 1];
      // transform data back to Asymm
      // rescale WS:
Anthony Lim's avatar
Anthony Lim committed
      rescaleWS(norms, tmpWSNameNoRaw, -1.0);
    }
Anthony Lim's avatar
Anthony Lim committed
    updateMultipleNormalization(norms);
Anthony Lim's avatar
Anthony Lim committed
  } catch (const std::exception &e) {
Anthony Lim's avatar
Anthony Lim committed
    QString msg = "TF Asymmetry Fit failed.\n\n" + QString(e.what()) + "\n";
Anthony Lim's avatar
Anthony Lim committed
    QMessageBox::critical(this, "Mantid - Error", msg);
  }
Anthony Lim's avatar
Anthony Lim committed
  runFit();
Anthony Lim's avatar
Anthony Lim committed
/**
* Updates the normalization in the table WS
* assumes that the change is due to a calculation
* @param norms :: map of updated normalization values
*/
Anthony Lim's avatar
Anthony Lim committed
void MuonFitPropertyBrowser::updateMultipleNormalization(
    std::map<std::string, double> norms) {
  auto oldNorm = readMultipleNormalization();
  ITableWorkspace_sptr table = WorkspaceFactory::Instance().createTable();
  AnalysisDataService::Instance().addOrReplace("MuonAnalysisTFNormalizations",
                                               table);
  table->addColumn("double", "norm");
  table->addColumn("str", "name");
  table->addColumn("str", "method");

  for (auto norm : oldNorm) {
    Mantid::API::TableRow row = table->appendRow();
    auto it = norms.find(std::get<0>(norm));
    if (it != norms.end() && it->second != std::get<1>(norm)) {
      // write new norm
Anthony Lim's avatar
Anthony Lim committed
      row << it->second << std::get<0>(norm) << "Calculated";
Anthony Lim's avatar
Anthony Lim committed
    } else {
      // write old norm
      row << std::get<1>(norm) << std::get<0>(norm) << "Estimated";
    }
  }
}
/** Gets the fitting function for TFAsymmetry fit
* @param original :: The function defined by the user (in GUI)
* @param norms :: vector of normalization constants
* @returns :: The fitting function for the TFAsymmetry fit
*/
Anthony Lim's avatar
Anthony Lim committed
Mantid::API::IFunction_sptr MuonFitPropertyBrowser::getTFAsymmFitFunction(
    Mantid::API::IFunction_sptr original, const std::vector<double> norms) {
Anthony Lim's avatar
Anthony Lim committed
  auto multi = boost::make_shared<MultiDomainFunction>();
  auto tmp = boost::dynamic_pointer_cast<MultiDomainFunction>(original);
  size_t numDomains = original->getNumberDomains();
  for (size_t j = 0; j < numDomains; j++) {
    IFunction_sptr userFunc;
    auto constant = FunctionFactory::Instance().createInitialized(
        "name = FlatBackground, A0 = 1.0, ties = (A0 = 1.0)");
    if (numDomains == 1) {
      userFunc = original;
    } else {
      userFunc = tmp->getFunction(j);
      multi->setDomainIndex(j, j);
    }
    auto inBrace = boost::make_shared<CompositeFunction>();
    inBrace->addFunction(constant);
    inBrace->addFunction(userFunc);
    auto norm = FunctionFactory::Instance().createInitialized(
        "composite=CompositeFunction,NumDeriv=true;name = FlatBackground, A0 "
        "=" +
        std::to_string(norms[j]));
    auto product = boost::dynamic_pointer_cast<CompositeFunction>(
        FunctionFactory::Instance().createFunction("ProductFunction"));
    product->addFunction(norm);
    product->addFunction(inBrace);
    multi->addFunction(product);
  }
Anthony Lim's avatar
Anthony Lim committed
  // add ties
  for (size_t j = 0; j < original->getParameterNames().size(); j++) {
Anthony Lim's avatar
Anthony Lim committed
    auto originalTie = original->getTie(j);
    if (originalTie) {
      auto name = original->getParameterNames()[j];
      auto stringTie = originalTie->asString();
      // change name to reflect new postion
      auto insertPosition = stringTie.find_first_of(".");
      stringTie.insert(insertPosition + 1, "f1.f1.");
      // need to change the other side of =
      insertPosition = stringTie.find_first_of("=");
      insertPosition = stringTie.find_first_of(".", insertPosition);
      stringTie.insert(insertPosition + 1, "f1.f1.");
      multi->addTies(stringTie);
    }
Anthony Lim's avatar
Anthony Lim committed
  return boost::dynamic_pointer_cast<IFunction>(multi);
}

std::vector<double> readNormalization() {
Anthony Lim's avatar
Anthony Lim committed
  std::vector<double> norm;
  if (!AnalysisDataService::Instance().doesExist("__norm__")) {
    norm.push_back(22.423);
  } else {
    Mantid::API::ITableWorkspace_sptr table =
        boost::dynamic_pointer_cast<Mantid::API::ITableWorkspace>(
            Mantid::API::AnalysisDataService::Instance().retrieve("__norm__"));
    auto colNorm = table->getColumn("norm");

    for (size_t j = 0; j < table->rowCount(); j++) {
Anthony Lim's avatar
Anthony Lim committed
      norm.push_back((*colNorm)[j]); // record and update norm....
    }
  }
  return norm;
Anthony Lim's avatar
Anthony Lim committed
/** Reads the normalization constants and which WS
* they belong to
* @returns :: A map of normalization constants and WS names
*/
std::map<std::string, double> readMultipleNormalization() {
Anthony Lim's avatar
Anthony Lim committed
  std::map<std::string, double> norm;
  if (AnalysisDataService::Instance().doesExist(
          "MuonAnalysisTFNormalizations")) {
    Mantid::API::ITableWorkspace_sptr table =
        boost::dynamic_pointer_cast<Mantid::API::ITableWorkspace>(
            Mantid::API::AnalysisDataService::Instance().retrieve(
                "MuonAnalysisTFNormalizations"));
    auto colNorm = table->getColumn("norm");
    auto colName = table->getColumn("name");
    for (size_t j = 0; j < table->rowCount(); j++) {
      norm[colName->cell<std::string>(j)] = ((*colNorm)[j]); // read norm
    }
  }
  return norm;
/** The transformation between normalized counts and asymmetry
* @param norm :: map of normalization constants
* @param wsName :: the name of the WS to rescale
* @param shift :: offset to add (+1 = to normalized counts, -1 = to asymmetry)
*/
Anthony Lim's avatar
Anthony Lim committed
void MuonFitPropertyBrowser::rescaleWS(const std::map<std::string, double> norm,
                                       const std::string wsName,
                                       const double shift) {
  // get norm:
  std::string tmp = wsName;
  // stored with ; instead of spaces
  std::replace(tmp.begin(), tmp.end(), ' ', ';');
  auto it = norm.find(tmp);
  if (it == norm.end()) {
    g_log.error("WS not found: " + wsName);
    return;
  }
  double value = it->second;
  rescaleWS(value, wsName, shift);
  if (rawData()) {
    rescaleWS(value, wsName + "_Raw", shift);
  }
/** The transformation between normalized counts and asymmetry
* @param value :: normalization constants
* @param wsName :: the name of the WS to rescale
* @param shift :: offset to add (+1 = to normalized counts, -1 = to asymmetry)
*/
Anthony Lim's avatar
Anthony Lim committed
void MuonFitPropertyBrowser::rescaleWS(const double value,
                                       const std::string wsName,
                                       const double shift) {
  // go back to normalized counts
  if (shift == 1.0) {
    IAlgorithm_sptr alg = AlgorithmManager::Instance().create("Scale");
    alg->initialize();
    alg->setProperty("InputWorkspace", wsName);
    alg->setProperty("OutputWorkspace", wsName);
    alg->setProperty("Factor", 1.0);
    alg->setProperty("Operation", "Add");
    alg->execute();
  }
  IAlgorithm_sptr alg = AlgorithmManager::Instance().create("Scale");
  alg->initialize();
  alg->setProperty("InputWorkspace", wsName);
  alg->setProperty("OutputWorkspace", wsName);
  if (shift == 1) {
    alg->setProperty("Factor", value);
  } else {
    alg->setProperty("Factor", 1. / value);
  }
  alg->setProperty("Operation", "Multiply");
  alg->execute();
  // if to asymmetry
  if (shift == -1.0) {
    IAlgorithm_sptr alg = AlgorithmManager::Instance().create("Scale");
    alg->initialize();
    alg->setProperty("InputWorkspace", wsName);
    alg->setProperty("OutputWorkspace", wsName);
    alg->setProperty("Factor", -1.0);
    alg->setProperty("Operation", "Add");
    alg->execute();
  }
 * Requests checks and updates prior to running a fit
void MuonFitPropertyBrowser::fit() { emit preFitChecksRequested(false); }
/**
 * Creates an instance of Fit algorithm, sets its properties and launches it.
 */
void MuonFitPropertyBrowser::runFit() {
  std::string wsName = workspaceName();

  if (wsName.empty()) {
    QMessageBox::critical(this, "Mantid - Error", "Workspace name is not set");
    m_initialParameters.resize(compositeFunction()->nParams());
    for (size_t i = 0; i < compositeFunction()->nParams(); i++) {
      m_initialParameters[i] = compositeFunction()->getParameter(i);
    }
    m_fitActionUndoFit->setEnabled(true);

    // Delete any existing results for this workspace, UNLESS we are doing a
    // simultaneous fit
    if (m_workspacesToFit.size() < 2) {
      if (AnalysisDataService::Instance().doesExist(
              wsName + "_NormalisedCovarianceMatrix")) {
        FrameworkManager::Instance().deleteWorkspace(
            wsName + "_NormalisedCovarianceMatrix");
      }
      if (AnalysisDataService::Instance().doesExist(wsName + "_Parameters")) {
        FrameworkManager::Instance().deleteWorkspace(wsName + "_Parameters");
      }
      if (AnalysisDataService::Instance().doesExist(wsName + "_Workspace")) {
        FrameworkManager::Instance().deleteWorkspace(wsName + "_Workspace");
      }
    IAlgorithm_sptr alg = AlgorithmManager::Instance().create("Fit");
    if (m_compositeFunction->name() == "MultiBG") {
      alg->setPropertyValue("Function", "");
    } else if (m_compositeFunction->nFunctions() > 1) {
      alg->setProperty("Function", boost::dynamic_pointer_cast<IFunction>(
                                       m_compositeFunction));
    } else {
      alg->setProperty("Function", boost::dynamic_pointer_cast<IFunction>(
                                       m_compositeFunction->getFunction(0)));
    }
      alg->setPropertyValue("InputWorkspace", wsName + "_Raw");
      alg->setPropertyValue("InputWorkspace", wsName);
    alg->setProperty("WorkspaceIndex", workspaceIndex());
    alg->setProperty("StartX", startX());
    alg->setProperty("EndX", endX());
    alg->setPropertyValue("Minimizer", minimizer());
    alg->setPropertyValue("CostFunction", costFunction());

    // If we are doing a simultaneous fit, set this up here
    const int nWorkspaces = static_cast<int>(m_workspacesToFit.size());
    if (nWorkspaces > 1) {
      alg->setPropertyValue("InputWorkspace", m_workspacesToFit[0]);
      for (int i = 1; i < nWorkspaces; i++) {
        std::string suffix = boost::lexical_cast<std::string>(i);
        alg->setPropertyValue("InputWorkspace_" + suffix, m_workspacesToFit[i]);
        alg->setProperty("WorkspaceIndex_" + suffix, workspaceIndex());
        alg->setProperty("StartX_" + suffix, startX());
        alg->setProperty("EndX_" + suffix, endX());
      }
Anthony Lim's avatar
Anthony Lim committed
    } else {
      setSingleFitLabel(wsName);
Anthony Lim's avatar
Anthony Lim committed
    alg->setPropertyValue("Output", outputName());
    observeFinish(alg);
    alg->executeAsync();
  } catch (const std::exception &e) {
    QString msg = "Fit algorithm failed.\n\n" + QString(e.what()) + "\n";
    QMessageBox::critical(this, "Mantid - Error", msg);
void MuonFitPropertyBrowser::runSequentialFit() {
  emit sequentialFitRequested();
}

/**