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
#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
#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 <QSettings>
#include <QMessageBox>
#include <QAction>
#include <QLabel>
#include <QPushButton>
#include <QMenu>
#include <QSignalMapper>
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)));
m_normalization = m_enumManager->addProperty("Normalization");
setNormalization();
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);
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");
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);
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);
/* 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,
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);
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);
// Don't show "Function" or "Data" sections as they have separate widgets
m_browser->setItemVisible(m_functionsGroup, false);
m_browser->setItemVisible(m_settingsGroup, false);
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");
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.
// Create an empty splitter that can hold extra widgets
m_widgetSplitter = new QSplitter(Qt::Vertical, w);
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);
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()));
setTFAsymmMode(TFAsymmMode);
void MuonFitPropertyBrowser::executeFitMenu(const QString &item) {
} else {
FitPropertyBrowser::executeFitMenu(item);
}
// Create group/pair selection pop up
void MuonFitPropertyBrowser::groupBtnPressed() { genGroupWindow(); }
void MuonFitPropertyBrowser::periodBtnPressed() { genPeriodWindow(); }
void MuonFitPropertyBrowser::generateBtnPressed() { genCombinePeriodWindow(); }
* 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
void MuonFitPropertyBrowser::populateFitMenuButton(QSignalMapper *fitMapper,
QMenu *fitMenu) {
m_fitActionTFAsymm = new QAction("TF Asymmetry Fit", this);
fitMapper->setMapping(m_fitActionTFAsymm, "TFAsymm");
FitPropertyBrowser::populateFitMenuButton(fitMapper, fitMenu);
connect(m_fitActionTFAsymm, SIGNAL(triggered()), fitMapper, SLOT(map()));
fitMenu->addAction(m_fitActionTFAsymm);
/// Enable/disable the Fit button;
void MuonFitPropertyBrowser::setFitEnabled(bool yes) {
m_fitActionFit->setEnabled(yes);
m_fitActionSeqFit->setEnabled(yes);
if (!m_boolManager->value(m_keepNorm) && yes) {
} 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);
* @param prop :: A pointer to the function name property
*/
void MuonFitPropertyBrowser::enumChanged(QtProperty *prop) {
if (m_workspaceNames.empty()) {
if (this->isVisible()) {
g_log.error("No Data available. Please load Some data.");
}
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) {
setAllGroups();
m_reselectGroupBtn->setEnabled(false);
} else if (option == ALL_PAIRS_LABEL) {
setAllPairs();
m_reselectGroupBtn->setEnabled(false);
} else if (option == CUSTOM_LABEL) {
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) {
m_reselectPeriodBtn->setEnabled(true);
genPeriodWindow();
} else if (option == ALL_PERIODS_LABEL) {
setAllPeriods();
m_reselectPeriodBtn->setEnabled(false);
} else {
for (auto iter = m_periodBoxes.constBegin();
iter != m_periodBoxes.constEnd(); ++iter) {
m_boolManager->setValue(iter.value(), true);
} else {
m_boolManager->setValue(iter.value(), false);
}
m_reselectPeriodBtn->setEnabled(false);
}
}
updatePeriodDisplay();
} 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)) {
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());
}
}
* selected groups
*/
void MuonFitPropertyBrowser::updateGroupDisplay() {
auto tmp = getChosenGroups().join(",").toStdString();
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() {
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);
if (prop == m_startX) {
// 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;
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
h->setAttribute(prop);
}
}
}
/** @returns the normalization
*/
double MuonFitPropertyBrowser::normalization() const {
}
void MuonFitPropertyBrowser::setNormalization() {
setNormalization(workspaceName());
}
/**
* @param name :: the ws name to get normalization for
* @returns the normalization
*/
void MuonFitPropertyBrowser::setNormalization(const std::string name) {
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);
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) {
const bool val = m_boolManager->value(prop);
setTFAsymmMode(val);
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);
}
}
// 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() {
if (wsName.empty()) {
QMessageBox::critical(this, "Mantid - Error", "Workspace name is not set");
return;
}
std::vector<double> normVec;
auto norms = readMultipleNormalization();
// TFAsymm calculation -> there is already some estimated data
// rescale WS to normalized counts:
const int nWorkspaces = static_cast<int>(m_workspacesToFit.size());
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);
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);
}
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");
if (m_compositeFunction->name() == "MultiBG") {
alg->setPropertyValue("Function", "");
} else if (m_compositeFunction->nFunctions() > 1) {
IFunction_sptr userFunc = getFittingFunction();
auto TFAsymmFunc = getTFAsymmFitFunction(userFunc, normVec);
alg->setProperty("Function", TFAsymmFunc);
} else {
IFunction_sptr userFunc = m_compositeFunction->getFunction(0);
auto TFAsymmFunc = getTFAsymmFitFunction(userFunc, normVec);
alg->setProperty("Function", TFAsymmFunc);
}
if (rawData()) {
alg->setPropertyValue("InputWorkspace", wsName + "_Raw");
} else {
alg->setPropertyValue("InputWorkspace", wsName);
}
alg->setProperty("WorkspaceIndex", workspaceIndex());
alg->setProperty("StartX", startX());
alg->setProperty("EndX", endX());
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]);
// Remove existing results with the same name
if (AnalysisDataService::Instance().doesExist(outputName())) {
AnalysisDataService::Instance().deepRemoveGroup(outputName());
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);
alg->setPropertyValue("Output", outputName());
alg->execute();
// get norms
std::vector<double> newNorms;
IFunction_sptr outputFunction = alg->getProperty("Function");
for (int j = 0; j < nWorkspaces; j++) {
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:
QString msg = "TF Asymmetry Fit failed.\n\n" + QString(e.what()) + "\n";
QMessageBox::critical(this, "Mantid - Error", msg);
}
/**
* Updates the normalization in the table WS
* assumes that the change is due to a calculation
* @param norms :: map of updated normalization values
*/
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
row << it->second << std::get<0>(norm) << "Calculated";
} 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
*/
Mantid::API::IFunction_sptr MuonFitPropertyBrowser::getTFAsymmFitFunction(
Mantid::API::IFunction_sptr original, const std::vector<double> norms) {
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
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);
}
for (size_t j = 0; j < original->getParameterNames().size(); j++) {
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);
}
return boost::dynamic_pointer_cast<IFunction>(multi);
}
std::vector<double> readNormalization() {
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++) {
norm.push_back((*colNorm)[j]); // record and update norm....
}
}
return norm;
/** 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() {
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)
*/
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)
*/
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
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");
alg->initialize();
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());
}
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);
/**
* Show sequential fit dialog.
*/
void MuonFitPropertyBrowser::runSequentialFit() {
emit sequentialFitRequested();
}
/**