diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/BayesFitting.cpp b/qt/scientific_interfaces/Inelastic/BayesFitting/BayesFitting.cpp index 07f14bfe4e52..ae6b213876c8 100644 --- a/qt/scientific_interfaces/Inelastic/BayesFitting/BayesFitting.cpp +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/BayesFitting.cpp @@ -8,12 +8,10 @@ #include "MantidQtWidgets/Spectroscopy/SettingsWidget/Settings.h" #include "Quasi.h" #include "ResNormPresenter.h" -#include "Stretch.h" +#include "StretchPresenter.h" #include -using namespace MantidQt::CustomInterfaces; - namespace MantidQt::CustomInterfaces { DECLARE_SUBWINDOW(BayesFitting) @@ -25,17 +23,26 @@ BayesFitting::BayesFitting(QWidget *parent) // Connect Poco Notification Observer Mantid::Kernel::ConfigService::Instance().addObserver(m_changeObserver); - auto jobRunner = std::make_unique(true); - auto algorithmRunner = std::make_unique(std::move(jobRunner)); + auto resNormRunner = createAlgorithmRunner(); // insert each tab into the interface on creation auto resNormModel = std::make_unique(); auto resNormWidget = m_uiForm.bayesFittingTabs->widget(RES_NORM); - m_bayesTabs.emplace(RES_NORM, new ResNormPresenter(resNormWidget, std::move(algorithmRunner), std::move(resNormModel), + m_bayesTabs.emplace(RES_NORM, new ResNormPresenter(resNormWidget, std::move(resNormRunner), std::move(resNormModel), new ResNormView(resNormWidget))); m_bayesTabs.emplace(QUASI, new Quasi(m_uiForm.bayesFittingTabs->widget(QUASI))); - m_bayesTabs.emplace(STRETCH, new Stretch(m_uiForm.bayesFittingTabs->widget(STRETCH))); + + auto stretchRunner = createAlgorithmRunner(); + + auto tabContent = m_uiForm.bayesFittingTabs->widget(STRETCH); + m_bayesTabs.emplace(STRETCH, new StretchPresenter(tabContent, new StretchView(tabContent), + std::make_unique(), std::move(stretchRunner))); +} + +std::unique_ptr BayesFitting::createAlgorithmRunner() const { + auto jobRunner = std::make_unique(true); + return std::make_unique(std::move(jobRunner)); } void BayesFitting::initLayout() { diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/BayesFitting.h b/qt/scientific_interfaces/Inelastic/BayesFitting/BayesFitting.h index 64ec3f104722..e4b956f0f009 100644 --- a/qt/scientific_interfaces/Inelastic/BayesFitting/BayesFitting.h +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/BayesFitting.h @@ -45,6 +45,8 @@ class MANTIDQT_INELASTIC_DLL BayesFitting : public InelasticInterface { void initLayout() override; private: + std::unique_ptr createAlgorithmRunner() const; + std::string documentationPage() const override; void applySettings(std::map const &settings) override; diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/CMakeLists.txt b/qt/scientific_interfaces/Inelastic/BayesFitting/CMakeLists.txt index 0b5eb2f4951c..bc2dda0b725d 100644 --- a/qt/scientific_interfaces/Inelastic/BayesFitting/CMakeLists.txt +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/CMakeLists.txt @@ -7,12 +7,14 @@ set(SRC_FILES ResNormModel.cpp ResNormView.cpp ResNormPresenter.cpp - Stretch.cpp + StretchView.cpp + StretchPresenter.cpp + StretchModel.cpp ) -set(INC_FILES ResNormPresenter.h ResNormModel.h) +set(INC_FILES ResNormPresenter.h ResNormModel.h StretchPresenter.h StretchModel.h) -set(MOC_FILES BayesFitting.h BayesFittingTab.h Quasi.h ResNormView.h Stretch.h) +set(MOC_FILES BayesFitting.h BayesFittingTab.h Quasi.h ResNormView.h StretchView.h) set(UI_FILES BayesFitting.ui Quasi.ui ResNorm.ui Stretch.ui) @@ -29,7 +31,6 @@ set(ALL_INC_FILES ${ALL_INC_FILES} ${INC_FILES} PARENT_SCOPE ) - set(ALL_MOC_FILES ${ALL_MOC_FILES} ${MOC_FILES} PARENT_SCOPE diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/Stretch.cpp b/qt/scientific_interfaces/Inelastic/BayesFitting/Stretch.cpp deleted file mode 100644 index 6902b33f67b8..000000000000 --- a/qt/scientific_interfaces/Inelastic/BayesFitting/Stretch.cpp +++ /dev/null @@ -1,482 +0,0 @@ -// Mantid Repository : https://github.com/mantidproject/mantid -// -// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, -// NScD Oak Ridge National Laboratory, European Spallation Source, -// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS -// SPDX - License - Identifier: GPL - 3.0 + -#include "Stretch.h" -#include "MantidAPI/AlgorithmManager.h" -#include "MantidQtWidgets/Common/UserInputValidator.h" -#include "MantidQtWidgets/Common/WorkspaceUtils.h" -#include "MantidQtWidgets/Spectroscopy/InterfaceUtils.h" -#include "MantidQtWidgets/Spectroscopy/RunWidget/RunView.h" -#include "MantidQtWidgets/Spectroscopy/SettingsWidget/SettingsHelper.h" - -using namespace Mantid::API; - -using namespace MantidQt::MantidWidgets::WorkspaceUtils; -using namespace MantidQt::CustomInterfaces::InterfaceUtils; - -namespace { -Mantid::Kernel::Logger g_log("Stretch"); -struct PlotType { - inline static const std::string ALL = "All"; - inline static const std::string SIGMA = "Sigma"; - inline static const std::string BETA = "Beta"; - inline static const std::string FWHM = "FWHM"; -}; -} // namespace - -namespace MantidQt::CustomInterfaces { -Stretch::Stretch(QWidget *parent, std::unique_ptr algorithmRunner) - : BayesFittingTab(parent, std::move(algorithmRunner)), m_previewSpec(0), m_save(false) { - m_uiForm.setupUi(parent); - - setRunWidgetPresenter(std::make_unique(this, m_uiForm.runWidget)); - - // Create range selector - auto eRangeSelector = m_uiForm.ppPlot->addRangeSelector("StretchERange"); - connect(eRangeSelector, &MantidWidgets::RangeSelector::minValueChanged, this, &Stretch::minValueChanged); - connect(eRangeSelector, &MantidWidgets::RangeSelector::maxValueChanged, this, &Stretch::maxValueChanged); - setupFitOptions(); - setupPropertyBrowser(); - setupPlotOptions(); - - // Connect the data selector for the sample to the mini plot - connect(m_uiForm.dsSample, &DataSelector::dataReady, this, &Stretch::handleSampleInputReady); - connect(m_uiForm.chkSequentialFit, &QCheckBox::toggled, m_uiForm.cbPlot, &QComboBox::setEnabled); - // Connect preview spectrum spinner to handler - connect(m_uiForm.spPreviewSpectrum, static_cast(&QSpinBox::valueChanged), this, - &Stretch::previewSpecChanged); - m_uiForm.spPreviewSpectrum->setMaximum(0); - - // Connect the plot and save push buttons - connect(m_uiForm.pbPlot, &QPushButton::clicked, this, &Stretch::plotWorkspaces); - connect(m_uiForm.pbPlotContour, &QPushButton::clicked, this, &Stretch::plotContourClicked); - connect(m_uiForm.pbSave, &QPushButton::clicked, this, &Stretch::saveWorkspaces); - connect(m_uiForm.pbPlotPreview, &QPushButton::clicked, this, &Stretch::plotCurrentPreview); - - // Allows empty workspace selector when initially selected - m_uiForm.dsSample->isOptional(true); - m_uiForm.dsResolution->isOptional(true); -} - -void Stretch::setFileExtensionsByName(bool filter) { - QStringList const noSuffixes{""}; - auto const tabName("Stretch"); - m_uiForm.dsSample->setFBSuffixes(filter ? getSampleFBSuffixes(tabName) : getExtensions(tabName)); - m_uiForm.dsSample->setWSSuffixes(filter ? getSampleWSSuffixes(tabName) : noSuffixes); - m_uiForm.dsResolution->setFBSuffixes(filter ? getResolutionFBSuffixes(tabName) : getExtensions(tabName)); - m_uiForm.dsResolution->setWSSuffixes(filter ? getResolutionWSSuffixes(tabName) : noSuffixes); -} - -void Stretch::setLoadHistory(bool doLoadHistory) { - m_uiForm.dsSample->setLoadProperty("LoadHistory", doLoadHistory); - m_uiForm.dsResolution->setLoadProperty("LoadHistory", doLoadHistory); -} - -void Stretch::handleValidation(IUserInputValidator *validator) const { - validator->checkDataSelectorIsValid("Sample", m_uiForm.dsSample); - validator->checkDataSelectorIsValid("Resolution", m_uiForm.dsResolution); -} - -void Stretch::handleRun() { - auto const saveDirectory = Mantid::Kernel::ConfigService::Instance().getString("defaultsave.directory"); - if (saveDirectory.empty()) { - int const result = displaySaveDirectoryMessage(); - if (result == QMessageBox::No) { - m_runPresenter->setRunEnabled(true); - return; - } - } - - m_uiForm.ppPlot->watchADS(false); - - // Workspace input - auto const sampleName = m_uiForm.dsSample->getCurrentDataName().toStdString(); - auto const resName = m_uiForm.dsResolution->getCurrentDataName().toStdString(); - - // Collect input from options section - auto const background = m_uiForm.cbBackground->currentText().toStdString(); - - // Collect input from the properties browser - auto const eMin = m_properties["EMin"]->valueText().toDouble(); - auto const eMax = m_properties["EMax"]->valueText().toDouble(); - auto const beta = m_properties["Beta"]->valueText().toInt(); - - // Bool options - auto const elasticPeak = m_uiForm.chkElasticPeak->isChecked(); - - // Construct OutputNames - auto const cutIndex = sampleName.find_last_of("_"); - auto const baseName = sampleName.substr(0, cutIndex); - m_fitWorkspaceName = baseName + "_Stretch_Fit"; - m_contourWorkspaceName = baseName + "_Stretch_Contour"; - - // Temporary developer flag to allow the testing of quickBayes in the Bayes fitting interface - auto const useQuickBayes = SettingsHelper::hasDevelopmentFlag("quickbayes"); - - std::string const algorithmName = useQuickBayes ? "BayesStretch2" : "BayesStretch"; - auto stretch = AlgorithmManager::Instance().create(algorithmName); - stretch->initialize(); - stretch->setProperty("SampleWorkspace", sampleName); - stretch->setProperty("ResolutionWorkspace", resName); - stretch->setProperty("EMin", eMin); - stretch->setProperty("EMax", eMax); - stretch->setProperty("NumberBeta", beta); - stretch->setProperty("Elastic", elasticPeak); - stretch->setProperty("OutputWorkspaceFit", m_fitWorkspaceName); - stretch->setProperty("OutputWorkspaceContour", m_contourWorkspaceName); - stretch->setProperty("Background", background); - if (!useQuickBayes) { - auto const sigma = m_properties["Sigma"]->valueText().toInt(); - auto const nBins = m_properties["SampleBinning"]->valueText().toInt(); - auto const sequence = m_uiForm.chkSequentialFit->isChecked(); - - stretch->setProperty("SampleBins", nBins); - stretch->setProperty("NumberSigma", sigma); - stretch->setProperty("Loop", sequence); - } - - m_batchAlgoRunner->addAlgorithm(stretch); - connect(m_batchAlgoRunner, &API::BatchAlgorithmRunner::batchComplete, this, &Stretch::algorithmComplete); - m_batchAlgoRunner->executeBatchAsync(); -} - -/** - * Handles the saving and plotting of workspaces after execution - */ -void Stretch::algorithmComplete(const bool &error) { - disconnect(m_batchAlgoRunner, &API::BatchAlgorithmRunner::batchComplete, this, &Stretch::algorithmComplete); - - m_runPresenter->setRunEnabled(true); - setPlotResultEnabled(!error); - setPlotContourEnabled(!error); - setSaveResultEnabled(!error); - if (!error) { - if (doesExistInADS(m_contourWorkspaceName)) - populateContourWorkspaceComboBox(); - else - setPlotContourEnabled(false); - - m_uiForm.ppPlot->watchADS(true); - } -} - -void Stretch::populateContourWorkspaceComboBox() { - m_uiForm.cbPlotContour->clear(); - auto const contourGroup = getADSWorkspace(m_contourWorkspaceName); - auto const contourNames = contourGroup->getNames(); - for (auto const &name : contourNames) - m_uiForm.cbPlotContour->addItem(QString::fromStdString(name)); -} - -/** - * Handles the saving of workspaces post algorithm completion - * when save button is clicked - */ -void Stretch::saveWorkspaces() { - - auto fitWorkspace = QString::fromStdString(m_fitWorkspaceName); - auto contourWorkspace = QString::fromStdString(m_contourWorkspaceName); - // Check workspaces exist - InelasticTab::checkADSForPlotSaveWorkspace(m_fitWorkspaceName, false); - InelasticTab::checkADSForPlotSaveWorkspace(m_contourWorkspaceName, false); - - const auto saveDir = - QString::fromStdString(Mantid::Kernel::ConfigService::Instance().getString("defaultsave.directory")); - // Check validity of save path - const auto fitFullPath = saveDir + fitWorkspace + QString::fromStdString(".nxs"); - const auto contourFullPath = saveDir + contourWorkspace + QString::fromStdString(".nxs"); - addSaveWorkspaceToQueue(fitWorkspace, fitFullPath); - addSaveWorkspaceToQueue(contourWorkspace, contourFullPath); - m_batchAlgoRunner->executeBatchAsync(); -} - -int Stretch::displaySaveDirectoryMessage() { - char const *textMessage = "BayesStretch requires a default save directory and " - "one is not currently set." - " If run, the algorithm will default to saving files " - "to the current working directory." - " Would you still like to run the algorithm?"; - return QMessageBox::question(nullptr, tr("Save Directory"), tr(textMessage), QMessageBox::Yes, QMessageBox::No, - QMessageBox::NoButton); -} - -/** - * Handles the plotting of workspace post algorithm completion - */ -void Stretch::plotWorkspaces() { - setPlotResultIsPlotting(true); - - std::string const plotType = m_uiForm.cbPlot->currentText().toStdString(); - auto const plotErrors = SettingsHelper::externalPlotErrorBars(); - auto const plotSigma = (plotType == "All" || plotType == "Sigma"); - auto const plotBeta = (plotType == "All" || plotType == "Beta"); - - auto const fitWorkspace = getADSWorkspace(m_fitWorkspaceName); - for (auto it = fitWorkspace->begin(); it < fitWorkspace->end(); ++it) { - auto const name = (*it)->getName(); - if (plotSigma && name.substr(name.length() - 5) == "Sigma") { - m_plotter->plotSpectra(name, "0", plotErrors); - } else if (plotBeta && name.substr(name.length() - 4) == "Beta") { - m_plotter->plotSpectra(name, "0", plotErrors); - } - } - setPlotResultIsPlotting(false); -} - -void Stretch::plotContourClicked() { - setPlotContourIsPlotting(true); - - auto const workspaceName = m_uiForm.cbPlotContour->currentText().toStdString(); - if (checkADSForPlotSaveWorkspace(workspaceName, true)) - m_plotter->plotContour(workspaceName); - - setPlotContourIsPlotting(false); -} - -/** - * Set the data selectors to use the default save directory - * when browsing for input files. - * - * @param settings :: The current settings - */ -void Stretch::loadSettings(const QSettings &settings) { - m_uiForm.dsSample->readSettings(settings.group()); - m_uiForm.dsResolution->readSettings(settings.group()); -} - -/** - * Get called whenever the settings are updated - * - * @param settings :: The current settings - */ -void Stretch::applySettings(std::map const &settings) { - setupFitOptions(); - setupPropertyBrowser(); - setupPlotOptions(); - setFileExtensionsByName(settings.at("RestrictInput").toBool()); - setLoadHistory(settings.at("LoadHistory").toBool()); -} - -/** - * Setup the fit options based on the algorithm used - * - */ -void Stretch::setupFitOptions() { - auto const useQuickBayes = SettingsHelper::hasDevelopmentFlag("quickbayes"); - m_uiForm.cbBackground->clear(); - if (useQuickBayes) { - m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::LINEAR)); - m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::FLAT)); - m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::ZERO)); - m_uiForm.chkSequentialFit->hide(); - } else { - m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::SLOPING)); - m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::FLAT)); - m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::ZERO)); - m_uiForm.chkSequentialFit->show(); - } -} - -/** - * Setup the property browser based on the algorithm used - * - */ -void Stretch::setupPropertyBrowser() { - auto const useQuickBayes = SettingsHelper::hasDevelopmentFlag("quickbayes"); - - m_properties.clear(); - m_dblManager->clear(); - m_propTree->clear(); - - m_uiForm.treeSpace->addWidget(m_propTree); - - m_properties["EMin"] = m_dblManager->addProperty("EMin"); - m_properties["EMax"] = m_dblManager->addProperty("EMax"); - m_properties["Beta"] = m_dblManager->addProperty("Beta"); - - m_dblManager->setDecimals(m_properties["EMin"], NUM_DECIMALS); - m_dblManager->setDecimals(m_properties["EMax"], NUM_DECIMALS); - m_dblManager->setDecimals(m_properties["Beta"], INT_DECIMALS); - - m_propTree->addProperty(m_properties["EMin"]); - m_propTree->addProperty(m_properties["EMax"]); - m_propTree->addProperty(m_properties["Beta"]); - - m_dblManager->setValue(m_properties["Beta"], 50); - m_dblManager->setMinimum(m_properties["Beta"], 1); - m_dblManager->setMaximum(m_properties["Beta"], 200); - - if (!useQuickBayes) { - m_properties["SampleBinning"] = m_dblManager->addProperty("Sample Binning"); - m_properties["Sigma"] = m_dblManager->addProperty("Sigma"); - - m_dblManager->setDecimals(m_properties["SampleBinning"], INT_DECIMALS); - m_dblManager->setDecimals(m_properties["Sigma"], INT_DECIMALS); - - m_propTree->addProperty(m_properties["SampleBinning"]); - m_propTree->addProperty(m_properties["Sigma"]); - - m_dblManager->setValue(m_properties["Sigma"], 50); - m_dblManager->setMinimum(m_properties["Sigma"], 1); - m_dblManager->setMaximum(m_properties["Sigma"], 200); - m_dblManager->setValue(m_properties["SampleBinning"], 1); - m_dblManager->setMinimum(m_properties["SampleBinning"], 1); - } - - formatTreeWidget(m_propTree, m_properties); -} - -/** - * Setup the plot options based on the algorithm used - * - */ -void Stretch::setupPlotOptions() { - auto const useQuickBayes = SettingsHelper::hasDevelopmentFlag("quickbayes"); - m_uiForm.cbPlot->clear(); - if (useQuickBayes) { - m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::ALL)); - m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::FWHM)); - m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::BETA)); - } else { - m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::ALL)); - m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::SIGMA)); - m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::BETA)); - } -} - -/** - * Plots the loaded file to the miniplot and sets the guides - * and the range - * - * @param filename :: The name of the workspace to plot - */ -void Stretch::handleSampleInputReady(const QString &filename) { - try { - m_uiForm.ppPlot->clear(); - m_uiForm.ppPlot->addSpectrum("Sample", filename, 0); - } catch (std::exception const &ex) { - g_log.warning(ex.what()); - return; - } - - // update the maximum and minimum range bar positions - auto const range = getXRangeFromWorkspace(filename.toStdString()); - auto eRangeSelector = m_uiForm.ppPlot->getRangeSelector("StretchERange"); - setRangeSelector(eRangeSelector, m_properties["EMin"], m_properties["EMax"], range); - setPlotPropertyRange(eRangeSelector, m_properties["EMin"], m_properties["EMax"], range); - // update the current positions of the range bars - eRangeSelector->setMinimum(range.first); - eRangeSelector->setMaximum(range.second); - - // set the max spectrum - MatrixWorkspace_const_sptr sampleWs = getADSWorkspace(filename.toStdString()); - const int spectra = static_cast(sampleWs->getNumberHistograms()); - m_uiForm.spPreviewSpectrum->setMaximum(spectra - 1); -} - -/** - * Sets a new preview spectrum for the mini plot. - * - * @param value workspace index - */ -void Stretch::previewSpecChanged(int value) { - m_previewSpec = value; - - if (!m_uiForm.dsSample->isValid()) - return; - - m_uiForm.ppPlot->clear(); - - auto const sampleName = m_uiForm.dsSample->getCurrentDataName(); - try { - m_uiForm.ppPlot->addSpectrum("Sample", sampleName, m_previewSpec); - } catch (std::exception const &ex) { - g_log.warning(ex.what()); - } -} - -/** - * plots the current miniplot preview - */ -void Stretch::plotCurrentPreview() { - if (m_uiForm.ppPlot->hasCurve("Sample")) { - m_plotter->plotSpectra(m_uiForm.dsSample->getCurrentDataName().toStdString(), std::to_string(m_previewSpec), - SettingsHelper::externalPlotErrorBars()); - } -} - -/** - * Updates the property manager when the lower guide is moved on the mini plot - * - * @param min :: The new value of the lower guide - */ -void Stretch::minValueChanged(double min) { - disconnect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &Stretch::updateProperties); - m_dblManager->setValue(m_properties["EMin"], min); - connect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &Stretch::updateProperties); -} - -/** - * Updates the property manager when the upper guide is moved on the mini plot - * - * @param max :: The new value of the upper guide - */ -void Stretch::maxValueChanged(double max) { - disconnect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &Stretch::updateProperties); - m_dblManager->setValue(m_properties["EMax"], max); - connect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &Stretch::updateProperties); -} - -/** - * Handles when properties in the property manager are updated. - * - * @param prop :: The property being updated - * @param val :: The new value for the property - */ -void Stretch::updateProperties(QtProperty *prop, double val) { - auto eRangeSelector = m_uiForm.ppPlot->getRangeSelector("StretchERange"); - - disconnect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &Stretch::updateProperties); - - if (prop == m_properties["EMin"]) { - setRangeSelectorMin(m_properties["EMin"], m_properties["EMax"], eRangeSelector, val); - } else if (prop == m_properties["EMax"]) { - setRangeSelectorMax(m_properties["EMin"], m_properties["EMax"], eRangeSelector, val); - } - - connect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &Stretch::updateProperties); -} - -void Stretch::setPlotResultEnabled(bool enabled) { - m_uiForm.pbPlot->setEnabled(enabled); - m_uiForm.cbPlot->setEnabled(enabled); -} - -void Stretch::setPlotContourEnabled(bool enabled) { - m_uiForm.pbPlotContour->setEnabled(enabled); - m_uiForm.cbPlotContour->setEnabled(enabled); -} - -void Stretch::setSaveResultEnabled(bool enabled) { m_uiForm.pbSave->setEnabled(enabled); } - -void Stretch::setButtonsEnabled(bool enabled) { - m_runPresenter->setRunEnabled(enabled); - setPlotResultEnabled(enabled); - setPlotContourEnabled(enabled); - setSaveResultEnabled(enabled); -} - -void Stretch::setPlotResultIsPlotting(bool plotting) { - m_uiForm.pbPlot->setText(plotting ? "Plotting..." : "Plot"); - setButtonsEnabled(!plotting); -} - -void Stretch::setPlotContourIsPlotting(bool plotting) { - m_uiForm.pbPlotContour->setText(plotting ? "Plotting..." : "Plot Contour"); - setButtonsEnabled(!plotting); -} - -} // namespace MantidQt::CustomInterfaces diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/Stretch.h b/qt/scientific_interfaces/Inelastic/BayesFitting/Stretch.h deleted file mode 100644 index 56bab13475ba..000000000000 --- a/qt/scientific_interfaces/Inelastic/BayesFitting/Stretch.h +++ /dev/null @@ -1,81 +0,0 @@ -// Mantid Repository : https://github.com/mantidproject/mantid -// -// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, -// NScD Oak Ridge National Laboratory, European Spallation Source, -// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS -// SPDX - License - Identifier: GPL - 3.0 + -#pragma once - -#include "BayesFittingTab.h" -#include "DllConfig.h" -#include "MantidAPI/WorkspaceGroup_fwd.h" -#include "MantidQtWidgets/Spectroscopy/RunWidget/IRunSubscriber.h" -#include "ui_Stretch.h" - -namespace MantidQt { -namespace CustomInterfaces { -class MANTIDQT_INELASTIC_DLL Stretch : public BayesFittingTab, public IRunSubscriber { - Q_OBJECT - -public: - Stretch(QWidget *parent = nullptr, std::unique_ptr algorithmRunner = nullptr); - - /// Load default settings into the interface - void loadSettings(const QSettings &settings) override; - - void handleValidation(IUserInputValidator *validator) const override; - void handleRun() override; - const std::string getSubscriberName() const override { return "Stretch"; } - - // Slot for when settings are changed - void applySettings(std::map const &settings) override; - - // Setup fit options, property browser, and plot options ui elements - void setupFitOptions(); - void setupPropertyBrowser(); - void setupPlotOptions(); - -private slots: - /// Slot for when the min range on the range selector changes - void minValueChanged(double min); - /// Slot for when the min range on the range selector changes - void maxValueChanged(double max); - /// Slot to update the guides when the range properties change - void updateProperties(QtProperty *prop, double val) override; - /// Slot to handle when a new sample file is available - void handleSampleInputReady(const QString &filename); - /// Save the workspaces produces from the algorithm - void saveWorkspaces(); - - void plotWorkspaces(); - void plotContourClicked(); - void algorithmComplete(const bool &error); - void plotCurrentPreview(); - void previewSpecChanged(int value); - -private: - void setFileExtensionsByName(bool filter) override; - void setLoadHistory(bool doLoadHistory) override; - - void populateContourWorkspaceComboBox(); - int displaySaveDirectoryMessage(); - - void setPlotResultEnabled(bool enabled); - void setPlotContourEnabled(bool enabled); - void setSaveResultEnabled(bool enabled); - void setButtonsEnabled(bool enabled); - void setPlotResultIsPlotting(bool plotting); - void setPlotContourIsPlotting(bool plotting); - - /// Current preview spectrum - int m_previewSpec; - // The ui form - Ui::Stretch m_uiForm; - // Output Names - std::string m_fitWorkspaceName; - std::string m_contourWorkspaceName; - // state of plot and save when algorithm is run - bool m_save; -}; -} // namespace CustomInterfaces -} // namespace MantidQt diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/StretchData.h b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchData.h new file mode 100644 index 000000000000..caa8190b0494 --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchData.h @@ -0,0 +1,38 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include + +namespace MantidQt::CustomInterfaces { + +struct StretchRunData { + const std::string sampleName; + const std::string resolutionName; + const std::string backgroundName; + const double eMin; + const double eMax; + const int beta; + const bool elasticPeak; + const int sigma; + const int sampleBinning; + const bool sequentialFit; + + StretchRunData(const std::string &sample, const std::string &resolution, double min, double max, int b, bool elastic, + const std::string &bg, int sig, int binning, bool sequential) + : sampleName(sample), resolutionName(resolution), backgroundName(bg), eMin(min), eMax(max), beta(b), + elasticPeak(elastic), sigma(sig), sampleBinning(binning), sequentialFit(sequential) {} +}; + +struct CurrentPreviewData { + const std::string sampleName; + const bool hasSample; + + CurrentPreviewData(const std::string &sampleName, bool hasSample) : sampleName(sampleName), hasSample(hasSample) {} +}; + +} // namespace MantidQt::CustomInterfaces diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/StretchModel.cpp b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchModel.cpp new file mode 100644 index 000000000000..82e58e2558ee --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchModel.cpp @@ -0,0 +1,56 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#include "StretchModel.h" +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/AlgorithmProperties.h" +#include "MantidAPI/AlgorithmRuntimeProps.h" + +using namespace Mantid::API; + +namespace MantidQt::CustomInterfaces { + +MantidQt::API::IConfiguredAlgorithm_sptr StretchModel::stretchAlgorithm(const StretchRunData &algParams, + const std::string &fitWorkspaceName, + const std::string &contourWorkspaceName, + const bool useQuickBayes) const { + auto properties = std::make_unique(); + + properties->setProperty("SampleWorkspace", algParams.sampleName); + properties->setProperty("ResolutionWorkspace", algParams.resolutionName); + properties->setProperty("EMin", algParams.eMin); + properties->setProperty("EMax", algParams.eMax); + properties->setProperty("NumberBeta", algParams.beta); + properties->setProperty("Elastic", algParams.elasticPeak); + properties->setProperty("OutputWorkspaceFit", fitWorkspaceName); + properties->setProperty("OutputWorkspaceContour", contourWorkspaceName); + properties->setProperty("Background", algParams.backgroundName); + if (!useQuickBayes) { + properties->setProperty("SampleBins", algParams.sampleBinning); + properties->setProperty("NumberSigma", algParams.sigma); + properties->setProperty("Loop", algParams.sequentialFit); + } + + std::string const algorithmName = useQuickBayes ? "BayesStretch2" : "BayesStretch"; + auto stretch = AlgorithmManager::Instance().create(algorithmName); + stretch->initialize(); + + return std::make_shared(stretch, std::move(properties)); +} + +API::IConfiguredAlgorithm_sptr StretchModel::setupSaveAlgorithm(const std::string &wsName) const { + auto saveAlgo = AlgorithmManager::Instance().create("SaveNexusProcessed"); + saveAlgo->initialize(); + + auto saveProps = std::make_unique(); + + saveProps->setPropertyValue("Filename", wsName + ".nxs"); + saveProps->setPropertyValue("InputWorkspace", wsName); + + return std::make_shared(saveAlgo, std::move(saveProps)); +} + +} // namespace MantidQt::CustomInterfaces diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/StretchModel.h b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchModel.h new file mode 100644 index 000000000000..a148696f5098 --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchModel.h @@ -0,0 +1,41 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include "DllConfig.h" +#include "StretchData.h" +#include + +namespace MantidQt { +namespace CustomInterfaces { + +class MANTIDQT_INELASTIC_DLL IStretchModel { +public: + virtual ~IStretchModel() = default; + + virtual MantidQt::API::IConfiguredAlgorithm_sptr stretchAlgorithm(const StretchRunData &algParams, + const std::string &fitWorkspaceName, + const std::string &contourWorkspaceName, + const bool useQuickBayes) const = 0; + + virtual API::IConfiguredAlgorithm_sptr setupSaveAlgorithm(const std::string &wsName) const = 0; +}; + +class MANTIDQT_INELASTIC_DLL StretchModel : public IStretchModel { +public: + StretchModel() = default; + ~StretchModel() override = default; + + MantidQt::API::IConfiguredAlgorithm_sptr stretchAlgorithm(const StretchRunData &algParams, + const std::string &fitWorkspaceName, + const std::string &contourWorkspaceNames, + const bool useQuickBayes = false) const override; + + API::IConfiguredAlgorithm_sptr setupSaveAlgorithm(const std::string &wsName) const override; +}; +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/StretchPresenter.cpp b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchPresenter.cpp new file mode 100644 index 000000000000..68c28c3030c7 --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchPresenter.cpp @@ -0,0 +1,164 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#include "StretchPresenter.h" +#include "MantidQtWidgets/Common/UserInputValidator.h" +#include "MantidQtWidgets/Common/WorkspaceUtils.h" +#include "MantidQtWidgets/Spectroscopy/InterfaceUtils.h" +#include "MantidQtWidgets/Spectroscopy/SettingsWidget/SettingsHelper.h" +#include + +using namespace Mantid::API; +using namespace MantidQt::MantidWidgets::WorkspaceUtils; + +namespace { +Mantid::Kernel::Logger g_log("Stretch"); +struct PlotType { + inline static const std::string ALL = "All"; + inline static const std::string SIGMA = "Sigma"; + inline static const std::string BETA = "Beta"; +}; +} // namespace + +namespace MantidQt::CustomInterfaces { +StretchPresenter::StretchPresenter(QWidget *parent, IStretchView *view, std::unique_ptr model, + std::unique_ptr algorithmRunner) + : BayesFittingTab(parent, std::move(algorithmRunner)), m_previewSpec(0), m_view(view), m_model(std::move(model)) { + m_view->subscribePresenter(this); + + setRunWidgetPresenter(std::make_unique(this, m_view->getRunWidget())); +} + +void StretchPresenter::handleValidation(IUserInputValidator *validator) const { m_view->validateUserInput(validator); } + +void StretchPresenter::handleRun() { + auto const saveDirectory = Mantid::Kernel::ConfigService::Instance().getString("defaultsave.directory"); + if (saveDirectory.empty()) { + auto const result = m_view->displaySaveDirectoryMessage(); + if (result) { + m_runPresenter->setRunEnabled(true); + return; + } + } + + m_view->setPlotADSEnabled(false); + + StretchRunData algParams = m_view->getRunData(); + + auto const cutIndex = algParams.sampleName.find_last_of("_"); + auto const baseName = algParams.sampleName.substr(0, cutIndex); + m_fitWorkspaceName = baseName + "_Stretch_Fit"; + m_contourWorkspaceName = baseName + "_Stretch_Contour"; + + auto const useQuickBayes = SettingsHelper::hasDevelopmentFlag("quickbayes"); + auto stretch = m_model->stretchAlgorithm(algParams, m_fitWorkspaceName, m_contourWorkspaceName, useQuickBayes); + m_algorithmRunner->execute(stretch); +} + +void StretchPresenter::runComplete(IAlgorithm_sptr const &algorithm, bool const error) { + (void)algorithm; + + m_view->setPlotResultEnabled(!error); + m_view->setPlotContourEnabled(!error); + m_view->setSaveResultEnabled(!error); + if (!error) { + if (doesExistInADS(m_contourWorkspaceName)) + resetPlotContourOptions(); + else + m_view->setPlotContourEnabled(false); + + m_view->setPlotADSEnabled(false); + } +} + +void StretchPresenter::setButtonsEnabled(bool enabled) { + m_runPresenter->setRunEnabled(enabled); + m_view->setButtonsEnabled(enabled); +} + +void StretchPresenter::setPlotResultIsPlotting(bool plotting) { + m_view->setPlotResultIsPlotting(plotting); + setButtonsEnabled(!plotting); +} + +void StretchPresenter::setPlotContourIsPlotting(bool plotting) { + m_view->setPlotContourIsPlotting(plotting); + setButtonsEnabled(!plotting); +} + +void StretchPresenter::resetPlotContourOptions() { + auto const contourGroup = getADSWorkspace(m_contourWorkspaceName); + auto const contourNames = contourGroup->getNames(); + m_view->resetPlotContourOptions(contourNames); +} + +void StretchPresenter::setFileExtensionsByName(bool filter) { m_view->setFileExtensionsByName(filter); } + +void StretchPresenter::setLoadHistory(bool doLoadHistory) { m_view->setLoadHistory(doLoadHistory); } + +void StretchPresenter::notifySaveClicked() { + auto fitWorkspace = QString::fromStdString(m_fitWorkspaceName); + auto contourWorkspace = QString::fromStdString(m_contourWorkspaceName); + + InterfaceUtils::checkADSForPlotSaveWorkspace(m_fitWorkspaceName, false); + InterfaceUtils::checkADSForPlotSaveWorkspace(m_contourWorkspaceName, false); + + std::deque algorithmQueue; + + algorithmQueue.push_back(m_model->setupSaveAlgorithm(fitWorkspace.toStdString())); + algorithmQueue.push_back(m_model->setupSaveAlgorithm(contourWorkspace.toStdString())); + + m_algorithmRunner->execute(algorithmQueue); +} + +void StretchPresenter::notifyPlotClicked() { + setPlotResultIsPlotting(true); + + std::string const plotType = m_view->getPlotType(); + auto const plotErrors = SettingsHelper::externalPlotErrorBars(); + auto const plotSigma = (plotType == PlotType::ALL || plotType == PlotType::SIGMA); + auto const plotBeta = (plotType == PlotType::ALL || plotType == PlotType::BETA); + + auto const fitWorkspace = getADSWorkspace(m_fitWorkspaceName); + for (auto it = fitWorkspace->begin(); it < fitWorkspace->end(); ++it) { + auto const name = (*it)->getName(); + if (plotSigma && name.substr(name.length() - 5) == PlotType::SIGMA) { + m_plotter->plotSpectra(name, "0", plotErrors); + } else if (plotBeta && name.substr(name.length() - 4) == PlotType::BETA) { + m_plotter->plotSpectra(name, "0", plotErrors); + } + } + + setPlotResultIsPlotting(false); +} + +void StretchPresenter::notifyPlotContourClicked() { + setPlotContourIsPlotting(true); + + auto const workspaceName = m_view->getPlotContour(); + if (checkADSForPlotSaveWorkspace(workspaceName, true)) + m_plotter->plotContour(workspaceName); + + setPlotContourIsPlotting(false); +} + +void StretchPresenter::notifyPreviewSpecChanged(int specNum) { m_previewSpec = specNum; } + +void StretchPresenter::notifyPlotCurrentPreviewClicked() { + auto previewData = m_view->getCurrentPreviewData(); + if (previewData.hasSample) { + m_plotter->plotSpectra(previewData.sampleName, std::to_string(m_previewSpec), + SettingsHelper::externalPlotErrorBars()); + } +} + +void StretchPresenter::loadSettings(const QSettings &settings) { m_view->loadSettings(settings); } + +void StretchPresenter::applySettings(std::map const &settings) { + m_view->applySettings(settings); +} + +} // namespace MantidQt::CustomInterfaces diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/StretchPresenter.h b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchPresenter.h new file mode 100644 index 000000000000..380c81de29fa --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchPresenter.h @@ -0,0 +1,70 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include "BayesFittingTab.h" +#include "DllConfig.h" +#include "MantidQtWidgets/Common/IAlgorithmRunnerSubscriber.h" +#include "MantidQtWidgets/Spectroscopy/RunWidget/IRunSubscriber.h" +#include "StretchData.h" +#include "StretchModel.h" +#include "StretchView.h" +#include "ui_Stretch.h" + +namespace MantidQt { +namespace CustomInterfaces { + +class MANTIDQT_INELASTIC_DLL IStretchPresenter : public IRunSubscriber, + public IStretchViewSubscriber, + public API::IAlgorithmRunnerSubscriber { +public: + virtual ~IStretchPresenter() = default; +}; + +class MANTIDQT_INELASTIC_DLL StretchPresenter : public BayesFittingTab, public IStretchPresenter { + +public: + StretchPresenter(QWidget *parent, IStretchView *view, std::unique_ptr model, + std::unique_ptr algorithmRunner); + ~StretchPresenter() override = default; + + void handleValidation(IUserInputValidator *validator) const override; + void handleRun() override; + const std::string getSubscriberName() const override { return "Stretch"; } + + void loadSettings(const QSettings &settings) override; + void applySettings(std::map const &settings) override; + + void notifySaveClicked() override; + void notifyPlotClicked() override; + void notifyPlotContourClicked() override; + void notifyPlotCurrentPreviewClicked() override; + void notifyPreviewSpecChanged(int specNum) override; + +protected: + void runComplete(IAlgorithm_sptr const &algorithm, bool const error) override; + +private: + void setFileExtensionsByName(bool filter) override; + void setLoadHistory(bool doLoadHistory) override; + + void setButtonsEnabled(bool enabled); + void setPlotResultIsPlotting(bool plotting); + void setPlotContourIsPlotting(bool plotting); + + void resetPlotContourOptions(); + + int m_previewSpec; + + std::string m_fitWorkspaceName; + std::string m_contourWorkspaceName; + + IStretchView *m_view; + std::unique_ptr m_model; +}; +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/StretchView.cpp b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchView.cpp new file mode 100644 index 000000000000..cbce19a1a039 --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchView.cpp @@ -0,0 +1,337 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#include "StretchView.h" +#include "MantidQtWidgets/Common/WorkspaceUtils.h" +#include "MantidQtWidgets/Plotting/RangeSelector.h" +#include "MantidQtWidgets/Spectroscopy/InterfaceUtils.h" +#include "MantidQtWidgets/Spectroscopy/SettingsWidget/SettingsHelper.h" + +using namespace MantidQt::MantidWidgets::WorkspaceUtils; +using namespace MantidQt::CustomInterfaces::InterfaceUtils; + +namespace { + +static const unsigned int NUM_DECIMALS = 6; +static const unsigned int INT_DECIMALS = 0; + +Mantid::Kernel::Logger g_log("Stretch"); + +struct BackgroundType { + inline static const std::string SLOPING = "Sloping"; + inline static const std::string FLAT = "Flat"; + inline static const std::string ZERO = "Zero"; + inline static const std::string LINEAR = "Linear"; +}; + +struct PlotType { + inline static const std::string ALL = "All"; + inline static const std::string SIGMA = "Sigma"; + inline static const std::string BETA = "Beta"; + inline static const std::string FWHM = "FWHM"; +}; + +} // namespace + +namespace MantidQt::CustomInterfaces { +StretchView::StretchView(QWidget *parent) + : m_dblManager(new QtDoublePropertyManager()), m_properties(), m_propTree(new QtTreePropertyBrowser()), + m_dblEdFac(new DoubleEditorFactory()) { + m_uiForm.setupUi(parent); + + m_propTree->setFactoryForManager(m_dblManager, m_dblEdFac); + + auto eRangeSelector = m_uiForm.ppPlot->addRangeSelector("StretchERange"); + connect(eRangeSelector, &MantidWidgets::RangeSelector::minValueChanged, this, &StretchView::minValueChanged); + connect(eRangeSelector, &MantidWidgets::RangeSelector::maxValueChanged, this, &StretchView::maxValueChanged); + setupFitOptions(); + setupPropertyBrowser(); + setupPlotOptions(); + + connect(m_uiForm.dsSample, &DataSelector::dataReady, this, &StretchView::handleSampleInputReady); + connect(m_uiForm.chkSequentialFit, &QCheckBox::toggled, m_uiForm.cbPlot, &QComboBox::setEnabled); + + connect(m_uiForm.spPreviewSpectrum, static_cast(&QSpinBox::valueChanged), this, + &StretchView::previewSpecChanged); + m_uiForm.spPreviewSpectrum->setMaximum(0); + + connect(m_uiForm.pbSave, &QPushButton::clicked, this, &StretchView::saveWorkspacesClicked); + connect(m_uiForm.pbPlot, &QPushButton::clicked, this, &StretchView::plotWorkspacesClicked); + connect(m_uiForm.pbPlotContour, &QPushButton::clicked, this, &StretchView::plotContourClicked); + connect(m_uiForm.pbPlotPreview, &QPushButton::clicked, this, &StretchView::plotCurrentPreviewClicked); + + m_uiForm.dsSample->isOptional(true); + m_uiForm.dsResolution->isOptional(true); +} + +void StretchView::subscribePresenter(IStretchViewSubscriber *presenter) { m_presenter = presenter; } + +void StretchView::loadSettings(const QSettings &settings) { + m_uiForm.dsSample->readSettings(settings.group()); + m_uiForm.dsResolution->readSettings(settings.group()); +} + +void StretchView::applySettings(std::map const &settings) { + setupFitOptions(); + setupPropertyBrowser(); + setupPlotOptions(); + setFileExtensionsByName(settings.at("RestrictInput").toBool()); + setLoadHistory(settings.at("LoadHistory").toBool()); +} + +void StretchView::minValueChanged(double min) { + disconnect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &StretchView::propertiesUpdated); + m_dblManager->setValue(m_properties["EMin"], min); + connect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &StretchView::propertiesUpdated); +} + +void StretchView::maxValueChanged(double max) { + disconnect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &StretchView::propertiesUpdated); + m_dblManager->setValue(m_properties["EMax"], max); + connect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &StretchView::propertiesUpdated); +} + +void StretchView::propertiesUpdated(QtProperty *prop, double val) { + auto eRangeSelector = m_uiForm.ppPlot->getRangeSelector("StretchERange"); + + disconnect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &StretchView::propertiesUpdated); + + if (prop == m_properties["EMin"]) { + setRangeSelectorMin(m_dblManager, m_properties["EMin"], m_properties["EMax"], eRangeSelector, val); + } else if (prop == m_properties["EMax"]) { + setRangeSelectorMax(m_dblManager, m_properties["EMin"], m_properties["EMax"], eRangeSelector, val); + } + + connect(m_dblManager, &QtDoublePropertyManager::valueChanged, this, &StretchView::propertiesUpdated); +} + +void StretchView::setupFitOptions() { + auto const useQuickBayes = SettingsHelper::hasDevelopmentFlag("quickbayes"); + m_uiForm.cbBackground->clear(); + if (useQuickBayes) { + m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::LINEAR)); + m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::FLAT)); + m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::ZERO)); + m_uiForm.chkSequentialFit->hide(); + } else { + m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::SLOPING)); + m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::FLAT)); + m_uiForm.cbBackground->addItem(QString::fromStdString(BackgroundType::ZERO)); + m_uiForm.chkSequentialFit->show(); + } +} + +void StretchView::setupPropertyBrowser() { + auto const useQuickBayes = SettingsHelper::hasDevelopmentFlag("quickbayes"); + + m_properties.clear(); + m_dblManager->clear(); + m_propTree->clear(); + + m_uiForm.treeSpace->addWidget(m_propTree); + + m_properties["EMin"] = m_dblManager->addProperty("EMin"); + m_properties["EMax"] = m_dblManager->addProperty("EMax"); + m_properties["Beta"] = m_dblManager->addProperty("Beta"); + + m_dblManager->setDecimals(m_properties["EMin"], NUM_DECIMALS); + m_dblManager->setDecimals(m_properties["EMax"], NUM_DECIMALS); + m_dblManager->setDecimals(m_properties["Beta"], INT_DECIMALS); + + m_propTree->addProperty(m_properties["EMin"]); + m_propTree->addProperty(m_properties["EMax"]); + m_propTree->addProperty(m_properties["Beta"]); + + m_dblManager->setValue(m_properties["Beta"], 50); + m_dblManager->setMinimum(m_properties["Beta"], 1); + m_dblManager->setMaximum(m_properties["Beta"], 200); + + if (!useQuickBayes) { + m_properties["SampleBinning"] = m_dblManager->addProperty("Sample Binning"); + m_properties["Sigma"] = m_dblManager->addProperty("Sigma"); + + m_dblManager->setDecimals(m_properties["SampleBinning"], INT_DECIMALS); + m_dblManager->setDecimals(m_properties["Sigma"], INT_DECIMALS); + + m_propTree->addProperty(m_properties["SampleBinning"]); + m_propTree->addProperty(m_properties["Sigma"]); + + m_dblManager->setValue(m_properties["Sigma"], 50); + m_dblManager->setMinimum(m_properties["Sigma"], 1); + m_dblManager->setMaximum(m_properties["Sigma"], 200); + m_dblManager->setValue(m_properties["SampleBinning"], 1); + m_dblManager->setMinimum(m_properties["SampleBinning"], 1); + } + + formatTreeWidget(m_propTree, m_properties); +} + +// TODO(): common function in bayes fitting tab +void StretchView::formatTreeWidget(QtTreePropertyBrowser *treeWidget, + QMap const &properties) const { + treeWidget->setIndentation(0); + for (auto const &item : properties) + treeWidget->setBackgroundColor(treeWidget->topLevelItem(item), QColor(246, 246, 246)); +} + +void StretchView::setupPlotOptions() { + auto const useQuickBayes = SettingsHelper::hasDevelopmentFlag("quickbayes"); + m_uiForm.cbPlot->clear(); + if (useQuickBayes) { + m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::ALL)); + m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::FWHM)); + m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::BETA)); + } else { + m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::ALL)); + m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::SIGMA)); + m_uiForm.cbPlot->addItem(QString::fromStdString(PlotType::BETA)); + } +} + +void StretchView::handleSampleInputReady(const QString &filename) { + try { + m_uiForm.ppPlot->clear(); + m_uiForm.ppPlot->addSpectrum("Sample", filename, 0); + } catch (std::exception const &ex) { + g_log.warning(ex.what()); + return; + } + + auto const range = getXRangeFromWorkspace(filename.toStdString()); + auto eRangeSelector = m_uiForm.ppPlot->getRangeSelector("StretchERange"); + setRangeSelector(m_dblManager, eRangeSelector, m_properties["EMin"], m_properties["EMax"], range, std::nullopt); + setPlotPropertyRange(m_dblManager, eRangeSelector, m_properties["EMin"], m_properties["EMax"], range); + + eRangeSelector->setMinimum(range.first); + eRangeSelector->setMaximum(range.second); + + MatrixWorkspace_const_sptr sampleWs = getADSWorkspace(filename.toStdString()); + const int spectra = static_cast(sampleWs->getNumberHistograms()); + m_uiForm.spPreviewSpectrum->setMaximum(spectra - 1); +} + +void StretchView::previewSpecChanged(int value) { + if (!m_uiForm.dsSample->isValid()) + return; + + m_uiForm.ppPlot->clear(); + + auto const sampleName = m_uiForm.dsSample->getCurrentDataName(); + try { + m_uiForm.ppPlot->addSpectrum("Sample", sampleName, value); + m_presenter->notifyPreviewSpecChanged(value); + } catch (std::exception const &ex) { + g_log.warning(ex.what()); + } +} + +void StretchView::saveWorkspacesClicked() { m_presenter->notifySaveClicked(); } + +void StretchView::plotWorkspacesClicked() { m_presenter->notifyPlotClicked(); } + +void StretchView::plotContourClicked() { m_presenter->notifyPlotContourClicked(); } + +void StretchView::plotCurrentPreviewClicked() { m_presenter->notifyPlotCurrentPreviewClicked(); } + +void StretchView::setFileExtensionsByName(bool filter) { + QStringList const noSuffixes{""}; + auto const tabName("Stretch"); + m_uiForm.dsSample->setFBSuffixes(filter ? getSampleFBSuffixes(tabName) : getExtensions(tabName)); + m_uiForm.dsSample->setWSSuffixes(filter ? getSampleWSSuffixes(tabName) : noSuffixes); + m_uiForm.dsResolution->setFBSuffixes(filter ? getResolutionFBSuffixes(tabName) : getExtensions(tabName)); + m_uiForm.dsResolution->setWSSuffixes(filter ? getResolutionWSSuffixes(tabName) : noSuffixes); +} + +void StretchView::setLoadHistory(bool doLoadHistory) { + m_uiForm.dsSample->setLoadProperty("LoadHistory", doLoadHistory); + m_uiForm.dsResolution->setLoadProperty("LoadHistory", doLoadHistory); +} + +void StretchView::validateUserInput(IUserInputValidator *validator) const { + validator->checkDataSelectorIsValid("Sample", m_uiForm.dsSample); + validator->checkDataSelectorIsValid("Resolution", m_uiForm.dsResolution); +} + +StretchRunData StretchView::getRunData() const { + auto const useQuickBayes = SettingsHelper::hasDevelopmentFlag("quickbayes"); + + auto const sampleName = m_uiForm.dsSample->getCurrentDataName().toStdString(); + auto const resName = m_uiForm.dsResolution->getCurrentDataName().toStdString(); + + auto const background = m_uiForm.cbBackground->currentText().toStdString(); + + auto const eMin = m_properties["EMin"]->valueText().toDouble(); + auto const eMax = m_properties["EMax"]->valueText().toDouble(); + auto const beta = m_properties["Beta"]->valueText().toInt(); + + auto const elasticPeak = m_uiForm.chkElasticPeak->isChecked(); + + auto const sigma = !useQuickBayes ? m_properties["Sigma"]->valueText().toInt() : 0; + auto const nBins = !useQuickBayes ? m_properties["SampleBinning"]->valueText().toInt() : 0; + auto const sequence = !useQuickBayes ? m_uiForm.chkSequentialFit->isChecked() : false; + + return StretchRunData(sampleName, resName, eMin, eMax, beta, elasticPeak, background, sigma, nBins, sequence); +} + +CurrentPreviewData StretchView::getCurrentPreviewData() const { + auto const sampleName = m_uiForm.dsSample->getCurrentDataName().toStdString(); + auto const hasSample = m_uiForm.ppPlot->hasCurve("Sample"); + + return CurrentPreviewData(sampleName, hasSample); +} + +std::string StretchView::getPlotType() const { return m_uiForm.cbPlot->currentText().toStdString(); } + +std::string StretchView::getPlotContour() const { return m_uiForm.cbPlotContour->currentText().toStdString(); } + +IRunView *StretchView::getRunWidget() const { return m_uiForm.runWidget; } + +void StretchView::resetPlotContourOptions(const std::vector &contourNames) { + m_uiForm.cbPlotContour->clear(); + for (auto const &name : contourNames) + m_uiForm.cbPlotContour->addItem(QString::fromStdString(name)); +} + +void StretchView::setPlotADSEnabled(bool enabled) { m_uiForm.ppPlot->watchADS(enabled); } + +void StretchView::setPlotResultEnabled(bool enabled) { + m_uiForm.pbPlot->setEnabled(enabled); + m_uiForm.cbPlot->setEnabled(enabled); +} + +void StretchView::setPlotContourEnabled(bool enabled) { + m_uiForm.pbPlotContour->setEnabled(enabled); + m_uiForm.cbPlotContour->setEnabled(enabled); +} + +void StretchView::setSaveResultEnabled(bool enabled) { m_uiForm.pbSave->setEnabled(enabled); } + +bool StretchView::displaySaveDirectoryMessage() { + char const *textMessage = "BayesStretch requires a default save directory and " + "one is not currently set." + " If run, the algorithm will default to saving files " + "to the current working directory." + " Would you still like to run the algorithm?"; + auto const response = QMessageBox::question(nullptr, tr("Save Directory"), tr(textMessage), QMessageBox::Yes, + QMessageBox::No, QMessageBox::NoButton); + return response == QMessageBox::No; +} + +void StretchView::setButtonsEnabled(bool enabled) { + setPlotResultEnabled(enabled); + setPlotContourEnabled(enabled); + setSaveResultEnabled(enabled); +} + +void StretchView::setPlotResultIsPlotting(bool plotting) { + m_uiForm.pbPlot->setText(plotting ? "Plotting..." : "Plot"); +} + +void StretchView::setPlotContourIsPlotting(bool plotting) { + m_uiForm.pbPlotContour->setText(plotting ? "Plotting..." : "Plot Contour"); +} + +} // namespace MantidQt::CustomInterfaces diff --git a/qt/scientific_interfaces/Inelastic/BayesFitting/StretchView.h b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchView.h new file mode 100644 index 000000000000..f40913bc45f8 --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/BayesFitting/StretchView.h @@ -0,0 +1,123 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include "DllConfig.h" +#include "MantidQtWidgets/Common/QtPropertyBrowser/DoubleEditorFactory.h" +#include "MantidQtWidgets/Common/QtPropertyBrowser/QtTreePropertyBrowser" +#include "MantidQtWidgets/Common/UserInputValidator.h" +#include "MantidQtWidgets/Spectroscopy/RunWidget/IRunSubscriber.h" +#include "StretchData.h" +#include "ui_Stretch.h" + +namespace MantidQt { +namespace CustomInterfaces { + +class MANTIDQT_INELASTIC_DLL IStretchViewSubscriber { +public: + virtual ~IStretchViewSubscriber() = default; + + virtual void notifySaveClicked() = 0; + virtual void notifyPlotClicked() = 0; + virtual void notifyPlotContourClicked() = 0; + virtual void notifyPlotCurrentPreviewClicked() = 0; + virtual void notifyPreviewSpecChanged(int specNum) = 0; +}; + +class MANTIDQT_INELASTIC_DLL IStretchView { +public: + virtual ~IStretchView() = default; + + virtual void subscribePresenter(IStretchViewSubscriber *presenter) = 0; + virtual void loadSettings(const QSettings &settings) = 0; + virtual void applySettings(std::map const &settings) = 0; + virtual void validateUserInput(IUserInputValidator *validator) const = 0; + + virtual StretchRunData getRunData() const = 0; + virtual CurrentPreviewData getCurrentPreviewData() const = 0; + virtual std::string getPlotType() const = 0; + virtual std::string getPlotContour() const = 0; + virtual IRunView *getRunWidget() const = 0; + + virtual void setupFitOptions() = 0; + virtual void setupPropertyBrowser() = 0; + virtual void setupPlotOptions() = 0; + + virtual void setFileExtensionsByName(bool filter) = 0; + virtual void setLoadHistory(bool doLoadHistory) = 0; + + virtual void resetPlotContourOptions(const std::vector &contourNames) = 0; + virtual bool displaySaveDirectoryMessage() = 0; + + virtual void setPlotADSEnabled(bool enabled) = 0; + virtual void setPlotResultEnabled(bool enabled) = 0; + virtual void setPlotContourEnabled(bool enabled) = 0; + virtual void setSaveResultEnabled(bool enabled) = 0; + virtual void setButtonsEnabled(bool enabled) = 0; + virtual void setPlotResultIsPlotting(bool plotting) = 0; + virtual void setPlotContourIsPlotting(bool plotting) = 0; +}; + +class MANTIDQT_INELASTIC_DLL StretchView : public QWidget, public IStretchView { + Q_OBJECT +public: + explicit StretchView(QWidget *parent = nullptr); + ~StretchView() override = default; + + void subscribePresenter(IStretchViewSubscriber *presenter) override; + void loadSettings(const QSettings &settings) override; + void applySettings(std::map const &settings) override; + void validateUserInput(IUserInputValidator *validator) const override; + + StretchRunData getRunData() const override; + CurrentPreviewData getCurrentPreviewData() const override; + std::string getPlotType() const override; + std::string getPlotContour() const override; + IRunView *getRunWidget() const override; + + void setupFitOptions() override; + void setupPropertyBrowser() override; + void setupPlotOptions() override; + + void setFileExtensionsByName(bool filter) override; + void setLoadHistory(bool doLoadHistory) override; + + void resetPlotContourOptions(const std::vector &contourNames) override; + bool displaySaveDirectoryMessage() override; + + void setPlotADSEnabled(bool enabled) override; + void setPlotResultEnabled(bool enabled) override; + void setPlotContourEnabled(bool enabled) override; + void setSaveResultEnabled(bool enabled) override; + void setButtonsEnabled(bool enabled) override; + void setPlotResultIsPlotting(bool plotting) override; + void setPlotContourIsPlotting(bool plotting) override; + +private slots: + void minValueChanged(double min); + void maxValueChanged(double max); + void propertiesUpdated(QtProperty *prop, double val); + void handleSampleInputReady(const QString &filename); + void previewSpecChanged(int value); + + void saveWorkspacesClicked(); + void plotWorkspacesClicked(); + void plotContourClicked(); + void plotCurrentPreviewClicked(); + +private: + void formatTreeWidget(QtTreePropertyBrowser *treeWidget, QMap const &properties) const; + + Ui::Stretch m_uiForm; + QtDoublePropertyManager *m_dblManager; + QMap m_properties; + IStretchViewSubscriber *m_presenter; + QtTreePropertyBrowser *m_propTree; + DoubleEditorFactory *m_dblEdFac; +}; +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Inelastic/test/BayesFitting/CMakeLists.txt b/qt/scientific_interfaces/Inelastic/test/BayesFitting/CMakeLists.txt new file mode 100644 index 000000000000..21d569a96895 --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/test/BayesFitting/CMakeLists.txt @@ -0,0 +1,18 @@ +get_filename_component(SUB_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" NAME) + +set(TEST_FILES StretchModelTest.h StretchPresenterTest.h) + +set(TEST_HELPERS MockObjects.h) + +list(TRANSFORM TEST_FILES PREPEND ${SUB_DIRECTORY}/) +list(TRANSFORM TEST_HELPERS PREPEND ${SUB_DIRECTORY}/) + +set(ALL_TEST_FILES + ${ALL_TEST_FILES} ${TEST_FILES} + PARENT_SCOPE +) + +set(ALL_TEST_HELPERS + ${ALL_TEST_HELPERS} ${TEST_HELPERS} + PARENT_SCOPE +) diff --git a/qt/scientific_interfaces/Inelastic/test/BayesFitting/MockObjects.h b/qt/scientific_interfaces/Inelastic/test/BayesFitting/MockObjects.h new file mode 100644 index 000000000000..33336a65489f --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/test/BayesFitting/MockObjects.h @@ -0,0 +1,71 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source +// & Institut Laue - Langevin +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include +#include + +#include "BayesFitting/StretchModel.h" +#include "BayesFitting/StretchView.h" + +#include "MantidKernel/WarningSuppressions.h" +#include "MantidQtWidgets/Common/IConfiguredAlgorithm.h" + +#include + +using namespace MantidQt; +using namespace MantidQt::CustomInterfaces; + +GNU_DIAG_OFF_SUGGEST_OVERRIDE + +class MockStretchView : public IStretchView { +public: + virtual ~MockStretchView() = default; + + MOCK_METHOD(void, subscribePresenter, (IStretchViewSubscriber * presenter), (override)); + MOCK_METHOD(void, loadSettings, (const QSettings &settings), (override)); + MOCK_METHOD(void, applySettings, ((std::map const &settings)), (override)); + MOCK_METHOD(void, validateUserInput, (IUserInputValidator * validator), (const, override)); + + MOCK_METHOD(StretchRunData, getRunData, (), (const, override)); + MOCK_METHOD(CurrentPreviewData, getCurrentPreviewData, (), (const, override)); + MOCK_METHOD(std::string, getPlotType, (), (const, override)); + MOCK_METHOD(std::string, getPlotContour, (), (const, override)); + MOCK_METHOD(IRunView *, getRunWidget, (), (const, override)); + + MOCK_METHOD(void, setupFitOptions, (), (override)); + MOCK_METHOD(void, setupPropertyBrowser, (), (override)); + MOCK_METHOD(void, setupPlotOptions, (), (override)); + + MOCK_METHOD(void, setFileExtensionsByName, (bool filter), (override)); + MOCK_METHOD(void, setLoadHistory, (bool doLoadHistory), (override)); + + MOCK_METHOD(void, resetPlotContourOptions, ((const std::vector &contourNames)), (override)); + MOCK_METHOD(bool, displaySaveDirectoryMessage, (), (override)); + + MOCK_METHOD(void, setPlotADSEnabled, (bool enabled), (override)); + MOCK_METHOD(void, setPlotResultEnabled, (bool enabled), (override)); + MOCK_METHOD(void, setPlotContourEnabled, (bool enabled), (override)); + MOCK_METHOD(void, setSaveResultEnabled, (bool enabled), (override)); + MOCK_METHOD(void, setButtonsEnabled, (bool enabled), (override)); + MOCK_METHOD(void, setPlotResultIsPlotting, (bool plotting), (override)); + MOCK_METHOD(void, setPlotContourIsPlotting, (bool plotting), (override)); +}; + +class MockStretchModel : public IStretchModel { +public: + virtual ~MockStretchModel() = default; + + MOCK_METHOD(MantidQt::API::IConfiguredAlgorithm_sptr, stretchAlgorithm, + ((const StretchRunData &algParams), const std::string &fitWorkspaceName, + const std::string &contourWorkspaceName, const bool useQuickBayes), + (const, override)); + + MOCK_METHOD(API::IConfiguredAlgorithm_sptr, setupSaveAlgorithm, (const std::string &wsName), (const, override)); +}; + +GNU_DIAG_ON_SUGGEST_OVERRIDE diff --git a/qt/scientific_interfaces/Inelastic/test/BayesFitting/StretchModelTest.h b/qt/scientific_interfaces/Inelastic/test/BayesFitting/StretchModelTest.h new file mode 100644 index 000000000000..5c7e421ce8c5 --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/test/BayesFitting/StretchModelTest.h @@ -0,0 +1,212 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include +#include + +#include + +#include "BayesFitting/StretchModel.h" +#include "MantidAPI/Algorithm.h" +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/FrameworkManager.h" +#include "MantidAPI/IAlgorithmRuntimeProps.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/TableRow.h" +#include "MantidAPI/WorkspaceGroup.h" +#include "MantidFrameworkTestHelpers/WorkspaceCreationHelper.h" +#include "MantidQtWidgets/Spectroscopy/SettingsWidget/SettingsHelper.h" + +using namespace Mantid::API; +using namespace MantidQt::CustomInterfaces; + +class BayesStretch : public Algorithm { +public: + const std::string name() const override { return "BayesStretch"; }; + int version() const override { return 1; }; + const std::string summary() const override { return "BayesStretch Mock algorithm"; }; + +private: + void init() override { + declareProperty("SampleWorkspace", ""); + declareProperty("ResolutionWorkspace", ""); + declareProperty("EMin", 0.0); + declareProperty("EMax", 0.0); + declareProperty("NumberBeta", 0); + declareProperty("Elastic", false); + declareProperty("OutputWorkspaceFit", ""); + declareProperty("OutputWorkspaceContour", ""); + declareProperty("Background", ""); + declareProperty("SampleBins", 0); + declareProperty("NumberSigma", 0); + declareProperty("Loop", false); + }; + + void exec() override { + ITableWorkspace_sptr outputWS = WorkspaceFactory::Instance().createTable(); + + outputWS->addColumn("str", "SampleWorkspace"); + outputWS->addColumn("str", "ResolutionWorkspace"); + outputWS->addColumn("double", "EMin"); + outputWS->addColumn("double", "EMax"); + outputWS->addColumn("int", "NumberBeta"); + outputWS->addColumn("bool", "Elastic"); + outputWS->addColumn("str", "OutputWorkspaceFit"); + outputWS->addColumn("str", "OutputWorkspaceContour"); + outputWS->addColumn("str", "Background"); + outputWS->addColumn("int", "SampleBins"); + outputWS->addColumn("int", "NumberSigma"); + outputWS->addColumn("bool", "Loop"); + + TableRow newRow = outputWS->appendRow(); + + auto sampleWS = getPropertyValue("SampleWorkspace"); + auto resolutionWS = getPropertyValue("ResolutionWorkspace"); + auto eMin = std::stod(getPropertyValue("EMin")); + auto eMax = std::stod(getPropertyValue("EMax")); + auto numBeta = std::stoi(getPropertyValue("NumberBeta")); + auto elastic = getPropertyValue("Elastic") == "1"; + auto outputFit = getPropertyValue("OutputWorkspaceFit"); + auto outputContour = getPropertyValue("OutputWorkspaceContour"); + auto background = getPropertyValue("Background"); + auto sampleBins = std::stoi(getPropertyValue("SampleBins")); + auto numSigma = std::stoi(getPropertyValue("NumberSigma")); + auto loop = getPropertyValue("Loop") == "1"; + + newRow << sampleWS << resolutionWS << eMin << eMax << numBeta << elastic << outputFit << outputContour << background + << sampleBins << numSigma << loop; + + Mantid::API::AnalysisDataService::Instance().addOrReplace("outputWS", outputWS); + }; +}; + +class BayesStretch2 : public Algorithm { +public: + const std::string name() const override { return "BayesStretch2"; }; + int version() const override { return 1; }; + const std::string summary() const override { return "BayesStretch2 Mock algorithm"; }; + +private: + void init() override { + declareProperty("SampleWorkspace", ""); + declareProperty("ResolutionWorkspace", ""); + declareProperty("EMin", 0.0); + declareProperty("EMax", 0.0); + declareProperty("NumberBeta", 0); + declareProperty("Elastic", false); + declareProperty("OutputWorkspaceFit", ""); + declareProperty("OutputWorkspaceContour", ""); + declareProperty("Background", ""); + }; + + void exec() override { + ITableWorkspace_sptr outputWS = WorkspaceFactory::Instance().createTable(); + + outputWS->addColumn("str", "SampleWorkspace"); + outputWS->addColumn("str", "ResolutionWorkspace"); + outputWS->addColumn("double", "EMin"); + outputWS->addColumn("double", "EMax"); + outputWS->addColumn("int", "NumberBeta"); + outputWS->addColumn("bool", "Elastic"); + outputWS->addColumn("str", "OutputWorkspaceFit"); + outputWS->addColumn("str", "OutputWorkspaceContour"); + outputWS->addColumn("str", "Background"); + + TableRow newRow = outputWS->appendRow(); + + auto sampleWS = getPropertyValue("SampleWorkspace"); + auto resolutionWS = getPropertyValue("ResolutionWorkspace"); + auto eMin = std::stod(getPropertyValue("EMin")); + auto eMax = std::stod(getPropertyValue("EMax")); + auto numBeta = std::stoi(getPropertyValue("NumberBeta")); + auto elastic = getPropertyValue("Elastic") == "1"; + auto outputFit = getPropertyValue("OutputWorkspaceFit"); + auto outputContour = getPropertyValue("OutputWorkspaceContour"); + auto background = getPropertyValue("Background"); + + newRow << sampleWS << resolutionWS << eMin << eMax << numBeta << elastic << outputFit << outputContour + << background; + + Mantid::API::AnalysisDataService::Instance().addOrReplace("outputWS", outputWS); + }; +}; + +DECLARE_ALGORITHM(BayesStretch) +DECLARE_ALGORITHM(BayesStretch2) + +class StretchModelTest : public CxxTest::TestSuite { +public: + static StretchModelTest *createSuite() { return new StretchModelTest(); } + static void destroySuite(StretchModelTest *suite) { delete suite; } + + void setUp() override { + m_model = std::make_unique(); + Mantid::API::FrameworkManager::Instance(); + } + + void test_stretchAlgorithm_creates_BayesStretch_by_default() { + StretchRunData params("sample_ws", "res_ws", -0.5, 0.5, 50, true, "flat", 30, 1, true); + + auto configuredAlgorithm = m_model->stretchAlgorithm(params, "fit_ws", "contour_ws"); + + TS_ASSERT_EQUALS("BayesStretch", configuredAlgorithm->algorithm()->name()); + + auto &properties = configuredAlgorithm->getAlgorithmRuntimeProps(); + + TS_ASSERT_EQUALS("sample_ws", properties.getPropertyValue("SampleWorkspace")); + TS_ASSERT_EQUALS("res_ws", properties.getPropertyValue("ResolutionWorkspace")); + TS_ASSERT_EQUALS("-0.5", properties.getPropertyValue("EMin")); + TS_ASSERT_EQUALS("0.5", properties.getPropertyValue("EMax")); + TS_ASSERT_EQUALS("50", properties.getPropertyValue("NumberBeta")); + TS_ASSERT_EQUALS("30", properties.getPropertyValue("NumberSigma")); + TS_ASSERT_EQUALS("1", properties.getPropertyValue("Elastic")); + TS_ASSERT_EQUALS("fit_ws", properties.getPropertyValue("OutputWorkspaceFit")); + TS_ASSERT_EQUALS("contour_ws", properties.getPropertyValue("OutputWorkspaceContour")); + TS_ASSERT_EQUALS("flat", properties.getPropertyValue("Background")); + TS_ASSERT_EQUALS("1", properties.getPropertyValue("SampleBins")); + TS_ASSERT_EQUALS("1", properties.getPropertyValue("Loop")); + } + + void test_stretchAlgorithm_creates_BayesStretch2_when_quickbayes_enabled() { + StretchRunData params("sample_ws", "res_ws", -0.5, 0.5, 50, true, "flat", 30, 1, true); + + auto configuredAlgorithm = m_model->stretchAlgorithm(params, "fit_ws", "contour_ws", true); + + TS_ASSERT_EQUALS("BayesStretch2", configuredAlgorithm->algorithm()->name()); + + auto &properties = configuredAlgorithm->getAlgorithmRuntimeProps(); + + TS_ASSERT_EQUALS("sample_ws", properties.getPropertyValue("SampleWorkspace")); + TS_ASSERT_EQUALS("res_ws", properties.getPropertyValue("ResolutionWorkspace")); + TS_ASSERT_EQUALS("-0.5", properties.getPropertyValue("EMin")); + TS_ASSERT_EQUALS("0.5", properties.getPropertyValue("EMax")); + TS_ASSERT_EQUALS("50", properties.getPropertyValue("NumberBeta")); + TS_ASSERT_EQUALS("1", properties.getPropertyValue("Elastic")); + TS_ASSERT_EQUALS("fit_ws", properties.getPropertyValue("OutputWorkspaceFit")); + TS_ASSERT_EQUALS("contour_ws", properties.getPropertyValue("OutputWorkspaceContour")); + TS_ASSERT_EQUALS("flat", properties.getPropertyValue("Background")); + + TS_ASSERT(!properties.existsProperty("SampleBins")); + TS_ASSERT(!properties.existsProperty("NumberSigma")); + TS_ASSERT(!properties.existsProperty("Loop")); + } + + void test_setupSaveAlgorithm_creates_correct_save_algorithm() { + const std::string wsName = "test_workspace"; + auto configuredAlgorithm = m_model->setupSaveAlgorithm(wsName); + + auto &properties = configuredAlgorithm->getAlgorithmRuntimeProps(); + + TS_ASSERT_EQUALS("SaveNexusProcessed", configuredAlgorithm->algorithm()->name()); + TS_ASSERT_EQUALS("test_workspace.nxs", properties.getPropertyValue("Filename")); + TS_ASSERT_EQUALS("test_workspace", properties.getPropertyValue("InputWorkspace")); + } + +private: + std::unique_ptr m_model; +}; diff --git a/qt/scientific_interfaces/Inelastic/test/BayesFitting/StretchPresenterTest.h b/qt/scientific_interfaces/Inelastic/test/BayesFitting/StretchPresenterTest.h new file mode 100644 index 000000000000..58c59f0dadcf --- /dev/null +++ b/qt/scientific_interfaces/Inelastic/test/BayesFitting/StretchPresenterTest.h @@ -0,0 +1,150 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2024 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include +#include + +#include "BayesFitting/StretchPresenter.h" +#include "MantidFrameworkTestHelpers/WorkspaceCreationHelper.h" +#include "MantidQtWidgets/Common/MockAlgorithmRunner.h" +#include "MantidQtWidgets/Spectroscopy/MockObjects.h" +#include "MockObjects.h" + +#include + +using namespace MantidQt::CustomInterfaces; +using namespace testing; + +namespace { +auto &ads = Mantid::API::AnalysisDataService::Instance(); +} + +class StretchPresenterTest : public CxxTest::TestSuite { +public: + static StretchPresenterTest *createSuite() { return new StretchPresenterTest(); } + static void destroySuite(StretchPresenterTest *suite) { delete suite; } + + void setUp() override { + auto algorithmRunner = std::make_unique>(); + m_algorithmRunner = algorithmRunner.get(); + auto model = std::make_unique>(); + m_model = model.get(); + m_view = std::make_unique>(); + m_runView = std::make_unique>(); + + ON_CALL(*m_view, getRunWidget()).WillByDefault(Return(m_runView.get())); + m_presenter = + std::make_unique(nullptr, m_view.get(), std::move(model), std::move(algorithmRunner)); + + m_workspace = WorkspaceCreationHelper::create2DWorkspace(5, 4); + } + + void tearDown() override { + TS_ASSERT(Mock::VerifyAndClearExpectations(&m_view)); + TS_ASSERT(Mock::VerifyAndClearExpectations(&m_model)); + TS_ASSERT(Mock::VerifyAndClearExpectations(&m_algorithmRunner)); + + m_presenter.reset(); + m_view.reset(); + ads.clear(); + } + + void test_handleValidation_invalid_input() { + EXPECT_CALL(*m_view, validateUserInput(_)).WillOnce(Invoke([](IUserInputValidator *validator) { + validator->addErrorMessage("Invalid input"); + })); + + auto validator = std::make_unique(); + m_presenter->handleValidation(validator.get()); + + TS_ASSERT(!validator->isAllInputValid()); + } + + void test_handleValidation_valid_input() { + EXPECT_CALL(*m_view, validateUserInput(_)).WillOnce(Invoke([](IUserInputValidator *validator) { + (void)validator; + })); + + auto validator = std::make_unique(); + m_presenter->handleValidation(validator.get()); + + TS_ASSERT(validator->isAllInputValid()); + } + + void test_handleRun_with_Empty_savedir_and_user_rejects_prompt() { + auto &configSvc = Mantid::Kernel::ConfigService::Instance(); + configSvc.setString("defaultsave.directory", ""); + ON_CALL(*m_view, displaySaveDirectoryMessage()).WillByDefault(Return(true)); + + EXPECT_CALL(*m_view, displaySaveDirectoryMessage()).Times(1); + + m_presenter->handleRun(); + } + + void test_handleRun_with_Empty_savedir_and_user_enter_savedir() { + auto &configSvc = Mantid::Kernel::ConfigService::Instance(); + configSvc.setString("defaultsave.directory", "/test/test"); + ON_CALL(*m_view, displaySaveDirectoryMessage()).WillByDefault(Return(false)); + + StretchRunData runData("sample_ws", "res_ws", -0.5, 0.5, 50, true, "flat", 30, 1, true); + + ON_CALL(*m_view, getRunData()).WillByDefault(Return(runData)); + + EXPECT_CALL(*m_view, setPlotADSEnabled(false)).Times(1); + + EXPECT_CALL(*m_model, stretchAlgorithm(_, _, _, _)).Times(1); + + m_presenter->handleRun(); + } + + void test_handleRun_with_valid_input_and_savedir() { + StretchRunData runData("sample_ws", "res_ws", -0.5, 0.5, 50, true, "flat", 30, 1, true); + + ON_CALL(*m_view, getRunData()).WillByDefault(Return(runData)); + EXPECT_CALL(*m_view, setPlotADSEnabled(false)).Times(1); + + EXPECT_CALL(*m_model, stretchAlgorithm(_, _, _, _)).Times(1); + + m_presenter->handleRun(); + } + + void test_notifySaveClicked_with_output_workspaces() { + StretchRunData runData("sample_ws", "res_ws", -0.5, 0.5, 50, true, "flat", 30, 1, true); + + auto const cutIndex = runData.sampleName.find_last_of("_"); + auto const baseName = runData.sampleName.substr(0, cutIndex); + auto fitWorkspaceName = baseName + "_Stretch_Fit"; + auto contourWorkspaceName = baseName + "_Stretch_Contour"; + + ads.addOrReplace(fitWorkspaceName, m_workspace); + ads.addOrReplace(contourWorkspaceName, m_workspace); + + ON_CALL(*m_view, getRunData()).WillByDefault(Return(runData)); + EXPECT_CALL(*m_view, setPlotADSEnabled(false)).Times(1); + + EXPECT_CALL(*m_model, stretchAlgorithm(_, _, _, _)).Times(1); + + m_presenter->handleRun(); + + EXPECT_CALL(*m_model, setupSaveAlgorithm(_)).Times(2); + + EXPECT_CALL(*m_algorithmRunner, + execute(Matcher>(SizeIs(Eq(2))))) + .Times(1); + + m_presenter->notifySaveClicked(); + } + +private: + NiceMock *m_algorithmRunner; + NiceMock *m_model; + std::unique_ptr> m_runView; + std::unique_ptr> m_view; + std::unique_ptr m_presenter; + Mantid::API::MatrixWorkspace_sptr m_workspace; +}; diff --git a/qt/scientific_interfaces/Inelastic/test/CMakeLists.txt b/qt/scientific_interfaces/Inelastic/test/CMakeLists.txt index 159404a29ab1..a43d40555734 100644 --- a/qt/scientific_interfaces/Inelastic/test/CMakeLists.txt +++ b/qt/scientific_interfaces/Inelastic/test/CMakeLists.txt @@ -5,6 +5,7 @@ endif() set(ALL_TEST_FILES) set(ALL_TEST_HELPERS) +add_subdirectory(BayesFitting) add_subdirectory(Processor) add_subdirectory(QENSFitting) diff --git a/qt/widgets/spectroscopy/inc/MantidQtWidgets/Spectroscopy/InterfaceUtils.h b/qt/widgets/spectroscopy/inc/MantidQtWidgets/Spectroscopy/InterfaceUtils.h index b4ef43c54354..f8dde105f584 100644 --- a/qt/widgets/spectroscopy/inc/MantidQtWidgets/Spectroscopy/InterfaceUtils.h +++ b/qt/widgets/spectroscopy/inc/MantidQtWidgets/Spectroscopy/InterfaceUtils.h @@ -67,6 +67,9 @@ MANTID_SPECTROSCOPY_DLL void setRangeSelectorMax(QtDoublePropertyManager *dblPro QtProperty *maxProperty, MantidWidgets::RangeSelector *rangeSelector, double newValue); +MANTID_SPECTROSCOPY_DLL bool checkADSForPlotSaveWorkspace(const std::string &workspaceName, const bool plotting, + const bool warn = true); + } // namespace InterfaceUtils } // namespace CustomInterfaces } // namespace MantidQt diff --git a/qt/widgets/spectroscopy/src/InterfaceUtils.cpp b/qt/widgets/spectroscopy/src/InterfaceUtils.cpp index 7b396ddab1b2..b6f24dd8bcc6 100644 --- a/qt/widgets/spectroscopy/src/InterfaceUtils.cpp +++ b/qt/widgets/spectroscopy/src/InterfaceUtils.cpp @@ -5,6 +5,7 @@ // Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS // SPDX - License - Identifier: GPL - 3.0 + #include "MantidQtWidgets/Spectroscopy/InterfaceUtils.h" +#include "MantidAPI/AnalysisDataService.h" #include "MantidKernel/Logger.h" #include "MantidQtWidgets/Common/ParseKeyValueString.h" #include "MantidQtWidgets/Spectroscopy/SettingsWidget/SettingsHelper.h" @@ -229,6 +230,18 @@ void setRangeSelectorMax(QtDoublePropertyManager *dblPropertyManager, QtProperty dblPropertyManager->setValue(maxProperty, rangeSelector->getMaximum()); } +bool checkADSForPlotSaveWorkspace(const std::string &workspaceName, const bool plotting, const bool warn) { + const auto workspaceExists = Mantid::API::AnalysisDataService::Instance().doesExist(workspaceName); + if (warn && !workspaceExists) { + const std::string plotSave = plotting ? "plotting" : "saving"; + const auto errorMessage = + "Error while " + plotSave + ":\nThe workspace \"" + workspaceName + "\" could not be found."; + const char *textMessage = errorMessage.c_str(); + QMessageBox::warning(nullptr, QObject::tr("Indirect "), QObject::tr(textMessage)); + } + return workspaceExists; +} + } // namespace InterfaceUtils } // namespace CustomInterfaces } // namespace MantidQt