diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorModel.h b/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorModel.h index 4475a136a085c508f771598b53f57cc0c8566c08..1cff4e44c35160a75fb761498b4e7eda5518f8c9 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorModel.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorModel.h @@ -7,22 +7,36 @@ #pragma once #include "DllOption.h" +#include "MantidAPI/IFunction_fwd.h" #include "MantidAPI/MatrixWorkspace_fwd.h" +#include "MantidAPI/MultiDomainFunction.h" #include "MantidQtWidgets/Common/IndexTypes.h" -#include <map> #include <string> -#include <utility> +#include <vector> + +#include <QString> namespace MantidQt { namespace MantidWidgets { -// Workspace name, workspace index -// using WorkspaceDomain = std::pair<std::string, std::size_t>; -// StartX, EndX -// using XRange = std::pair<double, double>; -// Fit functions -// using FitFunctions = std::vector<IFunction_sptr>; +struct FitDomain { + + FitDomain(std::string const &prefix, std::string const &workspaceName, + WorkspaceIndex workspaceIndex, double startX, double endX) { + m_multiDomainFunctionPrefix = prefix; + m_workspaceName = workspaceName; + m_workspaceIndex = workspaceIndex; + m_startX = startX; + m_endX = endX; + } + + std::string m_multiDomainFunctionPrefix; + std::string m_workspaceName; + WorkspaceIndex m_workspaceIndex; + double m_startX; + double m_endX; +}; class EXPORT_OPT_MANTIDQT_COMMON FitScriptGeneratorModel { public: @@ -34,13 +48,40 @@ public: void addWorkspaceDomain(std::string const &workspaceName, WorkspaceIndex workspaceIndex, double startX, double endX); - void addWorkspaceDomains( - std::vector<Mantid::API::MatrixWorkspace_const_sptr> const &workspaces, - std::vector<WorkspaceIndex> const &workspaceIndices); - // private: - // std::map<WorkspaceDomain, XRange> m_fitRanges; - // std::map<WorkspaceDomain, FitFunctions> m_fitFunctions; +private: + void removeWorkspaceDomain( + std::size_t const &removeIndex, + std::vector<FitDomain>::const_iterator const &removeIter); + void addWorkspaceDomain(std::string const &prefix, + std::string const &workspaceName, + WorkspaceIndex workspaceIndex, double startX, + double endX); + + bool hasWorkspaceDomain(std::string const &workspaceName, + WorkspaceIndex workspaceIndex); + std::vector<FitDomain>::const_iterator + findWorkspaceDomain(std::string const &workspaceName, + WorkspaceIndex workspaceIndex) const; + + void removeCompositeAtPrefix(std::string const &prefix); + + void addEmptyCompositeAtPrefix(std::string const &prefix); + void addEmptyCompositeAtPrefix(std::string const &compositePrefix, + std::size_t const &compositeIndex); + + void removeCompositeAtIndex(std::size_t const &compositeIndex); + + bool hasCompositeAtPrefix(std::string const &prefix) const; + + std::string nextAvailablePrefix() const; + + [[nodiscard]] inline std::size_t numberOfDomains() const noexcept { + return m_fitDomains.size(); + } + + std::vector<FitDomain> m_fitDomains; + Mantid::API::MultiDomainFunction_sptr m_function; }; } // namespace MantidWidgets diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorPresenter.h b/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorPresenter.h index 1d45fb0238f24fe5b7eca4d9015c619718050a15..fd80f7fa4132b418e8f32435f374f7bf10fa5bc6 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorPresenter.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorPresenter.h @@ -10,6 +10,7 @@ #include "FitScriptGeneratorView.h" #include "MantidAPI/MatrixWorkspace_fwd.h" +#include "MantidQtWidgets/Common/IndexTypes.h" #include <string> #include <vector> @@ -41,10 +42,21 @@ private: void setWorkspaces(QStringList const &workspaceNames, double startX, double endX); + void addWorkspaces( + std::vector<Mantid::API::MatrixWorkspace_const_sptr> const &workspaces, + std::vector<WorkspaceIndex> const &workspaceIndices); void addWorkspace(std::string const &workspaceName, double startX, double endX); - void addWorkspace(MatrixWorkspace_const_sptr const &workspace, double startX, - double endX); + void addWorkspace(Mantid::API::MatrixWorkspace_const_sptr const &workspace, + double startX, double endX); + void addWorkspace(Mantid::API::MatrixWorkspace_const_sptr const &workspace, + WorkspaceIndex workspaceIndex, double startX, double endX); + void addWorkspace(std::string const &workspaceName, + WorkspaceIndex workspaceIndex, double startX, double endX); + + void displayWarningMessages(); + + std::vector<std::string> m_warnings; FitScriptGeneratorModel *m_model; FitScriptGeneratorView *m_view; diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorView.h b/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorView.h index 120b82b2ed92af83ea172ac68378d30f6ba63038..63ec7321a62bc41152a76bdb5671a58ab1a432ba 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorView.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/FitScriptGeneratorView.h @@ -62,14 +62,13 @@ public: void addWorkspaceDomain(std::string const &workspaceName, WorkspaceIndex workspaceIndex, double startX, double endX); - void addWorkspaceDomains( - std::vector<Mantid::API::MatrixWorkspace_const_sptr> const &workspaces, - std::vector<WorkspaceIndex> const &workspaceIndices); bool openAddWorkspaceDialog(); std::vector<Mantid::API::MatrixWorkspace_const_sptr> getDialogWorkspaces(); std::vector<WorkspaceIndex> getDialogWorkspaceIndices() const; + void displayWarning(std::string const &message); + private slots: void onRemoveClicked(); void onAddWorkspaceClicked(); @@ -81,8 +80,6 @@ private: void setFitBrowserOption(QString const &name, QString const &value); void setFittingType(QString const &fitType); - void displayWarning(QString const &message); - FitScriptGeneratorPresenter *m_presenter; AddWorkspaceDialog m_dialog; std::unique_ptr<FitScriptGeneratorDataTable> m_dataTable; diff --git a/qt/widgets/common/src/FitScriptGeneratorModel.cpp b/qt/widgets/common/src/FitScriptGeneratorModel.cpp index ad8ea17ce7d32aa84f88b2c8b0c052c01bbe0546..162ae3af6d239ea5f3117b76ac6de04f90a04b14 100644 --- a/qt/widgets/common/src/FitScriptGeneratorModel.cpp +++ b/qt/widgets/common/src/FitScriptGeneratorModel.cpp @@ -6,27 +6,207 @@ // SPDX - License - Identifier: GPL - 3.0 + #include "MantidQtWidgets/Common/FitScriptGeneratorModel.h" +#include "MantidAPI/CompositeFunction.h" +#include "MantidAPI/FunctionFactory.h" +#include "MantidAPI/IFunction.h" #include "MantidAPI/MatrixWorkspace.h" +#include <boost/algorithm/string.hpp> + +#include <algorithm> +#include <stdexcept> + using namespace Mantid::API; +namespace { + +IFunction_sptr createIFunction(std::string const &functionString) { + return FunctionFactory::Instance().createInitialized(functionString); +} + +CompositeFunction_sptr toComposite(IFunction_sptr function) { + return std::dynamic_pointer_cast<CompositeFunction>(function); +} + +CompositeFunction_sptr createEmptyComposite() { + return toComposite(createIFunction("name=CompositeFunction")); +} + +MultiDomainFunction_sptr +createMultiDomainFunction(std::size_t const &numberOfDomains) { + return FunctionFactory::Instance().createInitializedMultiDomainFunction( + "name=CompositeFunction", numberOfDomains); +} + +std::vector<std::string> splitStringBy(std::string const &str, + std::string const &delimiter) { + std::vector<std::string> subStrings; + boost::split(subStrings, str, boost::is_any_of(delimiter)); + subStrings.erase(std::remove_if(subStrings.begin(), subStrings.end(), + [](std::string const &subString) { + return subString.empty(); + }), + subStrings.cend()); + return subStrings; +} + +bool isSinglePrefix(std::string const &prefix) { + auto const subStrings = splitStringBy(prefix, "f."); + return subStrings.size() == 1; +} + +std::size_t getPrefixIndexAt(std::string const &prefix, + std::size_t const &index) { + auto const subStrings = splitStringBy(prefix, "f."); + return std::stoull(subStrings[index]); +} + +std::string getTopPrefix(std::string const &prefix) { + auto topPrefix = prefix; + auto const firstDotIndex = topPrefix.find("."); + if (firstDotIndex != std::string::npos) + topPrefix.erase(firstDotIndex, topPrefix.size() - firstDotIndex); + return topPrefix; +} + +IFunction_sptr getFunctionAtPrefix(std::string const &prefix, + IFunction_sptr const &function, + bool isLastFunction = false) { + if (isLastFunction) + return function; + + auto const isLast = isSinglePrefix(prefix); + auto const topPrefix = getTopPrefix(prefix); + auto const firstIndex = getPrefixIndexAt(prefix, 0); + + try { + return getFunctionAtPrefix(topPrefix, function->getFunction(firstIndex), + isLast); + } catch (std::exception const &) { + return IFunction_sptr(); + } +} + +} // namespace + namespace MantidQt { namespace MantidWidgets { -FitScriptGeneratorModel::FitScriptGeneratorModel() {} +FitScriptGeneratorModel::FitScriptGeneratorModel() + : m_fitDomains(), m_function(nullptr) {} FitScriptGeneratorModel::~FitScriptGeneratorModel() {} void FitScriptGeneratorModel::removeWorkspaceDomain( - std::string const &workspaceName, WorkspaceIndex workspaceIndex) {} + std::string const &workspaceName, WorkspaceIndex workspaceIndex) { + auto const removeIter = findWorkspaceDomain(workspaceName, workspaceIndex); + auto const removePrefix = removeIter->m_multiDomainFunctionPrefix; + auto const removeIndex = getPrefixIndexAt(removePrefix, 0); + + removeWorkspaceDomain(removeIndex, removeIter); + removeCompositeAtPrefix(removePrefix); +} + +void FitScriptGeneratorModel::removeWorkspaceDomain( + std::size_t const &removeIndex, + std::vector<FitDomain>::const_iterator const &removeIter) { + if (removeIter != m_fitDomains.cend()) + m_fitDomains.erase(removeIter); + + auto const decrementPrefixes = [&](FitDomain &fitDomain) { + auto thisIndex = getPrefixIndexAt(fitDomain.m_multiDomainFunctionPrefix, 0); + if (removeIndex < thisIndex) { + --thisIndex; + fitDomain.m_multiDomainFunctionPrefix = "f" + std::to_string(thisIndex); + } + return fitDomain; + }; + + std::transform(m_fitDomains.begin(), m_fitDomains.end(), m_fitDomains.begin(), + decrementPrefixes); +} void FitScriptGeneratorModel::addWorkspaceDomain( std::string const &workspaceName, WorkspaceIndex workspaceIndex, - double startX, double endX) {} + double startX, double endX) { + if (hasWorkspaceDomain(workspaceName, workspaceIndex)) + throw std::invalid_argument("The '" + workspaceName + " (" + + std::to_string(workspaceIndex.value) + + ")' domain already exists."); + + addWorkspaceDomain(nextAvailablePrefix(), workspaceName, workspaceIndex, + startX, endX); +} + +void FitScriptGeneratorModel::addWorkspaceDomain( + std::string const &prefix, std::string const &workspaceName, + WorkspaceIndex workspaceIndex, double startX, double endX) { + addEmptyCompositeAtPrefix(prefix); + m_fitDomains.emplace_back( + FitDomain(prefix, workspaceName, workspaceIndex, startX, endX)); +} + +bool FitScriptGeneratorModel::hasWorkspaceDomain( + std::string const &workspaceName, WorkspaceIndex workspaceIndex) { + return findWorkspaceDomain(workspaceName, workspaceIndex) != + m_fitDomains.end(); +} + +std::vector<FitDomain>::const_iterator +FitScriptGeneratorModel::findWorkspaceDomain( + std::string const &workspaceName, WorkspaceIndex workspaceIndex) const { + auto const isMatch = [&](FitDomain const &fitDomain) { + return fitDomain.m_workspaceName == workspaceName && + fitDomain.m_workspaceIndex == workspaceIndex; + }; + + return std::find_if(m_fitDomains.cbegin(), m_fitDomains.cend(), isMatch); +} + +void FitScriptGeneratorModel::removeCompositeAtPrefix( + std::string const &prefix) { + removeCompositeAtIndex(getPrefixIndexAt(prefix, 0)); +} + +void FitScriptGeneratorModel::addEmptyCompositeAtPrefix( + std::string const &prefix) { + if (!m_function) { + m_function = createMultiDomainFunction(1); + } else { + addEmptyCompositeAtPrefix(getTopPrefix(prefix), + getPrefixIndexAt(prefix, 0)); + } +} + +void FitScriptGeneratorModel::addEmptyCompositeAtPrefix( + std::string const &compositePrefix, std::size_t const &compositeIndex) { + if (compositeIndex != numberOfDomains()) + throw std::runtime_error("The composite index provided is invalid."); + + if (hasCompositeAtPrefix(compositePrefix)) + throw std::runtime_error("The composite prefix provided already exists."); + + m_function->addFunction(createEmptyComposite()); + m_function->setDomainIndex(compositeIndex, compositeIndex); +} + +void FitScriptGeneratorModel::removeCompositeAtIndex( + std::size_t const &compositeIndex) { + if (compositeIndex > numberOfDomains()) + throw std::runtime_error("The composite prefix provided does not exist"); + + m_function->removeFunction(compositeIndex); +} + +bool FitScriptGeneratorModel::hasCompositeAtPrefix( + std::string const &compositePrefix) const { + return static_cast<bool>( + toComposite(getFunctionAtPrefix(compositePrefix, m_function))); +} -void FitScriptGeneratorModel::addWorkspaceDomains( - std::vector<MatrixWorkspace_const_sptr> const &workspaces, - std::vector<WorkspaceIndex> const &workspaceIndices) {} +std::string FitScriptGeneratorModel::nextAvailablePrefix() const { + return "f" + std::to_string(numberOfDomains()); +} } // namespace MantidWidgets } // namespace MantidQt diff --git a/qt/widgets/common/src/FitScriptGeneratorPresenter.cpp b/qt/widgets/common/src/FitScriptGeneratorPresenter.cpp index 0a53c02c1e3722ec97cb2e44e7193aaf3b4f2b24..8496a2f79dd3847fa17778638c3fb8a56da06661 100644 --- a/qt/widgets/common/src/FitScriptGeneratorPresenter.cpp +++ b/qt/widgets/common/src/FitScriptGeneratorPresenter.cpp @@ -10,6 +10,9 @@ #include "MantidAPI/AnalysisDataService.h" #include "MantidAPI/MatrixWorkspace.h" +#include <algorithm> +#include <iterator> +#include <sstream> #include <stdexcept> namespace MantidQt { @@ -18,7 +21,7 @@ namespace MantidWidgets { FitScriptGeneratorPresenter::FitScriptGeneratorPresenter( FitScriptGeneratorView *view, FitScriptGeneratorModel *model, QStringList const &workspaceNames, double startX, double endX) - : m_view(view), m_model(model) { + : m_warnings(), m_view(view), m_model(model) { m_view->subscribePresenter(this); setWorkspaces(workspaceNames, startX, endX); } @@ -59,10 +62,8 @@ void FitScriptGeneratorPresenter::handleAddWorkspaceClicked() { auto const workspaces = m_view->getDialogWorkspaces(); auto const workspaceIndices = m_view->getDialogWorkspaceIndices(); - if (!workspaces.empty() && !workspaceIndices.empty()) { - m_view->addWorkspaceDomains(workspaces, workspaceIndices); - m_model->addWorkspaceDomains(workspaces, workspaceIndices); - } + if (!workspaces.empty() && !workspaceIndices.empty()) + addWorkspaces(workspaces, workspaceIndices); } } @@ -70,6 +71,17 @@ void FitScriptGeneratorPresenter::setWorkspaces( QStringList const &workspaceNames, double startX, double endX) { for (auto const &workspaceName : workspaceNames) addWorkspace(workspaceName.toStdString(), startX, endX); + displayWarningMessages(); +} +void FitScriptGeneratorPresenter::addWorkspaces( + std::vector<MatrixWorkspace_const_sptr> const &workspaces, + std::vector<WorkspaceIndex> const &workspaceIndices) { + for (auto const &workspace : workspaces) + for (auto const &workspaceIndex : workspaceIndices) { + auto const xData = workspace->x(workspaceIndex.value); + addWorkspace(workspace, workspaceIndex, xData.front(), xData.back()); + } + displayWarningMessages(); } void FitScriptGeneratorPresenter::addWorkspace(std::string const &workspaceName, @@ -81,9 +93,34 @@ void FitScriptGeneratorPresenter::addWorkspace(std::string const &workspaceName, void FitScriptGeneratorPresenter::addWorkspace( MatrixWorkspace_const_sptr const &workspace, double startX, double endX) { - for (auto index = 0u; index < workspace->getNumberHistograms(); ++index) { - m_view->addWorkspaceDomain(workspace->getName(), index, startX, endX); - m_model->addWorkspaceDomain(workspace->getName(), index, startX, endX); + for (auto index = 0u; index < workspace->getNumberHistograms(); ++index) + addWorkspace(workspace, WorkspaceIndex{index}, startX, endX); +} + +void FitScriptGeneratorPresenter::addWorkspace( + MatrixWorkspace_const_sptr const &workspace, WorkspaceIndex workspaceIndex, + double startX, double endX) { + addWorkspace(workspace->getName(), workspaceIndex, startX, endX); +} + +void FitScriptGeneratorPresenter::addWorkspace(std::string const &workspaceName, + WorkspaceIndex workspaceIndex, + double startX, double endX) { + try { + m_model->addWorkspaceDomain(workspaceName, workspaceIndex, startX, endX); + m_view->addWorkspaceDomain(workspaceName, workspaceIndex, startX, endX); + } catch (std::invalid_argument const &ex) { + m_warnings.emplace_back(ex.what()); + } +} + +void FitScriptGeneratorPresenter::displayWarningMessages() { + if (!m_warnings.empty()) { + std::stringstream ss; + std::copy(m_warnings.cbegin(), m_warnings.cend(), + std::ostream_iterator<std::string>(ss, "\n")); + m_view->displayWarning(ss.str()); + m_warnings.clear(); } } diff --git a/qt/widgets/common/src/FitScriptGeneratorView.cpp b/qt/widgets/common/src/FitScriptGeneratorView.cpp index a0ad18ad11ee557b5318d8ae7fbc2f5a99bacb34..24b5fdfee335fc24c0f1646f9db3ca6a95b018f3 100644 --- a/qt/widgets/common/src/FitScriptGeneratorView.cpp +++ b/qt/widgets/common/src/FitScriptGeneratorView.cpp @@ -161,18 +161,6 @@ void FitScriptGeneratorView::addWorkspaceDomain( startX, endX); } -void FitScriptGeneratorView::addWorkspaceDomains( - std::vector<MatrixWorkspace_const_sptr> const &workspaces, - std::vector<WorkspaceIndex> const &workspaceIndices) { - for (auto const &workspace : workspaces) { - for (auto const &workspaceIndex : workspaceIndices) { - auto const xData = workspace->x(workspaceIndex.value); - addWorkspaceDomain(workspace->getName(), workspaceIndex, xData.front(), - xData.back()); - } - } -} - bool FitScriptGeneratorView::openAddWorkspaceDialog() { return m_dialog.exec() == QDialog::Accepted; } @@ -185,9 +173,8 @@ FitScriptGeneratorView::getDialogWorkspaces() { if (AnalysisDataService::Instance().doesExist(workspaceName)) workspaces = getWorkspaces(workspaceName); else - displayWarning("Failed to add workspace '" + - QString::fromStdString(workspaceName) + - "' : workspace doesn't exist."); + displayWarning("Failed to add workspace '" + workspaceName + + "': workspace doesn't exist."); return workspaces; } @@ -196,8 +183,8 @@ FitScriptGeneratorView::getDialogWorkspaceIndices() const { return convertToWorkspaceIndex(m_dialog.workspaceIndices()); } -void FitScriptGeneratorView::displayWarning(QString const &message) { - QMessageBox::warning(this, "Warning!", message); +void FitScriptGeneratorView::displayWarning(std::string const &message) { + QMessageBox::warning(this, "Warning!", QString::fromStdString(message)); } } // namespace MantidWidgets