diff --git a/Framework/API/inc/MantidAPI/AlgorithmFactory.h b/Framework/API/inc/MantidAPI/AlgorithmFactory.h index 49ed56890b29e7b6a01f96d2c3e1c0fb72104c09..2f1a57cb835a14005e87553a23757f13567948b0 100644 --- a/Framework/API/inc/MantidAPI/AlgorithmFactory.h +++ b/Framework/API/inc/MantidAPI/AlgorithmFactory.h @@ -17,10 +17,10 @@ namespace API { /// Structure uniquely describing an algorithm with its name, category and /// version. struct AlgorithmDescriptor { - std::string name; ///< name - std::string alias; ///< alias - std::string category; ///< category + std::string name; ///< Algorithm Name int version; ///< version + std::string category; ///< category + std::string alias; ///< alias }; //---------------------------------------------------------------------- diff --git a/Framework/API/inc/MantidAPI/ScriptBuilder.h b/Framework/API/inc/MantidAPI/ScriptBuilder.h index 3331ccde3b57058ea62556caa5fd82c7cae018af..fb8e69f5b74e2f801a92dba0c80ee65cbe3b5c14 100644 --- a/Framework/API/inc/MantidAPI/ScriptBuilder.h +++ b/Framework/API/inc/MantidAPI/ScriptBuilder.h @@ -60,11 +60,10 @@ private: void buildChildren(std::ostringstream &os, std::vector<HistoryItem>::const_iterator &iter, int depth = 1); - const std::string buildCommentString(AlgorithmHistory_const_sptr algHistory); + const std::string buildCommentString(const AlgorithmHistory &algHistory); + const std::string buildAlgorithmString(const AlgorithmHistory &algHistory); const std::string - buildAlgorithmString(AlgorithmHistory_const_sptr algHistory); - const std::string - buildPropertyString(Mantid::Kernel::PropertyHistory_const_sptr propHistory); + buildPropertyString(const Mantid::Kernel::PropertyHistory &propHistory); const std::vector<HistoryItem> m_historyItems; std::string m_output; diff --git a/Framework/API/src/NotebookBuilder.cpp b/Framework/API/src/NotebookBuilder.cpp index f256baf095e04fdedbfadf2ef1c45e665b9f5159..63010c099143e760b1bdb564b6cc862ed3c6847f 100644 --- a/Framework/API/src/NotebookBuilder.cpp +++ b/Framework/API/src/NotebookBuilder.cpp @@ -122,21 +122,16 @@ NotebookBuilder::buildAlgorithmString(AlgorithmHistory_const_sptr algHistory) { properties << "Version=" << algHistory->version() << ", "; } else if (m_versionSpecificity == "old") { //...or only specify algorithm versions when they're not the newest version - bool oldVersion = false; - - std::vector<AlgorithmDescriptor> descriptors = - AlgorithmFactory::Instance().getDescriptors(); - for (auto &descriptor : descriptors) { - // If a newer version of this algorithm exists, then this must be an old - // version. - if (descriptor.name == algHistory->name() && - descriptor.version > algHistory->version()) { - oldVersion = true; - break; - } - } + const auto &algName = algHistory->name(); + auto &algFactory = API::AlgorithmFactory::Instance(); + int latestVersion = 0; - if (oldVersion) { + if (algFactory.exists(algName)) { // Check the alg still exists in Mantid + latestVersion = AlgorithmFactory::Instance().highestVersion(algName); + } + // If a newer version of this algorithm exists, then this must be an old + // version. + if (latestVersion > algHistory->version()) { properties << "Version=" << algHistory->version() << ", "; } } diff --git a/Framework/API/src/ScriptBuilder.cpp b/Framework/API/src/ScriptBuilder.cpp index 1d0197629f192ff04d359363608522d30ba8e13f..d852516ec45cb830b9e4041fa03b3ded94021dad 100644 --- a/Framework/API/src/ScriptBuilder.cpp +++ b/Framework/API/src/ScriptBuilder.cpp @@ -85,7 +85,7 @@ void ScriptBuilder::writeHistoryToStream( } } else { // create the string for this algorithm - os << buildAlgorithmString(algHistory); + os << buildAlgorithmString(*algHistory); if (m_timestampCommands) { os << " # " << algHistory->executionDate().toISO8601String(); } @@ -125,11 +125,11 @@ void ScriptBuilder::buildChildren( * @returns std::string to run this algorithm */ const std::string -ScriptBuilder::buildCommentString(AlgorithmHistory_const_sptr algHistory) { +ScriptBuilder::buildCommentString(const AlgorithmHistory &algHistory) { std::ostringstream comment; - const std::string name = algHistory->name(); + const std::string name = algHistory.name(); if (name == COMMENT_ALG) { - auto props = algHistory->getProperties(); + auto props = algHistory.getProperties(); for (auto &prop : props) { if (prop->name() == "Note") { comment << "# " << prop->value(); @@ -146,20 +146,20 @@ ScriptBuilder::buildCommentString(AlgorithmHistory_const_sptr algHistory) { * @returns std::string to run this algorithm */ const std::string -ScriptBuilder::buildAlgorithmString(AlgorithmHistory_const_sptr algHistory) { +ScriptBuilder::buildAlgorithmString(const AlgorithmHistory &algHistory) { std::ostringstream properties; - const std::string name = algHistory->name(); + const std::string name = algHistory.name(); std::string prop; if (name == COMMENT_ALG) return buildCommentString(algHistory); - auto props = algHistory->getProperties(); + auto props = algHistory.getProperties(); try { // create a fresh version of the algorithm - unmanaged - IAlgorithm_sptr algFresh = AlgorithmManager::Instance().createUnmanaged( - name, algHistory->version()); + auto algFresh = AlgorithmManager::Instance().createUnmanaged( + name, algHistory.version()); algFresh->initialize(); const auto &propsFresh = algFresh->getProperties(); @@ -182,11 +182,11 @@ ScriptBuilder::buildAlgorithmString(AlgorithmHistory_const_sptr algHistory) { } catch (std::exception &) { g_log.error() << "Could not create a fresh version of " << name - << " version " << algHistory->version() << "\n"; + << " version " << algHistory.version() << "\n"; } for (auto &propIter : props) { - prop = buildPropertyString(propIter); + prop = buildPropertyString(*propIter); if (prop.length() > 0) { properties << prop << ", "; } @@ -194,25 +194,20 @@ ScriptBuilder::buildAlgorithmString(AlgorithmHistory_const_sptr algHistory) { // Three cases, we can either specify the version of every algorithm... if (m_versionSpecificity == "all") { - properties << "Version=" << algHistory->version() << ", "; + properties << "Version=" << algHistory.version() << ", "; } else if (m_versionSpecificity == "old") { //...or only specify algorithm versions when they're not the newest version - bool oldVersion = false; - - std::vector<AlgorithmDescriptor> descriptors = - AlgorithmFactory::Instance().getDescriptors(); - for (auto &descriptor : descriptors) { - // If a newer version of this algorithm exists, then this must be an old - // version. - if (descriptor.name == algHistory->name() && - descriptor.version > algHistory->version()) { - oldVersion = true; - break; - } - } + const auto &algName = algHistory.name(); + auto &algFactory = API::AlgorithmFactory::Instance(); + int latestVersion = 0; - if (oldVersion) { - properties << "Version=" << algHistory->version() << ", "; + if (algFactory.exists(algName)) { // Check the alg still exists in Mantid + latestVersion = AlgorithmFactory::Instance().highestVersion(algName); + } + // If a newer version of this algorithm exists, then this must be an old + // version. + if (latestVersion > algHistory.version()) { + properties << "Version=" << algHistory.version() << ", "; } } // Third case is we never specify the version, so do nothing. @@ -233,8 +228,8 @@ ScriptBuilder::buildAlgorithmString(AlgorithmHistory_const_sptr algHistory) { * @param propHistory :: reference to a property history object * @returns std::string for this property */ -const std::string -ScriptBuilder::buildPropertyString(PropertyHistory_const_sptr propHistory) { +const std::string ScriptBuilder::buildPropertyString( + const Mantid::Kernel::PropertyHistory &propHistory) { using Mantid::Kernel::Direction; // Create a vector of all non workspace property type names @@ -242,28 +237,28 @@ ScriptBuilder::buildPropertyString(PropertyHistory_const_sptr propHistory) { std::string prop; // No need to specify value for default properties - if (!propHistory->isDefault()) { + if (!propHistory.isDefault()) { // Do not give values to output properties other than workspace properties if (find(nonWorkspaceTypes.begin(), nonWorkspaceTypes.end(), - propHistory->type()) != nonWorkspaceTypes.end() && - propHistory->direction() == Direction::Output) { - g_log.debug() << "Ignoring property " << propHistory->name() - << " of type " << propHistory->type() << '\n'; + propHistory.type()) != nonWorkspaceTypes.end() && + propHistory.direction() == Direction::Output) { + g_log.debug() << "Ignoring property " << propHistory.name() << " of type " + << propHistory.type() << '\n'; // Handle numerical properties - } else if (propHistory->type() == "number") { - prop = propHistory->name() + "=" + propHistory->value(); + } else if (propHistory.type() == "number") { + prop = propHistory.name() + "=" + propHistory.value(); // Handle boolean properties - } else if (propHistory->type() == "boolean") { - std::string value = (propHistory->value() == "1" ? "True" : "False"); - prop = propHistory->name() + "=" + value; + } else if (propHistory.type() == "boolean") { + std::string value = (propHistory.value() == "1" ? "True" : "False"); + prop = propHistory.name() + "=" + value; // Handle all other property types } else { std::string opener = "='"; - if (propHistory->value().find('\\') != std::string::npos) { + if (propHistory.value().find('\\') != std::string::npos) { opener = "=r'"; } - prop = propHistory->name() + opener + propHistory->value() + "'"; + prop = propHistory.name() + opener + propHistory.value() + "'"; } } diff --git a/Framework/API/test/AlgorithmFactoryTest.h b/Framework/API/test/AlgorithmFactoryTest.h index caa9d828ccafbc8860c244405556e1368835a7b2..a6d7ac51f7c8664036a8b557273b1bdb31a5c015 100644 --- a/Framework/API/test/AlgorithmFactoryTest.h +++ b/Framework/API/test/AlgorithmFactoryTest.h @@ -23,56 +23,58 @@ public: Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm> *newTwo = new Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm>; + auto &algFactory = AlgorithmFactory::Instance(); + + // Ensure the algorithm factory does not already have this + algFactory.unsubscribe("ToyAlgorithm", 1); + algFactory.unsubscribe("ToyAlgorithm", 2); + // get the number of algorithms it already has - std::vector<std::string> keys = AlgorithmFactory::Instance().getKeys(); + std::vector<std::string> keys = algFactory.getKeys(); size_t noOfAlgs = keys.size(); - TS_ASSERT_THROWS_NOTHING( - AlgorithmFactory::Instance().subscribe<ToyAlgorithm>()); - TS_ASSERT_THROWS_NOTHING(AlgorithmFactory::Instance().subscribe(newTwo)); + TS_ASSERT_THROWS_NOTHING(algFactory.subscribe<ToyAlgorithm>()); + TS_ASSERT_THROWS_NOTHING(algFactory.subscribe(newTwo)); - TS_ASSERT_THROWS_ANYTHING( - AlgorithmFactory::Instance().subscribe<ToyAlgorithm>()); + TS_ASSERT_THROWS_ANYTHING(algFactory.subscribe<ToyAlgorithm>()); // get the number of algorithms it has now - keys = AlgorithmFactory::Instance().getKeys(); + keys = algFactory.getKeys(); size_t noOfAlgsAfter = keys.size(); TS_ASSERT_EQUALS(noOfAlgsAfter, noOfAlgs + 2); - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 1); - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 2); + algFactory.unsubscribe("ToyAlgorithm", 1); + algFactory.unsubscribe("ToyAlgorithm", 2); } void testUnsubscribe() { Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm> *newTwo = new Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm>; + auto &algFactory = AlgorithmFactory::Instance(); + // get the nubmer of algorithms it already has - std::vector<std::string> keys = AlgorithmFactory::Instance().getKeys(); + std::vector<std::string> keys = algFactory.getKeys(); size_t noOfAlgs = keys.size(); - AlgorithmFactory::Instance().subscribe<ToyAlgorithm>(); - AlgorithmFactory::Instance().subscribe(newTwo); + algFactory.subscribe<ToyAlgorithm>(); + algFactory.subscribe(newTwo); - TS_ASSERT_THROWS_NOTHING( - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 1)); - TS_ASSERT_THROWS_NOTHING( - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 2)); + TS_ASSERT_THROWS_NOTHING(algFactory.unsubscribe("ToyAlgorithm", 1)); + TS_ASSERT_THROWS_NOTHING(algFactory.unsubscribe("ToyAlgorithm", 2)); // get the nubmer of algorithms it has now - keys = AlgorithmFactory::Instance().getKeys(); + keys = algFactory.getKeys(); size_t noOfAlgsAfter = keys.size(); TS_ASSERT_EQUALS(noOfAlgsAfter, noOfAlgs) // try unsubscribing them again - TS_ASSERT_THROWS_NOTHING( - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 1)); - TS_ASSERT_THROWS_NOTHING( - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 2)); + TS_ASSERT_THROWS_NOTHING(algFactory.unsubscribe("ToyAlgorithm", 1)); + TS_ASSERT_THROWS_NOTHING(algFactory.unsubscribe("ToyAlgorithm", 2)); // make sure the number hasn't changed - keys = AlgorithmFactory::Instance().getKeys(); + keys = algFactory.getKeys(); size_t noOfAlgsAgain = keys.size(); TS_ASSERT_EQUALS(noOfAlgsAfter, noOfAlgsAgain); @@ -82,90 +84,89 @@ public: Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm> *newTwo = new Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm>; - AlgorithmFactory::Instance().subscribe<ToyAlgorithm>(); - AlgorithmFactory::Instance().subscribe(newTwo); + auto &algFactory = AlgorithmFactory::Instance(); - TS_ASSERT(AlgorithmFactory::Instance().exists("ToyAlgorithm", 1)); - TS_ASSERT(AlgorithmFactory::Instance().exists("ToyAlgorithm", 2)); - TS_ASSERT(!AlgorithmFactory::Instance().exists("ToyAlgorithm", 3)); - TS_ASSERT(!AlgorithmFactory::Instance().exists("ToyAlgorithm", 4)); - TS_ASSERT(AlgorithmFactory::Instance().exists("ToyAlgorithm", -1)); + algFactory.subscribe<ToyAlgorithm>(); + algFactory.subscribe(newTwo); - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 1); - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 2); + TS_ASSERT(algFactory.exists("ToyAlgorithm", 1)); + TS_ASSERT(algFactory.exists("ToyAlgorithm", 2)); + TS_ASSERT(!algFactory.exists("ToyAlgorithm", 3)); + TS_ASSERT(!algFactory.exists("ToyAlgorithm", 4)); + TS_ASSERT(algFactory.exists("ToyAlgorithm", -1)); + + algFactory.unsubscribe("ToyAlgorithm", 1); + algFactory.unsubscribe("ToyAlgorithm", 2); } void testGetKeys() { std::vector<std::string> keys; + auto &algFactory = AlgorithmFactory::Instance(); + TS_ASSERT_EQUALS(0, keys.size()); - AlgorithmFactory::Instance().subscribe<ToyAlgorithm>(); + algFactory.subscribe<ToyAlgorithm>(); - TS_ASSERT_THROWS_NOTHING(keys = AlgorithmFactory::Instance().getKeys()); + TS_ASSERT_THROWS_NOTHING(keys = algFactory.getKeys()); size_t noOfAlgs = keys.size(); - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 1); + algFactory.unsubscribe("ToyAlgorithm", 1); - TS_ASSERT_THROWS_NOTHING(keys = AlgorithmFactory::Instance().getKeys()); + TS_ASSERT_THROWS_NOTHING(keys = algFactory.getKeys()); TS_ASSERT_EQUALS(noOfAlgs - 1, keys.size()); } void test_HighestVersion() { - auto &factory = AlgorithmFactory::Instance(); + auto &algFactory = AlgorithmFactory::Instance(); - TS_ASSERT_THROWS(factory.highestVersion("ToyAlgorithm"), + TS_ASSERT_THROWS(algFactory.highestVersion("ToyAlgorithm"), std::invalid_argument); - AlgorithmFactory::Instance().subscribe<ToyAlgorithm>(); - TS_ASSERT_EQUALS(1, factory.highestVersion("ToyAlgorithm")); + algFactory.subscribe<ToyAlgorithm>(); + TS_ASSERT_EQUALS(1, algFactory.highestVersion("ToyAlgorithm")); Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm> *newTwo = new Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm>; - AlgorithmFactory::Instance().subscribe(newTwo); - TS_ASSERT_EQUALS(2, factory.highestVersion("ToyAlgorithm")); + algFactory.subscribe(newTwo); + TS_ASSERT_EQUALS(2, algFactory.highestVersion("ToyAlgorithm")); - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 1); - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 2); + algFactory.unsubscribe("ToyAlgorithm", 1); + algFactory.unsubscribe("ToyAlgorithm", 2); } void testCreate() { Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm> *newTwo = new Mantid::Kernel::Instantiator<ToyAlgorithmTwo, Algorithm>; - AlgorithmFactory::Instance().subscribe<ToyAlgorithm>(); - AlgorithmFactory::Instance().subscribe(newTwo); - - TS_ASSERT_THROWS_NOTHING( - AlgorithmFactory::Instance().create("ToyAlgorithm", -1)); - TS_ASSERT_THROWS_ANYTHING( - AlgorithmFactory::Instance().create("AlgorithmDoesntExist", -1)); - - TS_ASSERT_THROWS_NOTHING( - AlgorithmFactory::Instance().create("ToyAlgorithm", 1)); - TS_ASSERT_THROWS_NOTHING( - AlgorithmFactory::Instance().create("ToyAlgorithm", 2)); - TS_ASSERT_THROWS_ANYTHING( - AlgorithmFactory::Instance().create("AlgorithmDoesntExist", 1)); - TS_ASSERT_THROWS_ANYTHING( - AlgorithmFactory::Instance().create("AlgorithmDoesntExist", 2)); - - TS_ASSERT_THROWS_ANYTHING(AlgorithmFactory::Instance().create("", 1)); - TS_ASSERT_THROWS_ANYTHING(AlgorithmFactory::Instance().create("", -1)); - - TS_ASSERT_THROWS_ANYTHING( - AlgorithmFactory::Instance().create("ToyAlgorithm", 3)); - TS_ASSERT_THROWS_ANYTHING( - AlgorithmFactory::Instance().create("ToyAlgorithm", 4)); - - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 1); - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 2); + auto &algFactory = AlgorithmFactory::Instance(); + + algFactory.subscribe<ToyAlgorithm>(); + algFactory.subscribe(newTwo); + + TS_ASSERT_THROWS_NOTHING(algFactory.create("ToyAlgorithm", -1)); + TS_ASSERT_THROWS_ANYTHING(algFactory.create("AlgorithmDoesntExist", -1)); + + TS_ASSERT_THROWS_NOTHING(algFactory.create("ToyAlgorithm", 1)); + TS_ASSERT_THROWS_NOTHING(algFactory.create("ToyAlgorithm", 2)); + TS_ASSERT_THROWS_ANYTHING(algFactory.create("AlgorithmDoesntExist", 1)); + TS_ASSERT_THROWS_ANYTHING(algFactory.create("AlgorithmDoesntExist", 2)); + + TS_ASSERT_THROWS_ANYTHING(algFactory.create("", 1)); + TS_ASSERT_THROWS_ANYTHING(algFactory.create("", -1)); + + TS_ASSERT_THROWS_ANYTHING(algFactory.create("ToyAlgorithm", 3)); + TS_ASSERT_THROWS_ANYTHING(algFactory.create("ToyAlgorithm", 4)); + + algFactory.unsubscribe("ToyAlgorithm", 1); + algFactory.unsubscribe("ToyAlgorithm", 2); } void testGetDescriptors() { - AlgorithmFactory::Instance().subscribe<ToyAlgorithm>(); + auto &algFactory = AlgorithmFactory::Instance(); + + algFactory.subscribe<ToyAlgorithm>(); std::vector<AlgorithmDescriptor> descriptors; - TS_ASSERT_THROWS_NOTHING( - descriptors = AlgorithmFactory::Instance().getDescriptors(true)); + TS_ASSERT_THROWS_NOTHING(descriptors = algFactory.getDescriptors(true)); size_t noOfAlgs = descriptors.size(); std::vector<AlgorithmDescriptor>::const_iterator descItr = @@ -179,60 +180,56 @@ public: } TS_ASSERT(foundAlg); - AlgorithmFactory::Instance().unsubscribe("ToyAlgorithm", 1); + algFactory.unsubscribe("ToyAlgorithm", 1); - TS_ASSERT_THROWS_NOTHING( - descriptors = AlgorithmFactory::Instance().getDescriptors(true)); + TS_ASSERT_THROWS_NOTHING(descriptors = algFactory.getDescriptors(true)); TS_ASSERT_EQUALS(noOfAlgs - 1, descriptors.size()); } void testGetCategories() { - AlgorithmFactory::Instance().subscribe<CategoryAlgorithm>(); + auto &algFactory = AlgorithmFactory::Instance(); + algFactory.subscribe<CategoryAlgorithm>(); std::unordered_set<std::string> validCategories; - TS_ASSERT_THROWS_NOTHING( - validCategories = AlgorithmFactory::Instance().getCategories(true)); + TS_ASSERT_THROWS_NOTHING(validCategories = algFactory.getCategories(true)); size_t noOfCats = validCategories.size(); TS_ASSERT_DIFFERS(validCategories.find("Fake"), validCategories.end()); - AlgorithmFactory::Instance().unsubscribe("CategoryAlgorithm", 1); - TS_ASSERT_THROWS_NOTHING( - validCategories = AlgorithmFactory::Instance().getCategories(true)); + algFactory.unsubscribe("CategoryAlgorithm", 1); + TS_ASSERT_THROWS_NOTHING(validCategories = algFactory.getCategories(true)); TS_ASSERT_EQUALS(noOfCats - 1, validCategories.size()); } void testGetCategoriesWithState() { - AlgorithmFactory::Instance().subscribe<CategoryAlgorithm>(); + auto &algFactory = AlgorithmFactory::Instance(); + algFactory.subscribe<CategoryAlgorithm>(); std::map<std::string, bool> validCategories; - TS_ASSERT_THROWS_NOTHING( - validCategories = - AlgorithmFactory::Instance().getCategoriesWithState()); + TS_ASSERT_THROWS_NOTHING(validCategories = + algFactory.getCategoriesWithState()); size_t noOfCats = validCategories.size(); TS_ASSERT_DIFFERS(validCategories.find("Fake"), validCategories.end()); - AlgorithmFactory::Instance().unsubscribe("CategoryAlgorithm", 1); - TS_ASSERT_THROWS_NOTHING( - validCategories = - AlgorithmFactory::Instance().getCategoriesWithState()); + algFactory.unsubscribe("CategoryAlgorithm", 1); + TS_ASSERT_THROWS_NOTHING(validCategories = + algFactory.getCategoriesWithState()); TS_ASSERT_EQUALS(noOfCats - 1, validCategories.size()); } void testDecodeName() { + auto &algFactory = AlgorithmFactory::Instance(); std::pair<std::string, int> basePair; basePair.first = "Cat"; basePair.second = 1; std::string mangledName = "Cat|1"; std::pair<std::string, int> outPair; - TS_ASSERT_THROWS_NOTHING( - outPair = AlgorithmFactory::Instance().decodeName(mangledName)); + TS_ASSERT_THROWS_NOTHING(outPair = algFactory.decodeName(mangledName)); TS_ASSERT_EQUALS(basePair.first, outPair.first); TS_ASSERT_EQUALS(basePair.second, outPair.second); mangledName = "Cat 1"; - TS_ASSERT_THROWS_ANYTHING( - outPair = AlgorithmFactory::Instance().decodeName(mangledName)); + TS_ASSERT_THROWS_ANYTHING(outPair = algFactory.decodeName(mangledName)); } }; diff --git a/Framework/Algorithms/test/GenerateIPythonNotebookTest.h b/Framework/Algorithms/test/GenerateIPythonNotebookTest.h index cbd4cdfeb4ba454945b84f300eaac91d6cfb499c..6628ace1b951f7f7ae7ff3e29e8b972596e8ccbd 100644 --- a/Framework/Algorithms/test/GenerateIPythonNotebookTest.h +++ b/Framework/Algorithms/test/GenerateIPythonNotebookTest.h @@ -148,7 +148,7 @@ public: TS_ASSERT_THROWS_NOTHING(powerer.execute()); TS_ASSERT_EQUALS(powerer.isExecuted(), true); - // set up history for the algorithn which is presumably removed from Mantid + // set up history for the algorithm which is presumably removed from Mantid auto ws = API::FrameworkManager::Instance().getWorkspace(wsName); API::WorkspaceHistory &history = ws->history(); auto pAlg = Mantid::Kernel::make_unique<NonExistingAlgorithm>(); diff --git a/Framework/Crystal/CMakeLists.txt b/Framework/Crystal/CMakeLists.txt index fcc6c95a1e9b4c2b76a5f5a34cd12231012b0234..5d22c05bcba5270d7eeb3d18781b1a238c2cdcb9 100644 --- a/Framework/Crystal/CMakeLists.txt +++ b/Framework/Crystal/CMakeLists.txt @@ -38,7 +38,6 @@ set ( SRC_FILES src/MaskPeaksWorkspace.cpp src/NormaliseVanadium.cpp src/OptimizeCrystalPlacement.cpp - src/OptimizeExtinctionParameters.cpp src/OptimizeLatticeForCellType.cpp src/PeakBackground.cpp src/PeakClusterProjection.cpp @@ -67,7 +66,6 @@ set ( SRC_FILES src/SortHKL.cpp src/SortPeaksWorkspace.cpp src/StatisticsOfPeaksWorkspace.cpp - src/TOFExtinction.cpp src/TransformHKL.cpp ) @@ -114,7 +112,6 @@ set ( INC_FILES inc/MantidCrystal/MaskPeaksWorkspace.h inc/MantidCrystal/NormaliseVanadium.h inc/MantidCrystal/OptimizeCrystalPlacement.h - inc/MantidCrystal/OptimizeExtinctionParameters.h inc/MantidCrystal/OptimizeLatticeForCellType.h inc/MantidCrystal/PeakBackground.h inc/MantidCrystal/PeakClusterProjection.h @@ -143,7 +140,6 @@ set ( INC_FILES inc/MantidCrystal/SortHKL.h inc/MantidCrystal/SortPeaksWorkspace.h inc/MantidCrystal/StatisticsOfPeaksWorkspace.h - inc/MantidCrystal/TOFExtinction.h inc/MantidCrystal/TransformHKL.h ) diff --git a/Framework/Crystal/inc/MantidCrystal/OptimizeExtinctionParameters.h b/Framework/Crystal/inc/MantidCrystal/OptimizeExtinctionParameters.h deleted file mode 100644 index aa96161150665e9b9e7b9b58306b214d08d6c16f..0000000000000000000000000000000000000000 --- a/Framework/Crystal/inc/MantidCrystal/OptimizeExtinctionParameters.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef MANTID_CRYSTAL_OptimizeExtinctionParameters_H_ -#define MANTID_CRYSTAL_OptimizeExtinctionParameters_H_ - -#include "MantidAPI/Algorithm.h" -#include "MantidKernel/System.h" -#include "MantidDataObjects/OffsetsWorkspace.h" -#include "MantidGeometry/Crystal/PointGroup.h" -#include <gsl/gsl_blas.h> -#include <gsl/gsl_multifit_nlin.h> -#include <gsl/gsl_multimin.h> -#include <gsl/gsl_statistics.h> - -namespace Mantid { -namespace Algorithms { -/** - Find the offsets for each detector - - @author Vickie Lynch, SNS - @date 02/06/2012 - - Copyright © 2009 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge - National Laboratory & European Spallation Source - - This file is part of Mantid. - - Mantid is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - Mantid is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - - File change history is stored at: <https://github.com/mantidproject/mantid> - Code Documentation is available at: <http://doxygen.mantidproject.org> - */ -class DLLExport OptimizeExtinctionParameters : public API::Algorithm { -public: - /// Default constructorMatrix - OptimizeExtinctionParameters(); - /// Algorithm's name for identification overriding a virtual method - const std::string name() const override { - return "OptimizeExtinctionParameters"; - } - /// Summary of algorithms purpose - const std::string summary() const override { - return "Finds optimal mosaic and r_crystallite parameters for extinction " - "correction."; - } - /// Algorithm's version for identification overriding a virtual method - int version() const override { return 1; } - /// Algorithm's category for identification overriding a virtual method - const std::string category() const override { return "Crystal\\Corrections"; } - /// Call TOFExtinction as a Child Algorithm to get statistics of the peaks - double fitMosaic(double mosaic, double rcrystallite, std::string inname, - std::string corrOption, std::string pointOption, - std::string tofParams); - -private: - /// Point Groups possible - std::vector<Mantid::Geometry::PointGroup_sptr> m_pointGroups; - // Overridden Algorithm methods - void init() override; - void exec() override; -}; - -} // namespace Algorithm -} // namespace Mantid - -#endif /*MANTID_CRYSTAL_OptimizeExtinctionParameters_H_*/ diff --git a/Framework/Crystal/inc/MantidCrystal/TOFExtinction.h b/Framework/Crystal/inc/MantidCrystal/TOFExtinction.h deleted file mode 100644 index 1464277cb77b10673705fca13e9ae529bf4e141d..0000000000000000000000000000000000000000 --- a/Framework/Crystal/inc/MantidCrystal/TOFExtinction.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef MANTID_CRYSTAL_TOFEXTINCTION_H_ -#define MANTID_CRYSTAL_TOFEXTINCTION_H_ - -#include "MantidKernel/System.h" -#include "MantidAPI/Algorithm.h" - -namespace Mantid { -namespace Crystal { - -/** Save a PeaksWorkspace to a Gsas-style ASCII .hkl file. - * - * @author Vickie Lynch, SNS - * @date 2012-01-20 - */ -class DLLExport TOFExtinction : public API::Algorithm { -public: - TOFExtinction(); - - /// Algorithm's name for identification - const std::string name() const override { return "TOFExtinction"; }; - /// Summary of algorithms purpose - const std::string summary() const override { - return "Extinction correction for single crystal peaks."; - } - - /// Algorithm's version for identification - int version() const override { return 1; }; - /// Algorithm's category for identification - const std::string category() const override { - return "Crystal;DataHandling\\Text"; - } - -private: - /// Initialise the properties; - void init() override; - /// Run the algorithm; - void exec() override; - double getEg(double mosaic); - double getEgLaue(double Eg, double twoth, double wl, double divBeam, - double betaBeam); - double getXqt(double Eg, double cellV, double wl, double twoth, double tbar, - double fsq); - double getZachariasen(double Xqt); - double getGaussian(double Xqt, double twoth); - double getLorentzian(double Xqt, double twoth); - double getEsLaue(double r, double twoth, double wl); - double getRg(double EgLaue, double EsLaue, double wl, double twoth); - double getRgGaussian(double EgLaue, double r_crystallite, double wl, - double twoth); - double getRgLorentzian(double EgLaue, double r_crystallite, double wl, - double twoth); - double getXqtII(double Rg, double cellV, double wl, double twoth, double tbar, - double fsq); - double getTypeIIZachariasen(double XqtII); - double getTypeIIGaussian(double XqtII, double twoth); - double getTypeIILorentzian(double XqtII, double twoth); - double getSigFsqr(double Rg, double cellV, double wl, double twoth, - double tbar, double fsq, double sigfsq, - double relSigRg = 0.03); - double absor_sphere(double &twoth, double &wl); - double m_smu = 0.0; ///< linear scattering coefficient in 1/cm - double m_amu = 0.0; ///< linear absoprtion coefficient in 1/cm - double m_radius = 0.0; ///< sample radius in cm -}; -} // namespace Mantid; -} // namespace Crystal; -#endif /* MANTID_CRYSTAL_TOFEXTINCTION_H_ */ diff --git a/Framework/Crystal/src/OptimizeExtinctionParameters.cpp b/Framework/Crystal/src/OptimizeExtinctionParameters.cpp deleted file mode 100644 index 1ade805dbbdf5920561b6030e3dcc3b4797ab8f3..0000000000000000000000000000000000000000 --- a/Framework/Crystal/src/OptimizeExtinctionParameters.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#include "MantidCrystal/OptimizeExtinctionParameters.h" -#include "MantidDataObjects/PeaksWorkspace.h" -#include "MantidAPI/AnalysisDataService.h" -#include "MantidAPI/FileProperty.h" -#include "MantidAPI/FunctionFactory.h" -#include "MantidAPI/IPeakFunction.h" -#include "MantidAPI/IBackgroundFunction.h" -#include "MantidAPI/CompositeFunction.h" -#include "MantidKernel/VectorHelper.h" -#include "MantidKernel/ListValidator.h" -#include "MantidKernel/BoundedValidator.h" -#include <fstream> - -using namespace Mantid::Geometry; - -namespace Mantid { -namespace Algorithms { - -// Register the class into the algorithm factory -// DECLARE_ALGORITHM(OptimizeExtinctionParameters) - -using namespace Kernel; -using namespace API; -using std::size_t; -using namespace DataObjects; - -/// Constructor -OptimizeExtinctionParameters::OptimizeExtinctionParameters() { - m_pointGroups = getAllPointGroups(); -} - -static double gsl_costFunction(const gsl_vector *v, void *params) { - std::string *p = reinterpret_cast<std::string *>(params); - std::string inname = p[0]; - std::string corrOption = p[1]; - std::string pointOption = p[2]; - std::string tofParams = p[3]; - std::vector<double> tofParam = - Kernel::VectorHelper::splitStringIntoVector<double>(tofParams); - double rcrystallite = tofParam[1]; - double mosaic = tofParam[2]; - if (corrOption.compare(5, 2, "II") == 0) - rcrystallite = gsl_vector_get(v, 0); - else - mosaic = gsl_vector_get(v, 0); - if (v->size > 1) - rcrystallite = gsl_vector_get(v, 1); - Mantid::Algorithms::OptimizeExtinctionParameters u; - return u.fitMosaic(mosaic, rcrystallite, inname, corrOption, pointOption, - tofParams); -} - -//----------------------------------------------------------------------------------------- -/** Initialisation method. Declares properties to be used in algorithm. - */ -void OptimizeExtinctionParameters::init() { - - declareProperty(make_unique<WorkspaceProperty<PeaksWorkspace>>( - "InputWorkspace", "", Direction::InOut), - "An input PeaksWorkspace with an instrument."); - std::vector<std::string> corrOptions{ - "Type I Zachariasen", "Type I Gaussian", "Type I Lorentzian", - "Type II Zachariasen", "Type II Gaussian", "Type II Lorentzian", - "Type I&II Zachariasen", "Type I&II Gaussian", "Type I&II Lorentzian", - "None, Scaling Only"}; - declareProperty("ExtinctionCorrectionType", corrOptions[0], - boost::make_shared<StringListValidator>(corrOptions), - "Select the type of extinction correction."); - - auto mustBePositive = boost::make_shared<BoundedValidator<double>>(); - mustBePositive->setLower(0.0); - declareProperty("Cell", 255.0, "Unit Cell Volume (Angstroms^3)"); - declareProperty("Mosaic", 0.262, "Mosaic Spread (FWHM) (Degrees)", - Direction::InOut); - declareProperty("RCrystallite", 6.0, - "Becker-Coppens Crystallite Radius (micron)", - Direction::InOut); - std::vector<std::string> propOptions; - for (auto &pointGroup : m_pointGroups) - propOptions.push_back(pointGroup->getName()); - declareProperty("PointGroup", propOptions[0], - boost::make_shared<StringListValidator>(propOptions), - "Which point group applies to this crystal?"); - declareProperty("OutputChi2", 0.0, Direction::Output); - - // Disable default gsl error handler (which is to call abort!) - gsl_set_error_handler_off(); -} - -//----------------------------------------------------------------------------------------- -/** Executes the algorithm - * - * @throw Exception::FileError If the grouping file cannot be opened or read - *successfully - */ -void OptimizeExtinctionParameters::exec() { - std::string par[4]; - std::string inname = getProperty("InputWorkspace"); - par[0] = inname; - std::string type = getProperty("ExtinctionCorrectionType"); - par[1] = type; - std::string group = getProperty("PointGroup"); - par[2] = group; - PeaksWorkspace_sptr ws = getProperty("InputWorkspace"); - - double mosaic = getProperty("Mosaic"); - double cell = getProperty("Cell"); - double r_crystallite = getProperty("RCrystallite"); - std::ostringstream strwi; - strwi << cell << "," << r_crystallite << "," << mosaic; - par[3] = strwi.str(); - - const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex; - gsl_multimin_fminimizer *s = nullptr; - gsl_vector *ss, *x; - gsl_multimin_function minex_func; - - // finally do the fitting - - size_t nopt = 1; - if (type.compare(7, 2, "II") == 0) - nopt = 2; - size_t iter = 0; - int status = 0; - - /* Starting point */ - x = gsl_vector_alloc(nopt); - if (type.compare(5, 2, "II") == 0) - gsl_vector_set(x, 0, r_crystallite); - else - gsl_vector_set(x, 0, mosaic); - if (nopt > 1) - gsl_vector_set(x, 1, r_crystallite); - - /* Set initial step sizes to 0.001 */ - ss = gsl_vector_alloc(nopt); - gsl_vector_set_all(ss, 0.001); - - /* Initialize method and iterate */ - minex_func.n = nopt; - minex_func.f = &Mantid::Algorithms::gsl_costFunction; - minex_func.params = ∥ - - s = gsl_multimin_fminimizer_alloc(T, nopt); - gsl_multimin_fminimizer_set(s, &minex_func, x, ss); - - do { - iter++; - status = gsl_multimin_fminimizer_iterate(s); - if (status) - break; - - double size = gsl_multimin_fminimizer_size(s); - status = gsl_multimin_test_size(size, 1e-4); - - } while (status == GSL_CONTINUE && iter < 500); - - // Output summary to log file - std::string reportOfDiffractionEventCalibrateDetectors = gsl_strerror(status); - // g_log.debug() << - if (type.compare(5, 2, "II") == 0) - r_crystallite = gsl_vector_get(s->x, 0); - else - mosaic = gsl_vector_get(s->x, 0); - if (nopt > 1) - r_crystallite = gsl_vector_get(s->x, 1); - std::cout << " Method used = " - << " Simplex" - << " Iteration = " << iter - << " Status = " << reportOfDiffractionEventCalibrateDetectors - << " Minimize Sum = " << s->fval << " Mosaic = " << mosaic - << " RCrystallite = " << r_crystallite << " \n"; - gsl_vector_free(x); - gsl_vector_free(ss); - gsl_multimin_fminimizer_free(s); - setProperty("Mosaic", mosaic); - setProperty("RCrystallite", r_crystallite); - setProperty("OutputChi2", s->fval); -} - -//----------------------------------------------------------------------------------------- -/** - * Calls Gaussian1D as a child algorithm to fit the offset peak in a spectrum - * @param mosaic - * @param rcrystallite - * @param inname - * @param corrOption - * @param pointOption - * @param tofParams - * @return - */ -double OptimizeExtinctionParameters::fitMosaic( - double mosaic, double rcrystallite, std::string inname, - std::string corrOption, std::string pointOption, std::string tofParams) { - PeaksWorkspace_sptr inputW = boost::dynamic_pointer_cast<PeaksWorkspace>( - AnalysisDataService::Instance().retrieve(inname)); - std::vector<double> tofParam = - Kernel::VectorHelper::splitStringIntoVector<double>(tofParams); - if (mosaic < 0.0 || rcrystallite < 0.0) - return 1e300; - - API::IAlgorithm_sptr tofextinction = - createChildAlgorithm("TOFExtinction", 0.0, 0.2); - tofextinction->setProperty("InputWorkspace", inputW); - tofextinction->setProperty("OutputWorkspace", "tmp"); - tofextinction->setProperty("ExtinctionCorrectionType", corrOption); - tofextinction->setProperty<double>("Mosaic", mosaic); - tofextinction->setProperty<double>("Cell", tofParam[0]); - tofextinction->setProperty<double>("RCrystallite", rcrystallite); - tofextinction->executeAsChildAlg(); - PeaksWorkspace_sptr peaksW = tofextinction->getProperty("OutputWorkspace"); - - API::IAlgorithm_sptr sorthkl = createChildAlgorithm("SortHKL", 0.0, 0.2); - sorthkl->setProperty("InputWorkspace", peaksW); - sorthkl->setProperty("OutputWorkspace", peaksW); - sorthkl->setProperty("PointGroup", pointOption); - sorthkl->executeAsChildAlg(); - double Chisq = sorthkl->getProperty("OutputChi2"); - std::cout << mosaic << " " << rcrystallite << " " << Chisq << "\n"; - return Chisq; -} - -} // namespace Algorithm -} // namespace Mantid diff --git a/Framework/Crystal/src/TOFExtinction.cpp b/Framework/Crystal/src/TOFExtinction.cpp deleted file mode 100644 index 82e4d5bd61511eb804add60ee67fb479e56e4b29..0000000000000000000000000000000000000000 --- a/Framework/Crystal/src/TOFExtinction.cpp +++ /dev/null @@ -1,424 +0,0 @@ -#include "MantidAPI/FileProperty.h" -#include "MantidAPI/Run.h" -#include "MantidAPI/Sample.h" -#include "MantidCrystal/TOFExtinction.h" -#include "MantidDataObjects/Peak.h" -#include "MantidDataObjects/PeaksWorkspace.h" -#include "MantidGeometry/Instrument/RectangularDetector.h" -#include "MantidKernel/Material.h" -#include "MantidKernel/Utils.h" -#include "MantidKernel/ListValidator.h" -#include <fstream> -#include <cmath> - -using namespace Mantid::Geometry; -using namespace Mantid::DataObjects; -using namespace Mantid::Kernel; -using namespace Mantid::API; -using namespace Mantid::PhysicalConstants; - -namespace { -const double pc[4][19] = { - {1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, - 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, - 1.0000}, - {1.9368, 1.8653, 1.6908, 1.4981, 1.3532, 1.2746, 1.2530, 1.2714, 1.3093, - 1.3559, 1.4019, 1.4434, 1.4794, 1.5088, 1.5317, 1.5489, 1.5608, 1.5677, - 1.5700}, - {0.0145, 0.1596, 0.5175, 0.9237, 1.2436, 1.4308, 1.4944, 1.4635, 1.3770, - 1.2585, 1.1297, 1.0026, 0.8828, 0.7768, 0.6875, 0.6159, 0.5637, 0.5320, - 0.5216}, - {1.1386, 1.0604, 0.8598, 0.6111, 0.3798, 0.1962, 0.0652, -0.0198, -0.0716, - -0.0993, -0.1176, -0.1153, -0.1125, -0.1073, -0.1016, -0.0962, -0.0922, - -0.0898, -0.0892}}; - -const double MAX_WAVELENGTH = 50.0; // max in lamda_weight table - -const double STEPS_PER_ANGSTROM = 100; // resolution of lamda table - -const double radtodeg_half = 180.0 / M_PI / 2.; -} - -namespace Mantid { -namespace Crystal { - -// Register the algorithm into the AlgorithmFactory -// DECLARE_ALGORITHM(TOFExtinction) - -/** Initialize the algorithm's properties. - */ -void TOFExtinction::init() { - declareProperty(make_unique<WorkspaceProperty<PeaksWorkspace>>( - "InputWorkspace", "", Direction::InOut), - "An input PeaksWorkspace with an instrument."); - declareProperty(make_unique<WorkspaceProperty<PeaksWorkspace>>( - "OutputWorkspace", "", Direction::Output)); - std::vector<std::string> corrOptions{ - "Type I Zachariasen", "Type I Gaussian", "Type I Lorentzian", - "Type II Zachariasen", "Type II Gaussian", "Type II Lorentzian", - "Type I&II Zachariasen", "Type I&II Gaussian", "Type I&II Lorentzian", - "None, Scaling Only"}; - declareProperty("ExtinctionCorrectionType", corrOptions[0], - boost::make_shared<StringListValidator>(corrOptions), - "Select the type of extinction correction."); - - declareProperty("Mosaic", 0.262, "Mosaic Spread (FWHM) (Degrees)"); - declareProperty("Cell", 255.0, "Unit Cell Volume (Angstroms^3)"); - declareProperty("RCrystallite", 6.0, - "Becker-Coppens Crystallite Radius (micron)"); - declareProperty("ScaleFactor", 1.0, - "Multiply FSQ and sig(FSQ) by scaleFactor"); - declareProperty("DivBeam", 0.005, "Minimum beam divergence in radian"); - declareProperty("BetaBeam", 0.002, - "Wavelength dependence of beam divergence"); -} - -/** Execute the algorithm. - */ -void TOFExtinction::exec() { - - PeaksWorkspace_sptr inPeaksW = getProperty("InputWorkspace"); - /// Output peaks workspace, create if needed - PeaksWorkspace_sptr peaksW = getProperty("OutputWorkspace"); - if (peaksW != inPeaksW) - peaksW = inPeaksW->clone(); - - const auto sampleMaterial = inPeaksW->sample().getMaterial(); - if (sampleMaterial.totalScatterXSection(NeutronAtom::ReferenceLambda) != - 0.0) { - double rho = sampleMaterial.numberDensity(); - m_smu = - sampleMaterial.totalScatterXSection(NeutronAtom::ReferenceLambda) * rho; - m_amu = sampleMaterial.absorbXSection(NeutronAtom::ReferenceLambda) * rho; - } else { - throw std::invalid_argument( - "Could not retrieve LinearScatteringCoef from material"); - } - const API::Run &run = inPeaksW->run(); - if (run.hasProperty("Radius")) { - Kernel::Property *prop = run.getProperty("Radius"); - m_radius = boost::lexical_cast<double, std::string>(prop->value()); - } else { - throw std::invalid_argument("Could not retrieve Radius from run object"); - } - - std::string cType = getProperty("ExtinctionCorrectionType"); - int NumberPeaks = peaksW->getNumberPeaks(); - double mosaic = getProperty("Mosaic"); - double cell = getProperty("Cell"); - double r_crystallite = getProperty("RCrystallite"); - double scaleFactor = getProperty("ScaleFactor"); - double divBeam = getProperty("DivBeam"); - double betaBeam = getProperty("BetaBeam"); - double Eg = getEg( - mosaic); // defined by Zachariasen, W. H. (1967). Acta Cryst. A23, 558 - double y_corr = 1.0; - double sigfsq_ys = 0.0; - for (int i = 0; i < NumberPeaks; i++) { - Peak &peak1 = peaksW->getPeaks()[i]; - double fsq = peak1.getIntensity() * scaleFactor; - double sigfsq = peak1.getSigmaIntensity() * scaleFactor; - double wl = peak1.getWavelength(); - double twoth = peak1.getScattering(); - double tbar = absor_sphere(twoth, wl); - // Extinction Correction - - if (cType == "Type I Zachariasen") { - // Apply correction to fsq with Type-I Z for testing - double EgLaueI = getEgLaue(Eg, twoth, wl, divBeam, betaBeam); - double Xqt = getXqt(EgLaueI, cell, wl, twoth, tbar, fsq); - y_corr = getZachariasen(Xqt); - sigfsq_ys = getSigFsqr(EgLaueI, cell, wl, twoth, tbar, fsq, sigfsq); - } else if (cType == "Type I Gaussian") { - // Apply correction to fsq with Type-I BCG for testing - double EgLaueI = - M_SQRT2 * getEgLaue(Eg, twoth, wl, divBeam, betaBeam) * 2.0 / 3.0; - double Xqt = getXqt(EgLaueI, cell, wl, twoth, tbar, fsq); - y_corr = getGaussian(Xqt, twoth); - sigfsq_ys = getSigFsqr(EgLaueI, cell, wl, twoth, tbar, fsq, sigfsq); - } else if (cType == "Type I Lorentzian") { - // Apply correction to fsq with Type-I BCL for testing - double EgLaueI = getEgLaue(Eg, twoth, wl, divBeam, betaBeam); - double Xqt = getXqt(EgLaueI, cell, wl, twoth, tbar, fsq); - y_corr = getLorentzian(Xqt, twoth); - sigfsq_ys = getSigFsqr(EgLaueI, cell, wl, twoth, tbar, fsq, sigfsq); - } else if (cType == "Type II Zachariasen") { - // Apply correction to fsq with Type-II Z for testing - double EsLaue = getEgLaue(r_crystallite, twoth, wl, divBeam, betaBeam); - double Xqt = getXqt(EsLaue, cell, wl, twoth, tbar, fsq); - y_corr = getZachariasen(Xqt); - sigfsq_ys = getSigFsqr(EsLaue, cell, wl, twoth, tbar, fsq, sigfsq); - } else if (cType == "Type II Gaussian") { - // Apply correction to fsq with Type-II BCG for testing - double EsLaue = getEgLaue(r_crystallite, twoth, wl, divBeam, betaBeam); - double Xqt = getXqt(EsLaue, cell, wl, twoth, tbar, fsq); - y_corr = getGaussian(Xqt, twoth); - sigfsq_ys = getSigFsqr(EsLaue, cell, wl, twoth, tbar, fsq, sigfsq); - } else if (cType == "Type II Lorentzian") { - // Apply correction to fsq with Type-II BCL for testing - double EsLaue = getEgLaue(r_crystallite, twoth, wl, divBeam, betaBeam); - double Xqt = getXqt(EsLaue, cell, wl, twoth, tbar, fsq); - y_corr = getLorentzian(Xqt, twoth); - sigfsq_ys = getSigFsqr(EsLaue, cell, wl, twoth, tbar, fsq, sigfsq); - } else if (cType == "Type I&II Zachariasen") { - // Apply correction to fsq with Type-II Z for testing - double EgLaueI = getEgLaue(Eg, twoth, wl, divBeam, betaBeam); - double EsLaue = getEgLaue(r_crystallite, twoth, wl, divBeam, betaBeam); - double Rg = getRg(EgLaueI, EsLaue, wl, twoth); - double Xqt = getXqtII(Rg, cell, wl, twoth, tbar, fsq); - y_corr = getTypeIIZachariasen(Xqt); - sigfsq_ys = getSigFsqr(EsLaue, cell, wl, twoth, tbar, fsq, sigfsq); - } else if (cType == "Type I&II Gaussian") { - // Apply correction to fsq with Type-II BCG for testing - double EgLaueI = getEgLaue(Eg, twoth, wl, divBeam, betaBeam); - double Rg = getRgGaussian(EgLaueI, r_crystallite, wl, twoth); - double Xqt = getXqtII(Rg, cell, wl, twoth, tbar, fsq); - y_corr = getTypeIIGaussian(Xqt, twoth); - sigfsq_ys = getSigFsqr(Rg, cell, wl, twoth, tbar, fsq, sigfsq); - } else if (cType == "Type I&II Lorentzian") { - // Apply correction to fsq with Type-II BCL for testing - double EgLaueI = getEgLaue(Eg, twoth, wl, divBeam, betaBeam); - double Rg = getRgLorentzian(EgLaueI, r_crystallite, wl, twoth); - double Xqt = getXqtII(Rg, cell, wl, twoth, tbar, fsq); - y_corr = getTypeIILorentzian(Xqt, twoth); - sigfsq_ys = getSigFsqr(Rg, cell, wl, twoth, tbar, fsq, sigfsq); - } else if (cType == "None, Scaling Only") { - y_corr = 1.0; // No extinction correction - sigfsq_ys = sigfsq; - } - - double ys = fsq / y_corr; - // std::cout << fsq << " " << y_corr<<" "<<wl<<" "<<twoth<<" "<<tbar<< " - // " << ys <<"\n"; - if (!std::isnan(ys)) - peak1.setIntensity(ys); - else - peak1.setIntensity(0.0); - sigfsq_ys = - std::sqrt(1.0 + sigfsq_ys * sigfsq_ys + std::pow(0.005 * sigfsq_ys, 2)); - peak1.setSigmaIntensity(sigfsq_ys); - - // output reflection to log file and to hkl file with SaveHKL - } - setProperty("OutputWorkspace", peaksW); -} -double TOFExtinction::getEg(double mosaic) { - double Eg = 2.0 * std::sqrt(std::log(static_cast<double>(2.0)) / (2 * M_PI)) / - (mosaic * M_PI / 180.0); - return Eg; -} -double TOFExtinction::getEgLaue(double Eg, double twoth, double wl, - double divBeam, double betaBeam) { - // divbeam is the default [minimum] beam divergence in radian. - // Tomiyoshi, Yamada and Watanabe - UNUSED_ARG(wl); // Keep arguments consistent with tofExtinction.py - double EgLaue = - 1.0 / std::sqrt(std::pow(betaBeam * std::tan(twoth / 2.0), 2) + - std::pow(divBeam, 2) + 1.0 / std::pow(Eg, 2)); - return EgLaue; -} -double TOFExtinction::getXqt(double Eg, double cellV, double wl, double twoth, - double tbar, double fsq) { - // Xqt calculated from measured Fsqr; - // Maslen & Spadaccini - double beta = Eg / std::pow(cellV, 2) * std::pow(wl, 4) / 2 / - std::pow((std::sin(twoth / 2)), 2) * tbar * fsq / 10; - double Xqt = std::pow(beta, 2) + beta * std::sqrt(std::pow(beta, 2) + 1); - return Xqt; -} -double TOFExtinction::getZachariasen(double Xqt) { - // TYPE-I, Zachariasen, W. H. (1967). Acta Cryst. A23, 558 ; - double y_ext = std::sqrt(1 + 2 * Xqt); - return y_ext; -} -double TOFExtinction::getGaussian(double Xqt, double twoth) { - if (Xqt < 0.0 || Xqt > 30.0) - return 1; - // Type-I, Gaussian, Becker, P. J. & Coppens, P. (1974). Acta Cryst. A30, 129; - double y_ext = std::sqrt( - 1 + 2 * Xqt + - (0.58 + 0.48 * std::cos(twoth) + 0.24 * std::pow((std::cos(twoth)), 2)) * - std::pow(Xqt, 2) / (1 + (0.02 - 0.025 * std::cos(twoth)) * Xqt)); - return y_ext; -} -double TOFExtinction::getLorentzian(double Xqt, double twoth) { - // TYPE-I Lorentzian, Becker, P. J. & Coppens, P. (1974). Acta Cryst. A30, - // 129; - double y_ext; - if (twoth < M_PI / 2.0) - y_ext = std::sqrt(1 + 2 * Xqt + - (0.025 + 0.285 * std::cos(twoth)) * std::pow(Xqt, 2) / - (1 + 0.15 * Xqt - - 0.2 * std::pow((0.75 - std::cos(twoth)), 2) * Xqt)); - else - y_ext = std::sqrt(1 + 2 * Xqt + - (0.025 + 0.285 * std::cos(twoth)) * std::pow(Xqt, 2) / - (1 - 0.45 * Xqt * std::cos(twoth))); - return y_ext; -} -double TOFExtinction::getEsLaue(double r, double twoth, double wl) { - // Type II mosaic distribution radius in micron - // Tomiyoshi, Yamada and Watanabe - double EsLaue = r * 10000.0 * 2.0 * std::pow(std::sin(twoth / 2 / wl), 2); - return EsLaue; -} -double TOFExtinction::getRg(double EgLaue, double EsLaue, double wl, - double twoth) { - UNUSED_ARG(wl) - UNUSED_ARG(twoth) - // Two-theta dependence by Becker & Coppens, Acta Cryst A 30, 129 (1974) - // The factor is std::pow((std::sin(twoth/2)/wl),2) for tof neutron - // double Es = r*std::pow((1000.0*std::sin(twoth/2)/wl),2); - double Rg = EsLaue / std::sqrt(1 + EsLaue * EsLaue / EgLaue / EgLaue); - return Rg; -} -double TOFExtinction::getRgGaussian(double EgLaue, double r_crystallite, - double wl, double twoth) { - // Combined Type I and Type II correction by Becker & Coppens - double r = r_crystallite; // micron - double Es = 1.5 * r * 10000 * 2 * pow(std::sin(twoth / 2) / wl, 2); - double RgGaussian = Es / std::sqrt(1.0 + Es * Es / EgLaue / EgLaue / 2.0); - RgGaussian = 2.0 / 3.0 * RgGaussian; - return RgGaussian; -} -double TOFExtinction::getRgLorentzian(double EgLaue, double r_crystallite, - double wl, double twoth) { - // Combined Type I and Type II correction by Becker & Coppens - double r = r_crystallite; // micron - double Es = 1.5 * r * 10000 * 2 * pow(std::sin(twoth / 2) / wl, 2); - double RgLorentzian = Es / (1 + 2 * Es / EgLaue / 3.0); - RgLorentzian = 2.0 / 3.0 * RgLorentzian; - return RgLorentzian; -} -double TOFExtinction::getXqtII(double Rg, double cellV, double wl, double twoth, - double tbar, double fsq) { - double betaII = Rg / std::pow(cellV, 2) * std::pow(wl, 4) / 2.0 / - std::pow((std::sin(twoth / 2)), 2) * tbar * fsq / 10; - double XqtII = - std::pow(betaII, 2) + betaII * std::sqrt(std::pow(betaII, 2) + 1); - return XqtII; -} -double TOFExtinction::getTypeIIZachariasen(double XqtII) { - // TYPE-II, Zachariasen, W. H. (1967). Acta Cryst. A23, 558 ; - double y_ext_II = std::sqrt(1 + 2 * XqtII); - return y_ext_II; -} -double TOFExtinction::getTypeIIGaussian(double XqtII, double twoth) { - if (XqtII < 0.0 || XqtII > 30.0) - return 1; - // Becker, P. J. & Coppens, P. (1974). Acta Cryst. A30, 129; - double y_ext_II = std::sqrt( - 1 + 2 * XqtII + - (0.58 + 0.48 * std::cos(twoth) + 0.24 * std::pow((std::cos(twoth)), 2)) * - std::pow(XqtII, 2) / (1 + (0.02 - 0.025 * std::cos(twoth)) * XqtII)); - return y_ext_II; -} -double TOFExtinction::getTypeIILorentzian(double XqtII, double twoth) { - // TYPE-II Lorentzian, Becker, P. J. & Coppens, P. (1974). Acta Cryst. A30, - // 129; - double y_ext_II; - if (twoth < M_PI) - y_ext_II = - std::sqrt(1 + 2 * XqtII + - (0.025 + 0.285 * std::cos(twoth)) * std::pow(XqtII, 2) / - (1 + 0.15 * XqtII - - 0.2 * std::pow((0.75 - std::cos(twoth)), 2) * XqtII)); - else - y_ext_II = - std::sqrt(1 + 2 * XqtII + - (0.025 + 0.285 * std::cos(twoth)) * std::pow(XqtII, 2) / - (1 - 0.45 * XqtII * std::cos(twoth))); - return y_ext_II; -} -double TOFExtinction::getSigFsqr(double Rg, double cellV, double wl, - double twoth, double tbar, double fsq, - double sigfsq, double relSigRg) { - double sig_Rg = relSigRg * Rg; // Estimated - double beta = Rg / std::pow(cellV, 2) * std::pow(wl, 4) / 2 / - std::pow(sin(twoth / 2), 2) * tbar * fsq / 10; - double bb = beta * beta; - double sigSqr = - std::pow(2 * beta + bb / std::sqrt(bb + 1) + std::sqrt(bb + 1), 2) * - sigfsq * sigfsq + - fsq * fsq * std::pow(beta / Rg, 2) * - std::pow(1 + beta / std::sqrt(bb + 1), 2) * sig_Rg * sig_Rg; - double sig = std::sqrt(sigSqr); - return sig; -} -/** - * function to calculate a spherical absorption correction - * and tbar. based on values in: - * - * c. w. dwiggins, jr., acta cryst. a31, 395 (1975). - * - * in this paper, a is the transmission and a* = 1/a is - * the absorption correction. - * - * input are the smu (scattering) and amu (absorption at 1.8 ang.) - * linear absorption coefficients, the radius r of the sample - * the theta angle and wavelength. - * the absorption (absn) and tbar are returned. - * - * a. j. schultz, june, 2008 - */ -double TOFExtinction::absor_sphere(double &twoth, double &wl) { - int i; - double mu, mur; // mu is the linear absorption coefficient, - // r is the radius of the spherical sample. - double theta, astar1, astar2, frac, astar; - double trans; - - // For each of the 19 theta values in dwiggins (theta = 0.0 to 90.0 - // in steps of 5.0 deg.), the astar values vs.mur were fit to a third - // order polynomial in excel. these values are given in the static array - // pc[][] - - mu = m_smu + (m_amu / 1.8f) * wl; - - mur = mu * m_radius; - if (mur < 0. || mur > 2.5) { - std::ostringstream s; - s << mur; - throw std::runtime_error("muR is not in range of Dwiggins' table :" + - s.str()); - } - - theta = twoth * radtodeg_half; - if (theta < 0. || theta > 90.) { - std::ostringstream s; - s << theta; - throw std::runtime_error("theta is not in range of Dwiggins' table :" + - s.str()); - } - - // using the polymial coefficients, calulate astar (= 1/transmission) at - // theta values below and above the actual theta value. - - i = static_cast<int>(theta / 5.); - astar1 = pc[0][i] + mur * (pc[1][i] + mur * (pc[2][i] + pc[3][i] * mur)); - - i = i + 1; - astar2 = pc[0][i] + mur * (pc[1][i] + mur * (pc[2][i] + pc[3][i] * mur)); - - // do a linear interpolation between theta values. - - frac = theta - - static_cast<double>(static_cast<int>(theta / 5.)) * 5.; // theta%5. - frac = frac / 5.; - - astar = astar1 * (1 - frac) + astar2 * frac; // astar is the correction - trans = 1.f / astar; // trans is the transmission - // trans = exp(-mu*tbar) - - // calculate tbar as defined by coppens. - double tbar; - if (mu == 0.0) - tbar = 0.0; - else - tbar = -std::log(trans) / mu; - - return tbar; -} - -} // namespace Mantid -} // namespace Crystal diff --git a/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/ConvolutionFit.h b/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/ConvolutionFit.h index 2992c54cf99dbef4b6df01ddeb089e106b6c046b..5b167bfeb69bfc172a12bdb26c328b52ed676412 100644 --- a/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/ConvolutionFit.h +++ b/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/ConvolutionFit.h @@ -45,13 +45,13 @@ protected: processParameterTable(API::ITableWorkspace_sptr parameterTable) override; std::map<std::string, std::string> getAdditionalLogStrings() const override; std::map<std::string, std::string> getAdditionalLogNumbers() const override; + std::vector<std::string> getFitParameterNames() const override; private: std::map<std::string, std::string> validateInputs() override; bool throwIfElasticQConversionFails() const override; bool isFitParameter(const std::string &name) const override; - void calculateEISF(API::ITableWorkspace_sptr &) const; bool m_deltaUsed; }; diff --git a/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSequential.h b/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSequential.h index d4b56d4637db575f69da71921379c76b68f65302..07c543741b2fdf68dbb7dbb33a02378ab69d7631 100644 --- a/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSequential.h +++ b/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSequential.h @@ -47,6 +47,7 @@ protected: virtual std::map<std::string, std::string> getAdditionalLogNumbers() const; virtual API::ITableWorkspace_sptr processParameterTable(API::ITableWorkspace_sptr parameterTable); + virtual std::vector<std::string> getFitParameterNames() const; private: void init() override; @@ -54,17 +55,19 @@ private: API::ITableWorkspace_sptr performFit(const std::string &input, const std::string &output); void deleteTemporaryWorkspaces(const std::string &outputBaseName); - void addAdditionalLogs(API::MatrixWorkspace_sptr result); + void addAdditionalLogs(API::Workspace_sptr result); virtual bool throwIfElasticQConversionFails() const; virtual bool isFitParameter(const std::string ¶meterName) const; - std::vector<std::string> getFitParameterNames() const; std::set<std::string> getUniqueParameterNames() const; std::string getOutputBaseName() const; std::string getInputString( const std::vector<API::MatrixWorkspace_sptr> &workspaces) const; - API::MatrixWorkspace_sptr - processIndirectFitParameters(API::ITableWorkspace_sptr parameterWorkspace); + std::vector<std::size_t> getDatasetGrouping( + const std::vector<API::MatrixWorkspace_sptr> &workspaces) const; + API::WorkspaceGroup_sptr + processIndirectFitParameters(API::ITableWorkspace_sptr parameterWorkspace, + const std::vector<std::size_t> &grouping); std::vector<API::MatrixWorkspace_sptr> convertInputToElasticQ( const std::vector<API::MatrixWorkspace_sptr> &workspaces) const; @@ -74,7 +77,7 @@ private: void renameWorkspaces(API::WorkspaceGroup_sptr outputGroup, const std::vector<std::string> &spectra, const std::vector<API::MatrixWorkspace_sptr> &names); - void copyLogs(API::MatrixWorkspace_sptr resultWorkspace, + void copyLogs(API::WorkspaceGroup_sptr resultWorkspace, const std::vector<API::MatrixWorkspace_sptr> &workspaces); void copyLogs(API::MatrixWorkspace_sptr resultWorkspace, API::WorkspaceGroup_sptr resultGroup); diff --git a/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSimultaneous.h b/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSimultaneous.h index fa4500f9637f9183f40d5365d62be8ca135d9da9..03068373093769ea5c69e1d01011258338d00941 100644 --- a/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSimultaneous.h +++ b/Framework/CurveFitting/inc/MantidCurveFitting/Algorithms/QENSFitSimultaneous.h @@ -43,7 +43,7 @@ protected: virtual bool throwIfElasticQConversionFails() const; virtual bool isFitParameter(const std::string &name) const; std::set<std::string> getUniqueParameterNames() const; - std::vector<std::string> getFitParameterNames() const; + virtual std::vector<std::string> getFitParameterNames() const; virtual std::map<std::string, std::string> getAdditionalLogStrings() const; virtual std::map<std::string, std::string> getAdditionalLogNumbers() const; virtual API::ITableWorkspace_sptr @@ -57,16 +57,18 @@ private: std::pair<API::ITableWorkspace_sptr, API::Workspace_sptr> performFit(const std::vector<API::MatrixWorkspace_sptr> &workspaces, const std::string &output); - API::MatrixWorkspace_sptr - processIndirectFitParameters(API::ITableWorkspace_sptr parameterWorkspace); - void copyLogs(API::MatrixWorkspace_sptr resultWorkspace, + API::WorkspaceGroup_sptr + processIndirectFitParameters(API::ITableWorkspace_sptr parameterWorkspace, + const std::vector<std::size_t> &grouping); + void copyLogs(API::WorkspaceGroup_sptr resultWorkspace, const std::vector<API::MatrixWorkspace_sptr> &workspaces); void copyLogs(API::MatrixWorkspace_sptr resultWorkspace, API::WorkspaceGroup_sptr resultGroup); void extractMembers(API::WorkspaceGroup_sptr resultGroupWs, const std::vector<API::MatrixWorkspace_sptr> &workspaces, const std::string &outputWsName); - void addAdditionalLogs(API::MatrixWorkspace_sptr result); + void addAdditionalLogs(API::WorkspaceGroup_sptr group); + void addAdditionalLogs(API::Workspace_sptr result); API::IAlgorithm_sptr extractMembersAlgorithm(API::WorkspaceGroup_sptr resultGroupWs, diff --git a/Framework/CurveFitting/src/Algorithms/ConvolutionFit.cpp b/Framework/CurveFitting/src/Algorithms/ConvolutionFit.cpp index 379a89585d55f93162174201312b773acf30d588..905137797fc3a2d1f985b85b23ab7201e835c4a6 100644 --- a/Framework/CurveFitting/src/Algorithms/ConvolutionFit.cpp +++ b/Framework/CurveFitting/src/Algorithms/ConvolutionFit.cpp @@ -195,6 +195,54 @@ calculateEISFAndError(const MantidVec &height, const MantidVec &heightError, return {eisfY, addVectors(eisfYSumRoot, errOverTotalSq)}; } + +void addEISFToTable(ITableWorkspace_sptr &tableWs) { + // Get height data from parameter table + const auto height = searchForFitParameters("Height", tableWs).at(0); + const auto heightErr = searchForFitParameters("Height_Err", tableWs).at(0); + auto heightY = tableWs->getColumn(height)->numeric_fill<>(); + auto heightE = tableWs->getColumn(heightErr)->numeric_fill<>(); + + // Get amplitude column names + const auto ampIndices = searchForFitParameters("Amplitude", tableWs); + const auto ampErrorIndices = searchForFitParameters("Amplitude_Err", tableWs); + + // For each lorentzian, calculate EISF + auto maxSize = ampIndices.size(); + if (ampErrorIndices.size() > maxSize) + maxSize = ampErrorIndices.size(); + + for (auto i = 0u; i < maxSize; ++i) { + // Get amplitude from column in table workspace + auto ampY = tableWs->getColumn(ampIndices[i])->numeric_fill<>(); + auto ampErr = tableWs->getColumn(ampErrorIndices[i])->numeric_fill<>(); + auto eisfAndError = calculateEISFAndError(heightY, heightE, ampY, ampErr); + + // Append the calculated values to the table workspace + auto ampName = tableWs->getColumn(ampIndices[i])->name(); + auto ampErrorName = tableWs->getColumn(ampErrorIndices[i])->name(); + auto columnName = + ampName.substr(0, (ampName.size() - std::string("Amplitude").size())); + columnName += "EISF"; + auto errorColumnName = ampErrorName.substr( + 0, (ampErrorName.size() - std::string("Amplitude_Err").size())); + errorColumnName += "EISF_Err"; + + tableWs->addColumn("double", columnName); + tableWs->addColumn("double", errorColumnName); + auto maxEisf = eisfAndError.first.size(); + if (eisfAndError.second.size() > maxEisf) { + maxEisf = eisfAndError.second.size(); + } + + auto col = tableWs->getColumn(columnName); + auto errCol = tableWs->getColumn(errorColumnName); + for (auto j = 0u; j < maxEisf; j++) { + col->cell<double>(j) = eisfAndError.first.at(j); + errCol->cell<double>(j) = eisfAndError.second.at(j); + } + } +} } // namespace namespace Mantid { @@ -292,7 +340,7 @@ ITableWorkspace_sptr ConvolutionFit<Base>::processParameterTable( IFunction_sptr function = Base::getProperty("Function"); m_deltaUsed = containsFunction(function, "DeltaFunction"); if (m_deltaUsed) - calculateEISF(parameterTable); + addEISFToTable(parameterTable); return parameterTable; } @@ -316,57 +364,12 @@ ConvolutionFit<Base>::getAdditionalLogNumbers() const { return logs; } -/** - * Calculates the EISF if the fit includes a Delta function - * @param tableWs - The TableWorkspace to append the EISF calculation to - */ template <typename Base> -void ConvolutionFit<Base>::calculateEISF(ITableWorkspace_sptr &tableWs) const { - // Get height data from parameter table - const auto height = searchForFitParameters("Height", tableWs).at(0); - const auto heightErr = searchForFitParameters("Height_Err", tableWs).at(0); - auto heightY = tableWs->getColumn(height)->numeric_fill<>(); - auto heightE = tableWs->getColumn(heightErr)->numeric_fill<>(); - - // Get amplitude column names - const auto ampIndices = searchForFitParameters("Amplitude", tableWs); - const auto ampErrorIndices = searchForFitParameters("Amplitude_Err", tableWs); - - // For each lorentzian, calculate EISF - auto maxSize = ampIndices.size(); - if (ampErrorIndices.size() > maxSize) - maxSize = ampErrorIndices.size(); - - for (auto i = 0u; i < maxSize; ++i) { - // Get amplitude from column in table workspace - auto ampY = tableWs->getColumn(ampIndices[i])->numeric_fill<>(); - auto ampErr = tableWs->getColumn(ampErrorIndices[i])->numeric_fill<>(); - auto eisfAndError = calculateEISFAndError(heightY, heightE, ampY, ampErr); - - // Append the calculated values to the table workspace - auto ampName = tableWs->getColumn(ampIndices[i])->name(); - auto ampErrorName = tableWs->getColumn(ampErrorIndices[i])->name(); - auto columnName = - ampName.substr(0, (ampName.size() - std::string("Amplitude").size())); - columnName += "EISF"; - auto errorColumnName = ampErrorName.substr( - 0, (ampErrorName.size() - std::string("Amplitude_Err").size())); - errorColumnName += "EISF_Err"; - - tableWs->addColumn("double", columnName); - tableWs->addColumn("double", errorColumnName); - auto maxEisf = eisfAndError.first.size(); - if (eisfAndError.second.size() > maxEisf) { - maxEisf = eisfAndError.second.size(); - } - - auto col = tableWs->getColumn(columnName); - auto errCol = tableWs->getColumn(errorColumnName); - for (auto j = 0u; j < maxEisf; j++) { - col->cell<double>(j) = eisfAndError.first.at(j); - errCol->cell<double>(j) = eisfAndError.second.at(j); - } - } +std::vector<std::string> ConvolutionFit<Base>::getFitParameterNames() const { + auto names = Base::getFitParameterNames(); + if (m_deltaUsed) + names.emplace_back("EISF"); + return names; } // Register the algorithms into the AlgorithmFactory diff --git a/Framework/CurveFitting/src/Algorithms/QENSFitSequential.cpp b/Framework/CurveFitting/src/Algorithms/QENSFitSequential.cpp index e5f128fb6f70e0971870ae32be7c11df06adf10d..515811a11c183b568a74a5ff16f3e90e93767d30 100644 --- a/Framework/CurveFitting/src/Algorithms/QENSFitSequential.cpp +++ b/Framework/CurveFitting/src/Algorithms/QENSFitSequential.cpp @@ -251,6 +251,32 @@ void renameWorkspacesInQENSFit(Algorithm *qensFit, if (outputGroup->getName() != groupName) renameWorkspace(renameAlgorithm, outputGroup, groupName); } + +std::vector<std::size_t> +createDatasetGrouping(const std::vector<MatrixWorkspace_sptr> &workspaces, + std::size_t maximum) { + std::vector<std::size_t> grouping; + grouping.emplace_back(0); + for (auto i = 1u; i < workspaces.size(); ++i) { + if (workspaces[i] != workspaces[i - 1]) + grouping.emplace_back(i); + } + grouping.emplace_back(maximum); + return grouping; +} + +std::vector<std::size_t> +createDatasetGrouping(const std::vector<MatrixWorkspace_sptr> &workspaces) { + return createDatasetGrouping(workspaces, workspaces.size()); +} + +WorkspaceGroup_sptr +createGroup(const std::vector<MatrixWorkspace_sptr> &workspaces) { + WorkspaceGroup_sptr group(new WorkspaceGroup); + for (auto &&workspace : workspaces) + group->addWorkspace(workspace); + return group; +} } // namespace namespace Mantid { @@ -318,9 +344,9 @@ void QENSFitSequential::init() { "or values using the notation described in the description section of " "the help page."); - declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>( + declareProperty(make_unique<WorkspaceProperty<WorkspaceGroup>>( "OutputWorkspace", "", Direction::Output), - "The output result workspace"); + "The output result workspace(s)"); declareProperty(make_unique<WorkspaceProperty<ITableWorkspace>>( "OutputParameterWorkspace", "", Direction::Output, PropertyMode::Optional), @@ -448,7 +474,8 @@ void QENSFitSequential::exec() { const auto parameterWs = processParameterTable(performFit(inputString, outputBaseName)); - const auto resultWs = processIndirectFitParameters(parameterWs); + const auto resultWs = + processIndirectFitParameters(parameterWs, getDatasetGrouping(workspaces)); const auto groupWs = AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( outputBaseName + "_Workspaces"); @@ -468,7 +495,8 @@ void QENSFitSequential::exec() { deleteTemporaryWorkspaces(outputBaseName); addAdditionalLogs(resultWs); - copyLogs(resultWs, groupWs); + copyLogs(boost::dynamic_pointer_cast<MatrixWorkspace>(resultWs->getItem(0)), + groupWs); setProperty("OutputWorkspace", resultWs); setProperty("OutputParameterWorkspace", parameterWs); @@ -497,8 +525,7 @@ QENSFitSequential::getAdditionalLogNumbers() const { return logs; } -void QENSFitSequential::addAdditionalLogs( - MatrixWorkspace_sptr resultWorkspace) { +void QENSFitSequential::addAdditionalLogs(Workspace_sptr resultWorkspace) { auto logAdder = createChildAlgorithm("AddSampleLog", -1.0, -1.0, false); logAdder->setProperty("Workspace", resultWorkspace); @@ -566,17 +593,36 @@ void QENSFitSequential::deleteTemporaryWorkspaces( deleteTemporaries(deleter, getTemporaryName()); } -MatrixWorkspace_sptr QENSFitSequential::processIndirectFitParameters( - ITableWorkspace_sptr parameterWorkspace) { +std::vector<std::size_t> QENSFitSequential::getDatasetGrouping( + const std::vector<API::MatrixWorkspace_sptr> &workspaces) const { + if (getPropertyValue("Input").empty()) { + int maximum = getProperty("SpecMax"); + return createDatasetGrouping(workspaces, + static_cast<std::size_t>(maximum + 1)); + } + return createDatasetGrouping(workspaces); +} + +WorkspaceGroup_sptr QENSFitSequential::processIndirectFitParameters( + ITableWorkspace_sptr parameterWorkspace, + const std::vector<std::size_t> &grouping) { auto pifp = createChildAlgorithm("ProcessIndirectFitParameters", 0.91, 0.95, true); pifp->setProperty("InputWorkspace", parameterWorkspace); pifp->setProperty("ColumnX", "axis-1"); pifp->setProperty("XAxisUnit", "MomentumTransfer"); pifp->setProperty("ParameterNames", getFitParameterNames()); - pifp->setProperty("OutputWorkspace", "__Result"); - pifp->executeAsChildAlg(); - return pifp->getProperty("OutputWorkspace"); + + std::vector<MatrixWorkspace_sptr> results; + results.reserve(grouping.size() - 1); + for (auto i = 0u; i < grouping.size() - 1; ++i) { + pifp->setProperty("StartRowIndex", static_cast<int>(grouping[i])); + pifp->setProperty("EndRowIndex", static_cast<int>(grouping[i + 1]) - 1); + pifp->setProperty("OutputWorkspace", "__Result"); + pifp->executeAsChildAlg(); + results.push_back(pifp->getProperty("OutputWorkspace")); + } + return createGroup(results); } ITableWorkspace_sptr @@ -669,7 +715,7 @@ void QENSFitSequential::extractMembers( } void QENSFitSequential::copyLogs( - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, const std::vector<MatrixWorkspace_sptr> &workspaces) { auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false); logCopier->setProperty("OutputWorkspace", resultWorkspace->getName()); diff --git a/Framework/CurveFitting/src/Algorithms/QENSFitSimultaneous.cpp b/Framework/CurveFitting/src/Algorithms/QENSFitSimultaneous.cpp index c3658f01d7a6cc95c3f908fbebbc81d9ad1e60be..a6375b4b7c462b8ba52c7a404251cdcff8e02942 100644 --- a/Framework/CurveFitting/src/Algorithms/QENSFitSimultaneous.cpp +++ b/Framework/CurveFitting/src/Algorithms/QENSFitSimultaneous.cpp @@ -8,6 +8,7 @@ #include "MantidAPI/IFuncMinimizer.h" #include "MantidAPI/ITableWorkspace.h" #include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/NumericAxis.h" #include "MantidAPI/WorkspaceFactory.h" #include "MantidKernel/BoundedValidator.h" @@ -186,6 +187,54 @@ ITableWorkspace_sptr transposeFitTable(ITableWorkspace_sptr table, } return transposed; } + +double getValueFromNumericAxis(MatrixWorkspace_sptr workspace, + std::size_t axisIndex, std::size_t valueIndex) { + return dynamic_cast<NumericAxis *>(workspace->getAxis(axisIndex)) + ->getValue(valueIndex); +} + +void addQValuesToTableColumn( + ITableWorkspace &table, const std::vector<MatrixWorkspace_sptr> &workspaces, + const Mantid::Kernel::PropertyManagerOwner &indexProperties, + std::size_t columnIndex) { + if (workspaces.empty()) + return; + + const auto column = table.getColumn(columnIndex); + const std::string prefix = "WorkspaceIndex"; + + int index = indexProperties.getProperty(prefix); + column->cell<double>(0) = getValueFromNumericAxis( + workspaces[0], 1, static_cast<std::size_t>(index)); + + for (auto i = 1u; i < workspaces.size(); ++i) { + const auto indexName = prefix + "_" + std::to_string(i); + index = indexProperties.getProperty(indexName); + column->cell<double>(i) = getValueFromNumericAxis( + workspaces[i], 1, static_cast<std::size_t>(index)); + } +} + +std::vector<std::size_t> +createDatasetGrouping(const std::vector<MatrixWorkspace_sptr> &workspaces) { + std::vector<std::size_t> grouping; + grouping.emplace_back(0); + for (auto i = 1u; i < workspaces.size(); ++i) { + if (workspaces[i] != workspaces[i - 1]) + grouping.emplace_back(i); + } + grouping.emplace_back(workspaces.size()); + return grouping; +} + +WorkspaceGroup_sptr +createGroup(const std::vector<MatrixWorkspace_sptr> &workspaces) { + WorkspaceGroup_sptr group(new WorkspaceGroup); + for (auto &&workspace : workspaces) + group->addWorkspace(workspace); + return group; +} } // namespace namespace Mantid { @@ -256,9 +305,9 @@ void QENSFitSimultaneous::initConcrete() { "Convolution are output convolved\n" "with corresponding resolution"); - declareProperty(make_unique<WorkspaceProperty<>>("OutputWorkspace", "", - Direction::Output), - "The output result workspace"); + declareProperty(make_unique<WorkspaceProperty<WorkspaceGroup>>( + "OutputWorkspace", "", Direction::Output), + "The output result workspace(s)"); declareProperty(make_unique<WorkspaceProperty<ITableWorkspace>>( "OutputParameterWorkspace", "", Direction::Output, PropertyMode::Optional), @@ -309,10 +358,13 @@ void QENSFitSimultaneous::execConcrete() { convertToSingleDomain(getProperty("Function")); const auto fitResult = performFit(inputWorkspaces, outputBaseName); - const auto parameterWs = processParameterTable( - transposeFitTable(fitResult.first, singleDomainFunction)); + auto transposedTable = + transposeFitTable(fitResult.first, singleDomainFunction); + addQValuesToTableColumn(*transposedTable, workspaces, *this, 0); + const auto parameterWs = processParameterTable(transposedTable); const auto groupWs = makeGroup(fitResult.second); - const auto resultWs = processIndirectFitParameters(parameterWs); + const auto resultWs = processIndirectFitParameters( + parameterWs, createDatasetGrouping(workspaces)); copyLogs(resultWs, workspaces); const bool doExtractMembers = getProperty("ExtractMembers"); @@ -320,7 +372,8 @@ void QENSFitSimultaneous::execConcrete() { extractMembers(groupWs, workspaces, outputBaseName + "_Members"); addAdditionalLogs(resultWs); - copyLogs(resultWs, groupWs); + copyLogs(boost::dynamic_pointer_cast<MatrixWorkspace>(resultWs->getItem(0)), + groupWs); setProperty("OutputWorkspace", resultWs); setProperty("OutputParameterWorkspace", parameterWs); @@ -364,28 +417,40 @@ QENSFitSimultaneous::performFit( return {fit->getProperty("OutputParameters"), outputWS}; } -MatrixWorkspace_sptr QENSFitSimultaneous::processIndirectFitParameters( - ITableWorkspace_sptr parameterWorkspace) { +WorkspaceGroup_sptr QENSFitSimultaneous::processIndirectFitParameters( + ITableWorkspace_sptr parameterWorkspace, + const std::vector<std::size_t> &grouping) { auto pifp = createChildAlgorithm("ProcessIndirectFitParameters", 0.91, 0.95, true); pifp->setProperty("InputWorkspace", parameterWorkspace); pifp->setProperty("ColumnX", "axis-1"); pifp->setProperty("XAxisUnit", "MomentumTransfer"); pifp->setProperty("ParameterNames", getFitParameterNames()); - pifp->setProperty("OutputWorkspace", "__Result"); - pifp->executeAsChildAlg(); - return pifp->getProperty("OutputWorkspace"); + + std::vector<MatrixWorkspace_sptr> results; + results.reserve(grouping.size() - 1); + for (auto i = 0u; i < grouping.size() - 1; ++i) { + pifp->setProperty("StartRowIndex", static_cast<int>(grouping[i])); + pifp->setProperty("EndRowIndex", static_cast<int>(grouping[i + 1]) - 1); + pifp->setProperty("OutputWorkspace", "__Result"); + pifp->executeAsChildAlg(); + results.push_back(pifp->getProperty("OutputWorkspace")); + } + return createGroup(results); } void QENSFitSimultaneous::copyLogs( - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, const std::vector<MatrixWorkspace_sptr> &workspaces) { auto logCopier = createChildAlgorithm("CopyLogs", -1.0, -1.0, false); - logCopier->setProperty("OutputWorkspace", resultWorkspace); - - for (const auto &workspace : workspaces) { - logCopier->setProperty("InputWorkspace", workspace); - logCopier->executeAsChildAlg(); + for (auto &&workspace : *resultWorkspace) { + logCopier->setProperty( + "OutputWorkspace", + boost::dynamic_pointer_cast<MatrixWorkspace>(workspace)); + for (const auto &workspace : workspaces) { + logCopier->setProperty("InputWorkspace", workspace); + logCopier->executeAsChildAlg(); + } } } @@ -421,8 +486,12 @@ void QENSFitSimultaneous::extractMembers( AnalysisDataService::Instance().remove(workspaceName); } -void QENSFitSimultaneous::addAdditionalLogs( - MatrixWorkspace_sptr resultWorkspace) { +void QENSFitSimultaneous::addAdditionalLogs(API::WorkspaceGroup_sptr group) { + for (auto &&workspace : *group) + addAdditionalLogs(workspace); +} + +void QENSFitSimultaneous::addAdditionalLogs(Workspace_sptr resultWorkspace) { auto logAdder = createChildAlgorithm("AddSampleLog", -1.0, -1.0, false); logAdder->setProperty("Workspace", resultWorkspace); diff --git a/Framework/CurveFitting/test/Algorithms/ConvolutionFitSequentialTest.h b/Framework/CurveFitting/test/Algorithms/ConvolutionFitSequentialTest.h index efd677bdf11eb7092b4ff50af1c254e6045c7d21..62ef59bbd5649cf2082b6674bce05d52b731ee46 100644 --- a/Framework/CurveFitting/test/Algorithms/ConvolutionFitSequentialTest.h +++ b/Framework/CurveFitting/test/Algorithms/ConvolutionFitSequentialTest.h @@ -166,23 +166,21 @@ public: // further testing as this is tested in the ProcessIndirectFitParameters // Algorithm ITableWorkspace_sptr paramTable; - TS_ASSERT_THROWS_NOTHING( - paramTable = - AnalysisDataService::Instance().retrieveWS<ITableWorkspace>( - "ReductionWs_conv_1LFixF_s0_to_5_Parameters")); + TS_ASSERT_THROWS_NOTHING(paramTable = getWorkspaceFromADS<ITableWorkspace>( + "ReductionWs_conv_1LFixF_s0_to_5_Parameters")); // Retrieve and analyse results table + WorkspaceGroup_sptr resultGroup; + TS_ASSERT_THROWS_NOTHING(resultGroup = getWorkspaceFromADS<WorkspaceGroup>( + "ReductionWs_conv_1LFixF_s0_to_5_Result")); MatrixWorkspace_sptr resultWs; - TS_ASSERT_THROWS_NOTHING( - resultWs = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>( - "ReductionWs_conv_1LFixF_s0_to_5_Result")); + TS_ASSERT_THROWS_NOTHING(resultWs = getMatrixWorkspace(resultGroup, 0)); TS_ASSERT_EQUALS(resultWs->blocksize(), totalBins); // Retrieve and analyse group table WorkspaceGroup_sptr groupWs; - TS_ASSERT_THROWS_NOTHING( - groupWs = AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( - "ReductionWs_conv_1LFixF_s0_to_5_Workspaces")); + TS_ASSERT_THROWS_NOTHING(groupWs = getWorkspaceFromADS<WorkspaceGroup>( + "ReductionWs_conv_1LFixF_s0_to_5_Workspaces")); // Check number of expected Histograms and Histogram deminsions int entities = groupWs->getNumberOfEntries(); @@ -243,17 +241,14 @@ public: TS_ASSERT(alg.isExecuted()); // Assert that output is in ADS - TS_ASSERT_THROWS_NOTHING( - AnalysisDataService::Instance().retrieveWS<ITableWorkspace>( - "SqwWs_conv_1LFixF_s0_Parameters")); + TS_ASSERT_THROWS_NOTHING(getWorkspaceFromADS<ITableWorkspace>( + "SqwWs_conv_1LFixF_s0_Parameters")); TS_ASSERT_THROWS_NOTHING( - AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>( - "SqwWs_conv_1LFixF_s0_Result")); + getWorkspaceFromADS<WorkspaceGroup>("SqwWs_conv_1LFixF_s0_Result")); TS_ASSERT_THROWS_NOTHING( - AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( - "SqwWs_conv_1LFixF_s0_Workspaces")); + getWorkspaceFromADS<WorkspaceGroup>("SqwWs_conv_1LFixF_s0_Workspaces")); AnalysisDataService::Instance().clear(); } @@ -300,20 +295,17 @@ public: // Check members group workspace was created WorkspaceGroup_const_sptr membersGroupWs; TS_ASSERT_THROWS_NOTHING( - membersGroupWs = - AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( - runName + "_conv_1LFixF_s" + std::to_string(specMin) + "_to_" + - std::to_string(specMax) + "_Members")); + membersGroupWs = getWorkspaceFromADS<WorkspaceGroup>( + runName + "_conv_1LFixF_s" + std::to_string(specMin) + "_to_" + + std::to_string(specMax) + "_Members")); // Check all members have been extracted into their own workspace and // grouped // inside the members group workspace. std::unordered_set<std::string> members = { "Data", "Calc", "Diff", "LinearBackground", "Lorentzian"}; - for (size_t i = 0; i < membersGroupWs->size(); i++) { - MatrixWorkspace_const_sptr ws = - boost::dynamic_pointer_cast<const MatrixWorkspace>( - membersGroupWs->getItem(i)); + for (auto i = 0u; i < membersGroupWs->size(); ++i) { + MatrixWorkspace_const_sptr ws = getMatrixWorkspace(membersGroupWs, i); TS_ASSERT(ws->getNumberHistograms() == specMax - specMin + 1); std::string name = ws->getName(); members.erase(name.substr(name.find_last_of('_') + 1)); @@ -325,6 +317,16 @@ public: //------------------------ Private Functions--------------------------- + template <typename T = MatrixWorkspace> + boost::shared_ptr<T> getWorkspaceFromADS(const std::string &name) { + return AnalysisDataService::Instance().retrieveWS<T>(name); + } + + MatrixWorkspace_sptr getMatrixWorkspace(WorkspaceGroup_const_sptr group, + std::size_t index) { + return boost::dynamic_pointer_cast<MatrixWorkspace>(group->getItem(index)); + } + MatrixWorkspace_sptr loadWorkspace(const std::string &fileName) { Mantid::DataHandling::Load loadAlg; loadAlg.setChild(true); @@ -357,9 +359,7 @@ public: createWorkspace->setProperty("NSpec", 1); createWorkspace->setPropertyValue("OutputWorkspace", wsName); createWorkspace->execute(); - MatrixWorkspace_sptr sqwWS = - AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(wsName); - return sqwWS; + return getWorkspaceFromADS(wsName); } MatrixWorkspace_sptr create2DWorkspace(int xlen, int ylen) { diff --git a/Framework/CurveFitting/test/Algorithms/QENSFitSequentialTest.h b/Framework/CurveFitting/test/Algorithms/QENSFitSequentialTest.h index 108b8dcf44e4118dc55c2e0a67b5b41875d2f42e..9d8e5fa21bfd6f85557facd9bde4a1809e263b12 100644 --- a/Framework/CurveFitting/test/Algorithms/QENSFitSequentialTest.h +++ b/Framework/CurveFitting/test/Algorithms/QENSFitSequentialTest.h @@ -130,7 +130,7 @@ private: AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( outputBaseName + "_Workspaces")); TS_ASSERT_THROWS_NOTHING( - AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>( + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( outputBaseName + "_Result")); TS_ASSERT_EQUALS(groupWorkspace->size(), expectedGroupSize); diff --git a/Framework/CurveFitting/test/Algorithms/QENSFitSimultaneousTest.h b/Framework/CurveFitting/test/Algorithms/QENSFitSimultaneousTest.h index 785727e2c76e0edc50608a774f94a2060e749ecd..f22f93806fa366b4380b40c35841e978ef7e1c3d 100644 --- a/Framework/CurveFitting/test/Algorithms/QENSFitSimultaneousTest.h +++ b/Framework/CurveFitting/test/Algorithms/QENSFitSimultaneousTest.h @@ -132,7 +132,7 @@ private: AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( outputBaseName + "_Workspaces")); TS_ASSERT_THROWS_NOTHING( - AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>( + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( outputBaseName + "_Result")); TS_ASSERT_EQUALS(groupWorkspace->size(), expectedGroupSize); diff --git a/Framework/DataHandling/inc/MantidDataHandling/LoadBankFromDiskTask.h b/Framework/DataHandling/inc/MantidDataHandling/LoadBankFromDiskTask.h index 7e97544392a7bf027ea75f438e178d90d507101a..1f64a4d854a50da0758b6f0e40eb95703c811a32 100644 --- a/Framework/DataHandling/inc/MantidDataHandling/LoadBankFromDiskTask.h +++ b/Framework/DataHandling/inc/MantidDataHandling/LoadBankFromDiskTask.h @@ -55,8 +55,8 @@ public: private: void loadPulseTimes(::NeXus::File &file); void loadEventIndex(::NeXus::File &file, std::vector<uint64_t> &event_index); - void prepareEventId(::NeXus::File &file, size_t &start_event, - size_t &stop_event, std::vector<uint64_t> &event_index); + void prepareEventId(::NeXus::File &file, int64_t &start_event, + int64_t &stop_event, std::vector<uint64_t> &event_index); void loadEventId(::NeXus::File &file); void loadTof(::NeXus::File &file); void loadEventWeights(::NeXus::File &file); @@ -79,9 +79,9 @@ private: /// Old names in the file? bool m_oldNexusFileNames; /// Index to load start at in the file - std::vector<int> m_loadStart; + std::vector<int64_t> m_loadStart; /// How much to load in the file - std::vector<int> m_loadSize; + std::vector<int64_t> m_loadSize; /// Event pixel ID data uint32_t *m_event_id; /// Minimum pixel ID in this data diff --git a/Framework/DataHandling/src/LoadBankFromDiskTask.cpp b/Framework/DataHandling/src/LoadBankFromDiskTask.cpp index 25e44bf91ea335cdb533652839d6016fb886eecc..a3ad8100e1696e4f089b3bc85500afe68c3c8828 100644 --- a/Framework/DataHandling/src/LoadBankFromDiskTask.cpp +++ b/Framework/DataHandling/src/LoadBankFromDiskTask.cpp @@ -114,8 +114,8 @@ void LoadBankFromDiskTask::loadEventIndex(::NeXus::File &file, *the event list for that pulse) */ void LoadBankFromDiskTask::prepareEventId(::NeXus::File &file, - size_t &start_event, - size_t &stop_event, + int64_t &start_event, + int64_t &stop_event, std::vector<uint64_t> &event_index) { // Get the list of pixel ID's if (m_oldNexusFileNames) @@ -129,17 +129,17 @@ void LoadBankFromDiskTask::prepareEventId(::NeXus::File &file, // dims[0] can be negative in ISIS meaning 2^32 + dims[0]. Take that into // account int64_t dim0 = recalculateDataSize(id_info.dims[0]); - stop_event = static_cast<size_t>(dim0); + stop_event = dim0; // Handle the time filtering by changing the start/end offsets. for (size_t i = 0; i < thisBankPulseTimes->numPulses; i++) { if (thisBankPulseTimes->pulseTimes[i] >= m_loader.alg->filter_time_start) { - start_event = event_index[i]; + start_event = static_cast<int64_t>(event_index[i]); break; // stop looking } } - if (start_event > static_cast<size_t>(dim0)) { + if (start_event > dim0) { // If the frame indexes are bad then we can't construct the times of the // events properly and filtering by time // will not work on this data @@ -150,7 +150,7 @@ void LoadBankFromDiskTask::prepareEventId(::NeXus::File &file, << "All events will appear in the same frame and filtering by time " "will not be possible on this data.\n"; start_event = 0; - stop_event = static_cast<size_t>(dim0); + stop_event = dim0; } else { for (size_t i = 0; i < thisBankPulseTimes->numPulses; i++) { if (thisBankPulseTimes->pulseTimes[i] > m_loader.alg->filter_time_stop) { @@ -162,14 +162,16 @@ void LoadBankFromDiskTask::prepareEventId(::NeXus::File &file, // We are loading part - work out the event number range if (m_loader.chunk != EMPTY_INT()) { start_event = - (m_loader.chunk - m_loader.firstChunkForBank) * m_loader.eventsPerChunk; + static_cast<int64_t>(m_loader.chunk - m_loader.firstChunkForBank) * + static_cast<int64_t>(m_loader.eventsPerChunk); // Don't change stop_event for the final chunk - if (start_event + m_loader.eventsPerChunk < stop_event) - stop_event = start_event + m_loader.eventsPerChunk; + if (start_event + static_cast<int64_t>(m_loader.eventsPerChunk) < + stop_event) + stop_event = start_event + static_cast<int64_t>(m_loader.eventsPerChunk); } // Make sure it is within range - if (stop_event > static_cast<size_t>(dim0)) + if (stop_event > dim0) stop_event = dim0; m_loader.alg->getLogger().debug() << entry_name << ": start_event " @@ -375,19 +377,22 @@ void LoadBankFromDiskTask::run() { "and the number of pulse times in event_time_zero.\n"; // Open and validate event_id field. - size_t start_event = 0; - size_t stop_event = 0; + int64_t start_event = 0; + int64_t stop_event = 0; this->prepareEventId(file, start_event, stop_event, event_index); // These are the arguments to getSlab() - m_loadStart[0] = static_cast<int>(start_event); - m_loadSize[0] = static_cast<int>(stop_event - start_event); + m_loadStart[0] = start_event; + m_loadSize[0] = stop_event - start_event; if ((m_loadSize[0] > 0) && (m_loadStart[0] >= 0)) { // Load pixel IDs this->loadEventId(file); - if (m_loader.alg->getCancel()) + if (m_loader.alg->getCancel()) { + m_loader.alg->getLogger().error() << "Loading bank " << entry_name + << " is cancelled.\n"; m_loadError = true; // To allow cancelling the algorithm + } // And TOF. if (!m_loadError) { @@ -399,6 +404,11 @@ void LoadBankFromDiskTask::run() { } // Size is at least 1 else { // Found a size that was 0 or less; stop processing + m_loader.alg->getLogger().error() + << "Loading bank " << entry_name + << " is stopped due to either zero/negative loading size (" + << m_loadStart[0] << ") or negative load start index (" + << m_loadStart[0] << ")\n"; m_loadError = true; } @@ -470,8 +480,8 @@ void LoadBankFromDiskTask::run() { mid_id = (m_max_id + m_min_id) / 2; // No error? Launch a new task to process that data. - size_t numEvents = m_loadSize[0]; - size_t startAt = m_loadStart[0]; + size_t numEvents = static_cast<size_t>(m_loadSize[0]); + size_t startAt = static_cast<size_t>(m_loadStart[0]); // convert things to shared_arrays boost::shared_array<uint32_t> event_id_shrd(m_event_id); diff --git a/Framework/Kernel/inc/MantidKernel/DataService.h b/Framework/Kernel/inc/MantidKernel/DataService.h index 3d6de5faf49853b46c7b000922b295f1dc3808d4..5c0d36df6f609e01dae9cceb2874d532500cb13d 100644 --- a/Framework/Kernel/inc/MantidKernel/DataService.h +++ b/Framework/Kernel/inc/MantidKernel/DataService.h @@ -482,15 +482,22 @@ public: } /// Get a vector of the pointers to the data objects stored by the service - std::vector<boost::shared_ptr<T>> getObjects() const { + std::vector<boost::shared_ptr<T>> + getObjects(DataServiceHidden includeHidden = DataServiceHidden::Auto) const { std::lock_guard<std::recursive_mutex> _lock(m_mutex); - const bool showingHidden = showingHiddenObjects(); + const bool alwaysIncludeHidden = + includeHidden == DataServiceHidden::Include; + const bool usingAuto = + includeHidden == DataServiceHidden::Auto && showingHiddenObjects(); + + const bool showingHidden = alwaysIncludeHidden || usingAuto; + std::vector<boost::shared_ptr<T>> objects; objects.reserve(datamap.size()); - for (auto it = datamap.begin(); it != datamap.end(); ++it) { - if (showingHidden || !isHiddenDataServiceObject(it->first)) { - objects.push_back(it->second); + for (const auto &it : datamap) { + if (showingHidden || !isHiddenDataServiceObject(it.first)) { + objects.push_back(it.second); } } return objects; diff --git a/Framework/Kernel/test/DataServiceTest.h b/Framework/Kernel/test/DataServiceTest.h index 6dfa57de8dd36099f36898365854f1ebd3416ddb..1a617d45c6db2923f49da2ac75347bc109f15e46 100644 --- a/Framework/Kernel/test/DataServiceTest.h +++ b/Framework/Kernel/test/DataServiceTest.h @@ -6,6 +6,8 @@ #include <cxxtest/TestSuite.h> #include <Poco/NObserver.h> #include <boost/make_shared.hpp> + +#include <sstream> #include <mutex> using namespace Mantid; @@ -327,6 +329,89 @@ public: TS_ASSERT_EQUALS(objects.at(std::distance(names.cbegin(), cit)), three); } + void test_getObjectsReturnsConfigOption() { + svc.clear(); + + ConfigService::Instance().setString("MantidOptions.InvisibleWorkspaces", + "0"); + auto one = boost::make_shared<int>(1); + auto two = boost::make_shared<int>(2); + auto three = boost::make_shared<int>(3); + svc.add("One", one); + svc.add("Two", two); + svc.add("TwoAgain", + two); // Same pointer again - should appear twice in getObjects() + svc.add("__Three", three); + + auto objects = svc.getObjects(DataServiceHidden::Auto); + auto objectsDefaultCall = svc.getObjects(); + + TSM_ASSERT_EQUALS("Default parameter is not set to auto", objects, + objectsDefaultCall); + + TSM_ASSERT_EQUALS("Hidden entries should not be returned", objects.size(), + 3); + // Check the hidden ws isn't present + TS_ASSERT_EQUALS(objects.at(0), one); + TS_ASSERT_EQUALS(objects.at(1), two); + TS_ASSERT_EQUALS(objects.at(2), two); + TSM_ASSERT_EQUALS("Hidden entries should not be returned", + std::find(objects.cbegin(), objects.cend(), three), + objects.end()); + + ConfigService::Instance().setString("MantidOptions.InvisibleWorkspaces", + "1"); + objects = svc.getObjects(); + TS_ASSERT_EQUALS(objects.size(), 4); + } + + void test_getObjectsReturnsNoHiddenOption() { + svc.clear(); + + ConfigService::Instance().setString("MantidOptions.InvisibleWorkspaces", + "1"); + auto one = boost::make_shared<int>(1); + auto two = boost::make_shared<int>(2); + auto three = boost::make_shared<int>(3); + svc.add("One", one); + svc.add("Two", two); + svc.add("__Three", three); + + auto objects = svc.getObjects(DataServiceHidden::Exclude); + TSM_ASSERT_EQUALS("Hidden entries should not be returned", objects.size(), + 2); + // Check the hidden ws isn't present + TS_ASSERT_EQUALS(objects.at(0), one); + TS_ASSERT_EQUALS(objects.at(1), two); + TSM_ASSERT_EQUALS("Hidden entries should not be returned", + std::find(objects.cbegin(), objects.cend(), three), + objects.end()); + } + + void test_getObjectsReturnsHiddenOption() { + svc.clear(); + + ConfigService::Instance().setString("MantidOptions.InvisibleWorkspaces", + "0"); + auto one = boost::make_shared<int>(1); + auto two = boost::make_shared<int>(2); + auto three = boost::make_shared<int>(3); + svc.add("One", one); + svc.add("Two", two); + svc.add("__Three", three); + + auto objects = svc.getObjects(DataServiceHidden::Include); + TSM_ASSERT_EQUALS("Hidden entries should be returned", objects.size(), 3); + // Check the hidden ws isn't present + TS_ASSERT_DIFFERS(std::find(objects.begin(), objects.end(), one), + objects.end()); + TS_ASSERT_DIFFERS(std::find(objects.begin(), objects.end(), two), + objects.end()); + TSM_ASSERT_DIFFERS("Hidden entries should be returned", + std::find(objects.cbegin(), objects.cend(), three), + objects.end()); + } + void test_sortedAndHiddenGetNames() { auto one = boost::make_shared<int>(1); auto two = boost::make_shared<int>(2); diff --git a/Framework/MDAlgorithms/src/ConvertMDHistoToMatrixWorkspace.cpp b/Framework/MDAlgorithms/src/ConvertMDHistoToMatrixWorkspace.cpp index b780964eb5adae2ef0c072b12afe32d534fff444..6c307cbffe0d92347bf1ba3ae1aa308d74ae678f 100644 --- a/Framework/MDAlgorithms/src/ConvertMDHistoToMatrixWorkspace.cpp +++ b/Framework/MDAlgorithms/src/ConvertMDHistoToMatrixWorkspace.cpp @@ -6,7 +6,7 @@ #include "MantidAPI/IMDHistoWorkspace.h" #include "MantidAPI/MatrixWorkspace.h" #include "MantidAPI/NullCoordTransform.h" -#include "MantidAPI/NumericAxis.h" +#include "MantidAPI/BinEdgeAxis.h" #include "MantidAPI/WorkspaceFactory.h" #include "MantidHistogramData/LinearGenerator.h" @@ -320,8 +320,8 @@ void ConvertMDHistoToMatrixWorkspace::make2DWorkspace() { outputWorkspace->getAxis(0)->unit() = labelX; // set the second axis - auto yAxis = new NumericAxis(ny); - for (size_t i = 0; i < ny; ++i) { + auto yAxis = new BinEdgeAxis(ny + 1); + for (size_t i = 0; i <= ny; ++i) { yAxis->setValue(i, yDim->getX(i)); } auto labelY = boost::dynamic_pointer_cast<Kernel::Units::Label>( diff --git a/Framework/PythonInterface/mantid/api/src/Exports/IPeaksWorkspace.cpp b/Framework/PythonInterface/mantid/api/src/Exports/IPeaksWorkspace.cpp index b62e9d191165eff5f547dde00b2bffe9ea729555..f18962637f07ab20b4ac2b05697b4a3fc0b38461 100644 --- a/Framework/PythonInterface/mantid/api/src/Exports/IPeaksWorkspace.cpp +++ b/Framework/PythonInterface/mantid/api/src/Exports/IPeaksWorkspace.cpp @@ -1,18 +1,21 @@ #include "MantidAPI/IPeaksWorkspace.h" #include "MantidAPI/Run.h" #include "MantidGeometry/Crystal/IPeak.h" -#include "MantidPythonInterface/kernel/GetPointer.h" #include "MantidPythonInterface/kernel/Converters/PyObjectToV3D.h" +#include "MantidPythonInterface/kernel/GetPointer.h" #include "MantidPythonInterface/kernel/Registry/RegisterWorkspacePtrToPython.h" -#include <boost/python/class.hpp> -#include <boost/python/return_internal_reference.hpp> +#include <boost/none.hpp> #include <boost/optional.hpp> +#include <boost/python/class.hpp> #include <boost/python/manage_new_object.hpp> +#include <boost/python/return_internal_reference.hpp> +using namespace boost::python; using namespace Mantid::Geometry; using namespace Mantid::API; +using Mantid::API::Column_sptr; +using Mantid::Kernel::V3D; using Mantid::PythonInterface::Registry::RegisterWorkspacePtrToPython; -using namespace boost::python; GET_POINTER_SPECIALIZATION(IPeaksWorkspace) @@ -39,6 +42,158 @@ IPeak *createPeakQLabWithDistance(IPeaksWorkspace &self, const object &data, } /// Create a peak via it's QLab value from a list or numpy array void addPeak(IPeaksWorkspace &self, const IPeak &peak) { self.addPeak(peak); } + +/** + * PeakWorkspaceTableAdaptor + * + * A class to map PeaksWorkspace column names to IPeak setter functions. This + * will handle the unmarshalling of the bpl::object type before passing it to + * the appropriate setter method. + */ +class PeakWorkspaceTableAdaptor { +public: + /** + * Create a PeakWorkspaceTableAdaptor + * + * @param peaksWorkspace reference to a peaks workspace to convert values for. + */ + explicit PeakWorkspaceTableAdaptor(IPeaksWorkspace &peaksWorkspace) + : m_peaksWorkspace(peaksWorkspace) { + // Create a map of string -> setter functions + // Each function will extract the given value from the passed python type. + m_setterMap = {{"RunNumber", setterFunction(&IPeak::setRunNumber)}, + {"DetID", setterFunction(&IPeak::setDetectorID)}, + {"h", setterFunction(&IPeak::setH)}, + {"k", setterFunction(&IPeak::setK)}, + {"l", setterFunction(&IPeak::setL)}, + {"Wavelength", setterFunction(&IPeak::setWavelength)}, + {"Intens", setterFunction(&IPeak::setIntensity)}, + {"SigInt", setterFunction(&IPeak::setSigmaIntensity)}, + {"BinCount", setterFunction(&IPeak::setBinCount)}, + {"PeakNumber", setterFunction(&IPeak::setPeakNumber)}, + {"QLab", setterFunction(&IPeak::setQLabFrame)}, + {"QSample", setterFunction(&IPeak::setQSampleFrame)}}; + } + + /** + * Set the value of a PeaksWorkspace at the given column name and row index. + * + * @param columnName name of the column to set a value for. + * @param rowIndex index of the peak to set the value for. + * @param value value to set the property to. + */ + void setProperty(const std::string &columnName, const int rowIndex, + object value) { + auto &peak = m_peaksWorkspace.getPeak(rowIndex); + if (m_setterMap.find(columnName) == m_setterMap.end()) { + throw std::runtime_error(columnName + + " is a read only column of a peaks workspace"); + } + m_setterMap[columnName](peak, value); + } + +private: + // type alias for the member function to wrap + template <typename T> using MemberFunc = void (IPeak::*)(T value); + // special type alias for V3D functions that take an addtional parameter + using MemberFuncV3D = void (IPeak::*)(const V3D &value, + boost::optional<double>); + // type alias for the setter function + using SetterType = std::function<void(IPeak &peak, const object)>; + + /** + * Wrap a setter function on a IPeak with the bpl::extract function. + * + * @param func A pointer to a member function to wrap. + * @return a setter function wrapped with the bpl::extract function for the + * setter's value type + */ + template <typename T> SetterType setterFunction(MemberFunc<T> func) { + return [func](IPeak &peak, const object value) { + extract<T> extractor{value}; + if (!extractor.check()) { + throw std::runtime_error( + "Cannot set value. Value was not of the expected type!"); + } + (peak.*func)(extractor()); + }; + } + + /** + * Wrap a setter function on a IPeak with the bpl::extract function. + * + * This is a specilization of the templated function to handle the + * 2 parameter signature of V3D setter functions. + * + * @param func A pointer to a member function to wrap. + * @return a setter function wrapped with the bpl::extract function for the + * setter's value type + */ + SetterType setterFunction(MemberFuncV3D func) { + return [func](IPeak &peak, const object value) { + extract<const V3D &> extractor{value}; + if (!extractor.check()) { + throw std::runtime_error( + "Cannot set value. Value was not of the expected type!"); + } + (peak.*func)(extractor(), boost::none); + }; + } + + // The PeaksWorkspace we need to map value to. + IPeaksWorkspace &m_peaksWorkspace; + // Map of string value to setter functions. + std::unordered_map<std::string, SetterType> m_setterMap; +}; + +/** + * Get the row index and column name from python types. + * + * @param self A reference to the PeaksWorkspace python object that we were + * called on + * @param col_or_row A python object containing either a row index or a column + * name + * @param row_or_col An integer giving the row if value is a string or the + * column if value is an index + */ +std::pair<int, std::string> getRowAndColumnName(IPeaksWorkspace &self, + const object &col_or_row, + const int row_or_col) { + extract<std::string> columnNameExtractor{col_or_row}; + std::string columnName; + int rowIndex; + + if (columnNameExtractor.check()) { + columnName = columnNameExtractor(); + rowIndex = row_or_col; + } else { + rowIndex = extract<int>(col_or_row)(); + const auto colIndex = row_or_col; + const auto columnNames = self.getColumnNames(); + columnName = columnNames.at(colIndex); + } + + return std::make_pair(rowIndex, columnName); +} + +/** + * Sets the value of the given cell + * @param self A reference to the PeaksWorkspace python object that we were + * called on + * @param value A python object containing either a row index or a column name + * @param row_or_col An integer giving the row if value is a string or the + * column if value is an index + */ +void setCell(IPeaksWorkspace &self, const object &col_or_row, + const int row_or_col, const object &value) { + std::string columnName; + int rowIndex; + std::tie(rowIndex, columnName) = + getRowAndColumnName(self, col_or_row, row_or_col); + + PeakWorkspaceTableAdaptor tableMap{self}; + tableMap.setProperty(columnName, rowIndex, value); +} } void export_IPeaksWorkspace() { @@ -72,7 +227,12 @@ void export_IPeaksWorkspace() { "Return the Run object for this workspace") .def("peakInfoNumber", &IPeaksWorkspace::peakInfoNumber, (arg("self"), arg("qlab_frame"), arg("lab_coordinate")), - "Peak info number at Q vector for this workspace"); + "Peak info number at Q vector for this workspace") + .def("setCell", &setCell, (arg("self"), arg("row_or_column"), + arg("column_or_row"), arg("value")), + "Sets the value of a given cell. If the row_or_column argument is a " + "number then it is interpreted as a row otherwise it " + "is interpreted as a column name."); //------------------------------------------------------------------------------------------------- diff --git a/Framework/PythonInterface/test/python/mantid/api/IPeaksWorkspaceTest.py b/Framework/PythonInterface/test/python/mantid/api/IPeaksWorkspaceTest.py index 1d1576d9c119d0041b6e05230db576f550b01b5b..b34a6e675d6ea7a128a3575b31e5489955d76e80 100644 --- a/Framework/PythonInterface/test/python/mantid/api/IPeaksWorkspaceTest.py +++ b/Framework/PythonInterface/test/python/mantid/api/IPeaksWorkspaceTest.py @@ -54,15 +54,15 @@ class IPeaksWorkspaceTest(unittest.TestCase): # Peaks workspace will not be integrated by default. self.assertTrue(not pws.hasIntegratedPeaks()) - + def test_createPeakHKL(self): pws = WorkspaceCreationHelper.createPeaksWorkspace(0, True) lattice = pws.mutableSample().getOrientedLattice() - + # Simple test that the creational method is exposed p = pws.createPeakHKL([1,1,1]) self.assertTrue(IPeak != None) - + def test_peak_setQLabFrame(self): pws = WorkspaceCreationHelper.createPeaksWorkspace(1, True) p = pws.getPeak(0) @@ -74,7 +74,7 @@ class IPeaksWorkspaceTest(unittest.TestCase): self.assertAlmostEquals( p.getQLabFrame().X(), 1.0, places=10) self.assertAlmostEquals( p.getQLabFrame().Y(), 1.0, places=10) self.assertAlmostEquals( p.getQLabFrame().Z(), 1.0, places=10) - + try: p.setQLabFrame(V3D(1,1,1), 1) except Exception: @@ -82,7 +82,7 @@ class IPeaksWorkspaceTest(unittest.TestCase): self.assertAlmostEquals( p.getQLabFrame().X(), 1.0, places=10) self.assertAlmostEquals( p.getQLabFrame().Y(), 1.0, places=10) self.assertAlmostEquals( p.getQLabFrame().Z(), 1.0, places=10) - + def test_peak_setQSampleFrame(self): pws = WorkspaceCreationHelper.createPeaksWorkspace(1, True) p = pws.getPeak(0) @@ -95,7 +95,7 @@ class IPeaksWorkspaceTest(unittest.TestCase): self.assertAlmostEquals( p.getQSampleFrame().X(), 1.0, places=10) self.assertAlmostEquals( p.getQSampleFrame().Y(), 1.0, places=10) self.assertAlmostEquals( p.getQSampleFrame().Z(), 1.0, places=10) - + try: p.setQSampleFrame(V3D(1,1,1), 1) except Exception: @@ -103,8 +103,20 @@ class IPeaksWorkspaceTest(unittest.TestCase): self.assertAlmostEquals( p.getQSampleFrame().X(), 1.0, places=10) self.assertAlmostEquals( p.getQSampleFrame().Y(), 1.0, places=10) self.assertAlmostEquals( p.getQSampleFrame().Z(), 1.0, places=10) - - + + def test_setCell_with_column_name(self): + pws = WorkspaceCreationHelper.createPeaksWorkspace(1, True) + pws.setCell("h", 0, 1) + pws.setCell("k", 0, 2) + pws.setCell("l", 0, 3) + pws.setCell("QLab", 0, V3D(1,1,1)) + pws.setCell("QSample", 0, V3D(1,1,1)) + + self.assertEquals(pws.cell("h", 0), 1) + self.assertEquals(pws.cell("k", 0), 2) + self.assertEquals(pws.cell("l", 0), 3) + self.assertEquals(pws.cell("QLab", 0), V3D(1,1,1)) + self.assertEquals(pws.cell("QSample", 0), V3D(1,1,1)) if __name__ == '__main__': diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py index 98a1a4a7bb538bca98b5ad46a5e1b47acbd1ce43..8e4c84e8d24ab2c15a2dc9c5be293bdc20a5d8a3 100644 --- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py +++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/IqtFitSequentialTest.py @@ -19,18 +19,18 @@ class IqtFitSequentialTest(unittest.TestCase): def _validate_output(self, params, result, fit_group): self.assertTrue(isinstance(params, ITableWorkspace)) - self.assertTrue(isinstance(result, MatrixWorkspace)) + self.assertTrue(isinstance(result, WorkspaceGroup)) self.assertTrue(isinstance(fit_group, WorkspaceGroup)) self._validate_table_shape(params) - self._validate_matrix_shape(result) + self._validate_matrix_shape(result.getItem(0)) self._validate_group_shape(fit_group) self._validate_table_values(params) - self._validate_matrix_values(result) + self._validate_matrix_values(result.getItem(0)) self._validate_group_values(fit_group) - self._validate_sample_log_values(result) + self._validate_sample_log_values(result.getItem(0)) self._validate_sample_log_values(fit_group.getItem(0)) diff --git a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ProcessIndirectFitParameters.h b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ProcessIndirectFitParameters.h index 7b059f77710a39b3753e2dc3ce8496bc82418739..7ba897f737cbdc6315d8b2babeadfb3602d2bcac 100644 --- a/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ProcessIndirectFitParameters.h +++ b/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/ProcessIndirectFitParameters.h @@ -1,9 +1,9 @@ #ifndef MANTID_ALGORITHMS_PROCESSINDIRECTFITPARAMETERS_H_ #define MANTID_ALGORITHMS_PROCESSINDIRECTFITPARAMETERS_H_ -#include "MantidKernel/System.h" #include "MantidAPI/Algorithm.h" #include "MantidAPI/MatrixWorkspace.h" +#include "MantidKernel/System.h" namespace Mantid { namespace Algorithms { @@ -46,10 +46,8 @@ public: private: void init() override; void exec() override; - std::vector<std::string> searchForFitParams(const std::string &, - const std::vector<std::string> &); - std::vector<std::vector<API::MatrixWorkspace_sptr>> - reorder2DVector(std::vector<std::vector<API::MatrixWorkspace_sptr>> &); + std::size_t getStartRow() const; + std::size_t getEndRow(std::size_t maximum) const; }; } // namespace Algorithms } // namespace Mantid diff --git a/Framework/WorkflowAlgorithms/src/MuonProcess.cpp b/Framework/WorkflowAlgorithms/src/MuonProcess.cpp index 661315ebbba1070a87e09599a703ddc8251ac783..c5d5fda5b1c75e485ae777f5962a6a91f8c303c7 100644 --- a/Framework/WorkflowAlgorithms/src/MuonProcess.cpp +++ b/Framework/WorkflowAlgorithms/src/MuonProcess.cpp @@ -116,8 +116,9 @@ void MuonProcess::init() { "OutputWorkspace", "", Direction::Output), "An output workspace."); - declareProperty("CropWorkspace", true, - "Determines if the input workspace should be cropped"); + declareProperty("CropWorkspace", true, "Determines if the input workspace " + "should be cropped at Xmax, Xmin is " + "still aplied."); declareProperty("WorkspaceName", "", "The name of the input workspace"); } @@ -300,25 +301,23 @@ MatrixWorkspace_sptr MuonProcess::correctWorkspace(MatrixWorkspace_sptr ws, ws = changeOffset->getProperty("OutputWorkspace"); } - bool toCrop = getProperty("CropWorkspace"); - if (toCrop) { - // Crop workspace, if need to - double Xmin = getProperty("Xmin"); - double Xmax = getProperty("Xmax"); - if (Xmin != EMPTY_DBL() || Xmax != EMPTY_DBL()) { - IAlgorithm_sptr crop = createChildAlgorithm("CropWorkspace"); - crop->setProperty("InputWorkspace", ws); - - if (Xmin != EMPTY_DBL()) - crop->setProperty("Xmin", Xmin); - + // Crop workspace, if need to + double Xmin = getProperty("Xmin"); + double Xmax = getProperty("Xmax"); + if (Xmin != EMPTY_DBL() || Xmax != EMPTY_DBL()) { + IAlgorithm_sptr crop = createChildAlgorithm("CropWorkspace"); + crop->setProperty("InputWorkspace", ws); + + if (Xmin != EMPTY_DBL()) + crop->setProperty("Xmin", Xmin); + bool toCrop = getProperty("CropWorkspace"); + if (toCrop) { if (Xmax != EMPTY_DBL()) crop->setProperty("Xmax", Xmax); - - crop->execute(); - - ws = crop->getProperty("OutputWorkspace"); } + crop->execute(); + + ws = crop->getProperty("OutputWorkspace"); } // Rebin workspace if need to diff --git a/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp b/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp index ec98c9067d7fdfdb14a9bbac37d50c1b46be519e..4ea947cb3fc54ecaa0c025ce8061edfeb6909372 100644 --- a/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp +++ b/Framework/WorkflowAlgorithms/src/ProcessIndirectFitParameters.cpp @@ -1,14 +1,179 @@ #include "MantidWorkflowAlgorithms/ProcessIndirectFitParameters.h" +#include "MantidAPI/AlgorithmManager.h" #include "MantidAPI/ITableWorkspace.h" #include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/TableRow.h" #include "MantidAPI/TextAxis.h" #include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/BoundedValidator.h" #include "MantidKernel/ListValidator.h" #include "MantidKernel/MandatoryValidator.h" #include "MantidKernel/UnitFactory.h" +#include <boost/algorithm/string/predicate.hpp> + +namespace { +using namespace Mantid::API; + +template <typename T, typename... Ts> +std::vector<T, Ts...> repeat(std::vector<T, Ts...> const &vec, std::size_t n) { + std::vector<T, Ts...> result; + result.reserve(vec.size() * n); + for (; n > 0; --n) + result.insert(result.end(), vec.begin(), vec.end()); + return result; +} + +std::vector<std::string> appendSuffix(std::vector<std::string> const &vec, + std::string const &suffix) { + std::vector<std::string> appended; + appended.reserve(vec.size()); + for (auto &&str : vec) + appended.emplace_back(str + suffix); + return appended; +} + +MatrixWorkspace_sptr +createWorkspace(std::vector<double> const &x, std::vector<double> const &y, + std::vector<double> const &e, int numberOfSpectra, + std::vector<std::string> const &verticalAxisNames, + std::string const &unitX) { + auto createWorkspaceAlgorithm = + AlgorithmManager::Instance().createUnmanaged("CreateWorkspace"); + createWorkspaceAlgorithm->initialize(); + createWorkspaceAlgorithm->setChild(true); + createWorkspaceAlgorithm->setLogging(false); + createWorkspaceAlgorithm->setProperty("DataX", x); + createWorkspaceAlgorithm->setProperty("DataY", y); + createWorkspaceAlgorithm->setProperty("DataE", e); + createWorkspaceAlgorithm->setProperty("NSpec", numberOfSpectra); + createWorkspaceAlgorithm->setProperty("VerticalAxisUnit", "Text"); + createWorkspaceAlgorithm->setProperty("VerticalAxisValues", + verticalAxisNames); + createWorkspaceAlgorithm->setProperty("UnitX", unitX); + createWorkspaceAlgorithm->setProperty("OutputWorkspace", "__created"); + createWorkspaceAlgorithm->execute(); + return createWorkspaceAlgorithm->getProperty("OutputWorkspace"); +} + +template <typename T, typename OutputIterator> +void extractColumnValues(Column const &column, std::size_t startRow, + std::size_t endRow, OutputIterator outputIt) { + for (auto i = startRow; i <= endRow; ++i) + *outputIt++ = column.cell<T>(i); +} + +template <typename T, typename OutputIterator> +void extractValuesFromColumns(std::size_t startRow, std::size_t endRow, + std::vector<Column_const_sptr> columns, + OutputIterator outputIt) { + for (auto &&column : columns) + extractColumnValues<T>(*column, startRow, endRow, outputIt); +} + +template <typename T> +std::vector<T> getColumnValues(Column const &column, std::size_t startRow, + std::size_t endRow) { + std::vector<T> values; + values.reserve(1 + (endRow - startRow)); + extractColumnValues<T>(column, startRow, endRow, std::back_inserter(values)); + return values; +} + +std::string getColumnName(Column_const_sptr column) { return column->name(); } + +std::vector<std::string> +extractColumnNames(std::vector<Column_const_sptr> const &columns) { + std::vector<std::string> names; + names.reserve(columns.size()); + std::transform(columns.begin(), columns.end(), std::back_inserter(names), + getColumnName); + return names; +} + +template <typename ColumnFilter> +std::vector<Column_const_sptr> extractColumns(ITableWorkspace const *table, + ColumnFilter const &filter) { + std::vector<Column_const_sptr> columns; + for (auto i = 0u; i < table->columnCount(); ++i) { + auto const column = table->getColumn(i); + if (filter(*column)) + columns.emplace_back(column); + } + return columns; +} + +struct TableToMatrixWorkspaceConverter { + template <typename YFilter, typename EFilter> + TableToMatrixWorkspaceConverter(ITableWorkspace const *table, + std::vector<double> const &x, + YFilter const &yFilter, + EFilter const &eFilter) + : m_x(x), m_yColumns(extractColumns(table, yFilter)), + m_eColumns(extractColumns(table, eFilter)), + m_yAxis(extractColumnNames(m_yColumns)) {} + + MatrixWorkspace_sptr operator()(std::size_t startRow, std::size_t endRow, + std::string const &unitX) const { + auto const x = repeat(m_x, m_yColumns.size()); + + std::vector<double> y; + std::vector<double> e; + y.reserve(x.size()); + e.reserve(x.size()); + extractValuesFromColumns<double>(startRow, endRow, m_yColumns, + std::back_inserter(y)); + extractValuesFromColumns<double>(startRow, endRow, m_eColumns, + std::back_inserter(e)); + + return createWorkspace(x, y, e, static_cast<int>(m_yColumns.size()), + m_yAxis, unitX); + } + +private: + std::vector<double> const m_x; + std::vector<Column_const_sptr> const m_yColumns; + std::vector<Column_const_sptr> const m_eColumns; + std::vector<std::string> const m_yAxis; +}; + +struct EndsWithOneOf { + explicit EndsWithOneOf(std::vector<std::string> &&strings) + : m_strings(std::move(strings)) {} + + bool operator()(std::string const &value) const { + for (auto &&str : m_strings) { + if (boost::algorithm::ends_with(value, str)) + return true; + } + return false; + } + +private: + std::vector<std::string> const m_strings; +}; + +template <typename StringFilter> struct ColumnNameFilter { +public: + explicit ColumnNameFilter(StringFilter &&filter) + : m_filter(std::forward<StringFilter>(filter)) {} + + bool operator()(Column const &column) const { + return m_filter(column.name()); + } + +private: + StringFilter const m_filter; +}; + +template <typename StringFilter> +ColumnNameFilter<StringFilter> makeColumnNameFilter(StringFilter &&filter) { + return ColumnNameFilter<StringFilter>(std::forward<StringFilter>(filter)); +} +} // namespace + namespace Mantid { namespace Algorithms { @@ -65,6 +230,15 @@ void ProcessIndirectFitParameters::init() { boost::make_shared<StringListValidator>(unitOptions), "The unit to assign to the X Axis"); + auto positiveInt = boost::make_shared<Kernel::BoundedValidator<int>>(); + positiveInt->setLower(0); + declareProperty( + "StartRowIndex", EMPTY_INT(), positiveInt, + "The start row index to include in the output matrix workspace."); + declareProperty( + "EndRowIndex", EMPTY_INT(), positiveInt, + "The end row index to include in the output matrix workspace."); + declareProperty(make_unique<WorkspaceProperty<MatrixWorkspace>>( "OutputWorkspace", "", Direction::Output), "The name to give the output workspace"); @@ -74,164 +248,34 @@ void ProcessIndirectFitParameters::init() { /** Execute the algorithm. */ void ProcessIndirectFitParameters::exec() { - // Get Properties ITableWorkspace_sptr inputWs = getProperty("InputWorkspace"); std::string xColumn = getProperty("ColumnX"); - std::vector<std::string> parameterNames = getProperty("ParameterNames"); std::string xUnit = getProperty("XAxisUnit"); - MatrixWorkspace_sptr outputWs = getProperty("OutputWorkspace"); - - // Search for any parameters in the table with the given parameter names, - // ignoring their function index and output them to a workspace - auto workspaces = std::vector<std::vector<MatrixWorkspace_sptr>>(); - const size_t totalNames = parameterNames.size(); - Progress tblSearchProg = Progress(this, 0.0, 0.5, totalNames * 2); - for (size_t i = 0; i < totalNames; i++) { - tblSearchProg.report("Splitting table into relevant columns"); - auto const allColumnNames = inputWs->getColumnNames(); - auto columns = searchForFitParams(parameterNames.at(i), allColumnNames); - auto errColumns = - searchForFitParams((parameterNames.at(i) + "_Err"), allColumnNames); - - auto paramWorkspaces = std::vector<MatrixWorkspace_sptr>(); - size_t min = columns.size(); - if (errColumns.size() < min) { - min = errColumns.size(); - } - auto convertToMatrix = - createChildAlgorithm("ConvertTableToMatrixWorkspace", -1, -1, true); - tblSearchProg.report("Converting Column to Matrix"); - for (size_t j = 0; j < min; j++) { - convertToMatrix->setProperty("InputWorkspace", inputWs); - convertToMatrix->setProperty("ColumnX", xColumn); - convertToMatrix->setProperty("ColumnY", columns.at(j)); - convertToMatrix->setProperty("ColumnE", errColumns.at(j)); - convertToMatrix->setProperty("OutputWorkspace", columns.at(j)); - convertToMatrix->executeAsChildAlg(); - paramWorkspaces.push_back( - convertToMatrix->getProperty("OutputWorkspace")); - } - workspaces.push_back(std::move(paramWorkspaces)); - } - - Progress workflowProg = Progress(this, 0.5, 1.0, 10); - // Transpose list of workspaces, ignoring unequal length of lists - // this handles the case where a parameter occurs only once in the whole - // workspace - workflowProg.report("Reordering workspace vector"); - workspaces = reorder2DVector(workspaces); - - // Get output workspace columns - auto outputSpectraNames = std::vector<std::string>(); - for (const auto ¶mWorkspaces : workspaces) { - for (const auto &workspace : paramWorkspaces) { - outputSpectraNames.push_back(workspace->YUnitLabel()); - } - } - - // Join all the parameters for each peak into a single workspace per peak - auto tempWorkspaces = std::vector<MatrixWorkspace_sptr>(); - auto conjoin = createChildAlgorithm("ConjoinWorkspaces", -1, -1, true); - conjoin->setProperty("CheckOverlapping", false); - const size_t wsMax = workspaces.size(); - for (size_t j = 0; j < wsMax; j++) { - auto tempPeakWs = workspaces.at(j).at(0); - const size_t paramMax = workspaces.at(j).size(); - workflowProg.report("Conjoining matrix workspaces"); - for (size_t k = 1; k < paramMax; k++) { - auto paramWs = workspaces.at(j).at(k); - conjoin->setProperty("InputWorkspace1", tempPeakWs); - conjoin->setProperty("InputWorkspace2", paramWs); - conjoin->executeAsChildAlg(); - tempPeakWs = conjoin->getProperty("InputWorkspace1"); - } - tempWorkspaces.push_back(tempPeakWs); - } - - // Join all peaks into a single workspace - outputWs = tempWorkspaces.at(0); - for (auto it = tempWorkspaces.begin() + 1; it != tempWorkspaces.end(); ++it) { - workflowProg.report("Joining peak workspaces"); - conjoin->setProperty("InputWorkspace1", outputWs); - conjoin->setProperty("InputWorkspace2", *it); - conjoin->executeAsChildAlg(); - outputWs = conjoin->getProperty("InputWorkspace1"); - } - - // Replace axis on workspaces with text axis - workflowProg.report("Converting text axis"); - const auto numberOfHist = outputWs->getNumberHistograms(); - auto axis = new TextAxis(numberOfHist); - - for (size_t k = 0; k < numberOfHist; k++) { - axis->setLabel(k, outputSpectraNames[k]); - } - outputWs->replaceAxis(1, axis); - - workflowProg.report("Setting unit"); - // Set units for the xAxis - if (!xUnit.empty()) { - outputWs->getAxis(0)->setUnit(xUnit); - } - - setProperty("OutputWorkspace", outputWs); + std::vector<std::string> parameterNames = getProperty("ParameterNames"); + std::vector<std::string> errorNames = appendSuffix(parameterNames, "_Err"); + auto const startRow = getStartRow(); + auto const endRow = getEndRow(inputWs->rowCount() - 1); + + auto const x = + getColumnValues<double>(*inputWs->getColumn(xColumn), startRow, endRow); + auto const yFilter = + makeColumnNameFilter(EndsWithOneOf(std::move(parameterNames))); + auto const eFilter = + makeColumnNameFilter(EndsWithOneOf(std::move(errorNames))); + + TableToMatrixWorkspaceConverter converter(inputWs.get(), x, yFilter, eFilter); + auto const output = converter(startRow, endRow, xUnit); + setProperty("OutputWorkspace", output); } -/** - * Searchs for a particular word within all of the fit params in the columns of - * a table workspace (note: This method only matches strings that are at the end - * of a column name this is to ensure that "Amplitude" will match - * "f0.f0.f1.Amplitude" but not f0.f0.f1.Amplitude_Err") - * @param suffix - The string to search for - * @param columns - A string vector of all the column names in a table workspace - * @return - The full column names in which the string is present - */ -std::vector<std::string> ProcessIndirectFitParameters::searchForFitParams( - const std::string &suffix, const std::vector<std::string> &columns) { - auto fitParams = std::vector<std::string>(); - const size_t totalColumns = columns.size(); - for (size_t i = 0; i < totalColumns; i++) { - auto pos = columns.at(i).rfind(suffix); - if (pos != std::string::npos) { - auto endCheck = pos + suffix.size(); - if (endCheck == columns.at(i).size()) { - fitParams.push_back(columns.at(i)); - } - } - } - return fitParams; +std::size_t ProcessIndirectFitParameters::getStartRow() const { + int startRow = getProperty("StartRowIndex"); + return startRow == EMPTY_INT() ? 0 : static_cast<std::size_t>(startRow); } -/** - * Changes the ordering of a 2D vector of strings such that - * [[1a,2a,3a], [1b,2b,3b], [1c,2c,3c]] - * becomes - * [[1a,1b,1c], [2a,2b,2c], [3a,3b,3c]] - * @param original - The original vector to be transformed - * @return - The vector after it has been transformed - */ -std::vector<std::vector<MatrixWorkspace_sptr>> -ProcessIndirectFitParameters::reorder2DVector( - std::vector<std::vector<MatrixWorkspace_sptr>> &original) { - size_t maximumLength = original.at(0).size(); - for (size_t i = 1; i < original.size(); i++) { - if (original.at(i).size() > maximumLength) { - maximumLength = original.at(i).size(); - } - } - - auto reorderedVector = std::vector<std::vector<MatrixWorkspace_sptr>>(); - for (size_t i = 0; i < maximumLength; i++) { - std::vector<MatrixWorkspace_sptr> temp; - for (const auto &j : original) { - if (j.size() > i) { - temp.push_back(j.at(i)); - } - } - reorderedVector.push_back(temp); - } - - return reorderedVector; +std::size_t ProcessIndirectFitParameters::getEndRow(std::size_t maximum) const { + int endRow = getProperty("EndRowIndex"); + return endRow == EMPTY_INT() ? maximum : static_cast<std::size_t>(endRow); } } // namespace Algorithms diff --git a/Framework/WorkflowAlgorithms/test/MuonProcessTest.h b/Framework/WorkflowAlgorithms/test/MuonProcessTest.h index f1d400409b68a8566f37dc6e895a57cd5e4b97ff..e69cd6c68432d633f35b41292fa3303888a2d390 100644 --- a/Framework/WorkflowAlgorithms/test/MuonProcessTest.h +++ b/Framework/WorkflowAlgorithms/test/MuonProcessTest.h @@ -191,9 +191,9 @@ public: if (ws) { TS_ASSERT_EQUALS(ws->getNumberHistograms(), 1); - TS_ASSERT_DELTA(ws->readX(0)[0], -0.254, 0.001); - TS_ASSERT_DELTA(ws->readX(0)[1000], 15.746, 0.001); - TS_ASSERT_DELTA(ws->readX(0)[1752], 27.778, 0.001); + TS_ASSERT_DELTA(ws->readX(0)[0], 3.010, 0.001); + TS_ASSERT_DELTA(ws->readX(0)[1000], 19.010, 0.001); + TS_ASSERT_DELTA(ws->readX(0)[1752], 31.042, 0.001); } } diff --git a/MantidPlot/src/ApplicationWindow.cpp b/MantidPlot/src/ApplicationWindow.cpp index 76c35b7c3516f895ae6eb2423fa13f4135c8f1a6..f75919758dfb3b38fc21f20275d6f031743ce383 100644 --- a/MantidPlot/src/ApplicationWindow.cpp +++ b/MantidPlot/src/ApplicationWindow.cpp @@ -16742,6 +16742,19 @@ bool ApplicationWindow::isOfType(const QObject *obj, return strcmp(obj->metaObject()->className(), toCompare) == 0; } +/** + * Loads a project file as part of project recovery + * + * @param sourceFile The full path to the .project file + * @return True is loading was successful, false otherwise + */ +bool ApplicationWindow::loadProjectRecovery(std::string sourceFile) { + const bool isRecovery = true; + ProjectSerialiser projectWriter(this, isRecovery); + // File version is not applicable to project recovery - so set to 0 + return projectWriter.load(sourceFile, 0); +} + /** * Triggers saving project recovery on behalf of an external thread * or caller, such as project recovery. @@ -16762,9 +16775,5 @@ void ApplicationWindow::checkForProjectRecovery() { } // Recovery file present - if (m_projectRecovery.attemptRecovery()) { - // If it worked correctly reset project recovery and start saving - m_projectRecovery.clearAllCheckpoints(); - m_projectRecovery.startProjectSaving(); - } + m_projectRecovery.attemptRecovery(); } diff --git a/MantidPlot/src/ApplicationWindow.h b/MantidPlot/src/ApplicationWindow.h index 66a679e04ae2fb16b04b7809ce7683b15d2ebdb6..ba7830dd663b9b52535dd1cd3b8c111d830922f8 100644 --- a/MantidPlot/src/ApplicationWindow.h +++ b/MantidPlot/src/ApplicationWindow.h @@ -1123,6 +1123,8 @@ public slots: bool isOfType(const QObject *obj, const char *toCompare) const; + bool loadProjectRecovery(std::string sourceFile); + // The string must be copied from the other thread in saveProjectRecovery /// Saves the current project as part of recovery auto saving bool saveProjectRecovery(std::string destination); diff --git a/MantidPlot/src/ProjectRecovery.cpp b/MantidPlot/src/ProjectRecovery.cpp index 98c664ea88e61abbaae8c4a03034c9f22a10d25c..cc10f27086cf6d3237927ebb264f942ed810068b 100644 --- a/MantidPlot/src/ProjectRecovery.cpp +++ b/MantidPlot/src/ProjectRecovery.cpp @@ -178,29 +178,42 @@ ProjectRecovery::ProjectRecovery(ApplicationWindow *windowHandle) /// Destructor which also stops any background threads currently in progress ProjectRecovery::~ProjectRecovery() { stopProjectSaving(); } -bool ProjectRecovery::attemptRecovery() { +void ProjectRecovery::attemptRecovery() { QString recoveryMsg = QObject::tr( "Mantid did not close correctly and a recovery" " checkpoint has been found. Would you like to attempt recovery?"); int userChoice = QMessageBox::information( m_windowPtr, QObject::tr("Project Recovery"), recoveryMsg, - QObject::tr("Open in script editor"), QObject::tr("No"), 0, 1); + QObject::tr("Yes"), QObject::tr("No"), + QObject::tr("Only open script in editor"), 0, 1); if (userChoice == 1) { // User selected no - return true; + return; } const auto checkpointPaths = getRecoveryFolderCheckpoints(getRecoveryFolder()); auto &mostRecentCheckpoint = checkpointPaths.back(); - // TODO automated recovery - switch (userChoice) { - case 0: - return openInEditor(mostRecentCheckpoint); - default: + auto destFilename = + Poco::Path(Mantid::Kernel::ConfigService::Instance().getAppDataDir()); + destFilename.append("ordered_recovery.py"); + + if (userChoice == 0) { + // We have to spin up a new thread so the GUI can continue painting whilst + // we exec + openInEditor(mostRecentCheckpoint, destFilename); + std::thread recoveryThread( + [=] { loadRecoveryCheckpoint(mostRecentCheckpoint); }); + recoveryThread.detach(); + } else if (userChoice == 2) { + openInEditor(mostRecentCheckpoint, destFilename); + // Restart project recovery as we stay synchronous + clearAllCheckpoints(); + startProjectSaving(); + } else { throw std::runtime_error("Unknown choice in ProjectRecovery"); } } @@ -320,11 +333,59 @@ void ProjectRecovery::stopProjectSaving() { } } -bool ProjectRecovery::openInEditor(const Poco::Path &inputFolder) { - auto destFilename = - Poco::Path(Mantid::Kernel::ConfigService::Instance().getAppDataDir()); - destFilename.append("ordered_recovery.py"); - compileRecoveryScript(inputFolder, destFilename); +/** + * Asynchronously loads a recovery checkpoint by opening + * a scripting window to the ordered workspace + * history file, then execute it. When this finishes the + * project loading mechanism is invoked in the main GUI thread + * to recreate all Qt objects / widgets + * + * @param recoveryFolder : The checkpoint folder + * @throws : If Qt binding fails to main GUI thread + */ +void ProjectRecovery::loadRecoveryCheckpoint(const Poco::Path &recoveryFolder) { + ScriptingWindow *scriptWindow = m_windowPtr->getScriptWindowHandle(); + if (!scriptWindow) { + throw std::runtime_error("Could not get handle to scripting window"); + } + + // Ensure the window repaints so it doesn't appear frozen before exec + scriptWindow->executeCurrentTab(Script::ExecutionMode::Serialised); + g_log.notice("Re-opening GUIs"); + + auto projectFile = Poco::Path(recoveryFolder).append(OUTPUT_PROJ_NAME); + + bool loadCompleted = false; + if (!QMetaObject::invokeMethod( + m_windowPtr, "loadProjectRecovery", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, loadCompleted), + Q_ARG(const std::string, projectFile.toString()))) { + throw std::runtime_error( + "Project Recovery: Failed to load project windows - Qt binding failed"); + } + + if (!loadCompleted) { + g_log.warning("Loading failed to recovery everything completely"); + return; + } + g_log.notice("Project Recovery finished"); + + // Restart project recovery when the async part finishes + clearAllCheckpoints(); + startProjectSaving(); +} + +/** + * Compiles the project recovery script from a given checkpoint + * folder and opens this in the script editor + * + * @param inputFolder : The folder containing the checkpoint to recover + * @param historyDest : Where to save the ordered history + * @throws If a handle to the scripting window cannot be obtained + */ +void ProjectRecovery::openInEditor(const Poco::Path &inputFolder, + const Poco::Path &historyDest) { + compileRecoveryScript(inputFolder, historyDest); // Force application window to create the script window first const bool forceVisible = true; @@ -335,8 +396,7 @@ bool ProjectRecovery::openInEditor(const Poco::Path &inputFolder) { throw std::runtime_error("Could not get handle to scripting window"); } - scriptWindow->open(QString::fromStdString(destFilename.toString())); - return true; + scriptWindow->open(QString::fromStdString(historyDest.toString())); } /// Top level thread wrapper which catches all exceptions to gracefully handle @@ -430,7 +490,8 @@ void ProjectRecovery::saveWsHistories(const Poco::Path &historyDestFolder) { const auto &ads = Mantid::API::AnalysisDataService::Instance(); // Hold a copy to the shared pointers so they do not get deleted under us - const auto wsHandles = ads.getObjects(); + const auto wsHandles = + ads.getObjects(Mantid::Kernel::DataServiceHidden::Include); if (wsHandles.empty()) { return; diff --git a/MantidPlot/src/ProjectRecovery.h b/MantidPlot/src/ProjectRecovery.h index b1154dbb657f228db38fe983662bc40e9f764b5f..93b5e11d1d6ef6149ebb05b2a382c9e76ac78113 100644 --- a/MantidPlot/src/ProjectRecovery.h +++ b/MantidPlot/src/ProjectRecovery.h @@ -55,7 +55,7 @@ public: ~ProjectRecovery(); /// Attempts recovery of the most recent checkpoint - bool attemptRecovery(); + void attemptRecovery(); /// Checks if recovery is required bool checkForRecovery() const noexcept; @@ -81,11 +81,12 @@ private: /// Deletes oldest checkpoints beyond the maximum number to keep void deleteExistingCheckpoints(size_t checkpointsToKeep) const; - /// Loads a recovery checkpoint in the given folder - TODO in future PR - // bool loadRecoveryCheckpoint(const Poco::Path &path); + /// Loads a recovery checkpoint in the given folder + void loadRecoveryCheckpoint(const Poco::Path &path); /// Open a recovery checkpoint in the scripting window - bool openInEditor(const Poco::Path &inputFolder); + void openInEditor(const Poco::Path &inputFolder, + const Poco::Path &historyDest); /// Wraps the thread in a try catch to log any failures void projectSavingThreadWrapper(); diff --git a/MantidPlot/src/ProjectSerialiser.cpp b/MantidPlot/src/ProjectSerialiser.cpp index 23c66683f748112851e7d02b22883c246b22733f..888624d1a6927ef3fa2b7b5aaabf413822467727 100644 --- a/MantidPlot/src/ProjectSerialiser.cpp +++ b/MantidPlot/src/ProjectSerialiser.cpp @@ -132,8 +132,9 @@ bool ProjectSerialiser::save(const QString &projectName, bool compress, * @param fileVersion :: project file version used * @param isTopLevel :: whether this function is being called on a top level * folder. (Default True) + * @return True is loading was successful, otherwise false */ -void ProjectSerialiser::load(std::string filepath, const int fileVersion, +bool ProjectSerialiser::load(std::string filepath, const int fileVersion, const bool isTopLevel) { // We have to accept std::string to maintain Python compatibility auto qfilePath = QString::fromStdString(filepath); @@ -204,6 +205,8 @@ void ProjectSerialiser::load(std::string filepath, const int fileVersion, g_log.notice() << "Finished Loading Project: " << window->projectname.toStdString() << "\n"; + + return true; } /** @@ -225,7 +228,7 @@ void ProjectSerialiser::loadProjectSections(const std::string &lines, // If this is the top level folder of the project, we'll need to load the // workspaces before anything else. // If were recovering projects the workspaces should already be loaded - if (isTopLevel && !m_projectRecovery) { + if (isTopLevel) { loadWorkspaces(tsv); } @@ -278,8 +281,11 @@ void ProjectSerialiser::loadWorkspaces(const TSVSerialiser &tsv) { std::unordered_set<std::string> allWsNames(parsedNames.at(ALL_WS).begin(), parsedNames.at(ALL_WS).end()); - allWsNames.insert(parsedNames.at(ALL_GROUP_NAMES).begin(), - parsedNames.at(ALL_GROUP_NAMES).end()); + if (parsedNames.find(ALL_GROUP_NAMES) != parsedNames.cend()) { + // Add group names to the list of accepted names + allWsNames.insert(parsedNames.at(ALL_GROUP_NAMES).begin(), + parsedNames.at(ALL_GROUP_NAMES).end()); + } const auto loadedWs = adsInstance.getObjectNames(); for (const std::string &adsWsName : loadedWs) { @@ -302,7 +308,7 @@ void ProjectSerialiser::loadWorkspaces(const TSVSerialiser &tsv) { void ProjectSerialiser::loadWindows(const TSVSerialiser &tsv, const int fileVersion) { auto keys = WindowFactory::Instance().getKeys(); - // Work around for graph-table dependance. Graph3D's currently rely on + // Work around for graph-table dependence. Graph3D's currently rely on // looking up tables. These must be loaded before the graphs, so work around // by loading in reverse alphabetical order. std::reverse(keys.begin(), keys.end()); @@ -932,8 +938,8 @@ MantidQt::API::ProjectSerialiser::parseWsNames(const std::string &wsNames) { // The first element is removed since it says "WorkspaceNames" unparsedWorkspaces.erase(unparsedWorkspaces.begin()); + const char groupWorkspaceChar = ','; for (const auto &workspaceName : unparsedWorkspaces) { - const char groupWorkspaceChar = ','; if (workspaceName.find(groupWorkspaceChar) == std::string::npos) { // Normal workspace diff --git a/MantidPlot/src/ProjectSerialiser.h b/MantidPlot/src/ProjectSerialiser.h index f33fd8461def5f79b59c9deb5466bc9d9e83c86b..bb8575893bcfa13f28cab1619f776f4edf598992 100644 --- a/MantidPlot/src/ProjectSerialiser.h +++ b/MantidPlot/src/ProjectSerialiser.h @@ -71,7 +71,7 @@ public: bool save(const QString &projectName, bool compress = false, bool saveAll = true); /// Load a project file from disk - void load(std::string filepath, const int fileVersion, + bool load(std::string filepath, const int fileVersion, const bool isTopLevel = true); /// Open the script window and load scripts from string void openScriptWindow(const QStringList &files); diff --git a/Testing/SystemTests/tests/analysis/BASISAutoReduction.py b/Testing/SystemTests/tests/analysis/BASISAutoReduction.py index 960c43836259e34185ab311966c35eaae8595d64..516d1ac06e4467c11f1ab3a25bf3a9d36ac34115 100644 --- a/Testing/SystemTests/tests/analysis/BASISAutoReduction.py +++ b/Testing/SystemTests/tests/analysis/BASISAutoReduction.py @@ -10,6 +10,9 @@ from mantid.simpleapi import * class BASISAutoReductionTest(stresstesting.MantidStressTest): + def skipTests(self): + return True + def requiredFiles(self): return ['BSS_13387_event.nxs'] diff --git a/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py b/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py index c41ea24915483f42f1ac79f506047774faf1d579..c57e39910cbbd25966e3a1500aaeb3169b2d5a82 100644 --- a/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py +++ b/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py @@ -765,7 +765,7 @@ class ISISIndirectInelasticIqtAndIqtFit(with_metaclass(ABCMeta, ISISIndirectInel SpecMin=0, SpecMax=self.spec_max) self.result_names = [iqt_ws.name(), - iqtfitSeq_ws.name()] + iqtfitSeq_ws[0].name()] # Remove workspaces from Mantid for sample in self.samples: @@ -994,16 +994,16 @@ class ISISIndirectInelasticConvFit(with_metaclass(ABCMeta, ISISIndirectInelastic LoadNexus(self.sample, OutputWorkspace=self.sample) LoadNexus(FileFinder.getFullPath(self.resolution), OutputWorkspace=self.resolution) - ConvolutionFitSequential( - InputWorkspace=self.sample, - Function=self.func, - PassWSIndexToFunction=self.passWSIndexToFunction, - StartX=self.startx, - EndX=self.endx, - SpecMin=self.spectra_min, - SpecMax=self.spectra_max, - PeakRadius=5, - OutputWorkspace=self.result_names[0]) + convfitSeq_ws, params, fit_group = ConvolutionFitSequential(InputWorkspace=self.sample, + Function=self.func, + PassWSIndexToFunction=self.passWSIndexToFunction, + StartX=self.startx, + EndX=self.endx, + SpecMin=self.spectra_min, + SpecMax=self.spectra_max, + PeakRadius=5) + + self.result_names = [convfitSeq_ws[0].name()] def _validate_properties(self): '''Check the object properties are in an expected state to continue''' @@ -1045,7 +1045,7 @@ class OSIRISConvFit(ISISIndirectInelasticConvFit): self.spectra_max = 41 self.ties = False - self.result_names = ['osi97935_graphite002_conv_1LFitL_s0_to_41_Result'] + self.result_names = ['osi97935_graphite002_conv_1LFitL_s0_to_41_Result_1'] def get_reference_files(self): self.tolerance = 0.3 @@ -1072,7 +1072,7 @@ class IRISConvFit(ISISIndirectInelasticConvFit): self.spectra_max = 50 self.ties = False - self.result_names = ['irs53664_graphite002_conv_1LFitL_s0_to_50_Result'] + self.result_names = ['irs53664_graphite002_conv_1LFitL_s0_to_50_Result_1'] def get_reference_files(self): self.tolerance = 0.2 diff --git a/Testing/SystemTests/tests/analysis/LoadLotsOfFiles.py b/Testing/SystemTests/tests/analysis/LoadLotsOfFiles.py index d48378ed7072ab9378fe3e1475fc21652f54eb08..6bed93409417cb9025a3e71e1ea320dab602ac3f 100644 --- a/Testing/SystemTests/tests/analysis/LoadLotsOfFiles.py +++ b/Testing/SystemTests/tests/analysis/LoadLotsOfFiles.py @@ -113,7 +113,8 @@ BANNED_FILES = ['80_tubes_Top_and_Bottom_April_2015.xml', 'Fe-gamma.cif', 'Fe-alpha.cif', 'Sm2O3.cif', - 'template_ENGINX_241391_236516_North_bank.prm' + 'template_ENGINX_241391_236516_North_bank.prm', + 'BSS_13387_event.nxs' # bad event_index ] EXPECTED_EXT = '.expected' diff --git a/buildconfig/CMake/WindowsNSIS.cmake b/buildconfig/CMake/WindowsNSIS.cmake index c70096779408a15c5ed600e86799b41d6424aca0..c583bc4d7e9e6db0c3f33288f9ad9adf472f8291 100644 --- a/buildconfig/CMake/WindowsNSIS.cmake +++ b/buildconfig/CMake/WindowsNSIS.cmake @@ -44,7 +44,7 @@ endforeach() # Other third party dependencies set ( BOOST_DIST_DLLS boost_date_time-mt.dll - boost_python-mt.dll + boost_python27-mt.dll boost_regex-mt.dll boost_serialization-mt.dll ) diff --git a/docs/source/algorithms/ApplyMuonDetectorGroupPairing-v1.rst b/docs/source/algorithms/ApplyMuonDetectorGroupPairing-v1.rst index c558ee10809126a2c0b24e2e8536bee65279af43..92be5dba7dbf9a02bc2727590cad339506acc653 100644 --- a/docs/source/algorithms/ApplyMuonDetectorGroupPairing-v1.rst +++ b/docs/source/algorithms/ApplyMuonDetectorGroupPairing-v1.rst @@ -114,8 +114,8 @@ Output: .. testoutput:: ExMUSRPairAsymmetry - -0.0176193517359 + -0.0141980129084 .. categories:: -.. sourcelink:: \ No newline at end of file +.. sourcelink:: diff --git a/docs/source/algorithms/ApplyMuonDetectorGrouping-v1.rst b/docs/source/algorithms/ApplyMuonDetectorGrouping-v1.rst index a8610e8e58c288ee686c6fc51ad2c3027e60a1da..9f5595498a7576e7e563108461382472ca80b0a7 100644 --- a/docs/source/algorithms/ApplyMuonDetectorGrouping-v1.rst +++ b/docs/source/algorithms/ApplyMuonDetectorGrouping-v1.rst @@ -101,12 +101,12 @@ Output: .. testoutput:: ExCountsOffsetAndRebin - Total counts (rebinned) : 84438 - Total counts (no rebin) : 84438 + Total counts (rebinned) : 79970 + Total counts (no rebin) : 79970 Time range (original) : -0.550 - 31.450 mus - Time range (no rebin) : -0.550 - 31.450 mus - Time range (rebinned) : -0.550 - 31.450 mus + Time range (no rebin) : 0.106 - 31.450 mus + Time range (rebinned) : 0.106 - 31.306 mus Time step (original) : 0.016 mus Time step (no rebin) : 0.016 mus @@ -115,4 +115,4 @@ Output: .. categories:: -.. sourcelink:: \ No newline at end of file +.. sourcelink:: diff --git a/docs/source/algorithms/ConvolutionFitSequential-v1.rst b/docs/source/algorithms/ConvolutionFitSequential-v1.rst index 1e2f3cb907fb591cac19bd017b3451b7bf420e0c..cb45baa4f19cfb16f9169cfa2722176dd0805918 100644 --- a/docs/source/algorithms/ConvolutionFitSequential-v1.rst +++ b/docs/source/algorithms/ConvolutionFitSequential-v1.rst @@ -54,6 +54,7 @@ Usage SpecMin=specMin, SpecMax=specMax, ConvolveMembers=convolve, Minimizer=minimizer, MaxIterations=maxIt) + result_ws = result_ws[0] print("Result has %i Spectra" %result_ws.getNumberHistograms()) diff --git a/docs/source/algorithms/EnggSaveSinglePeakFitResultsToHDF5-v1.rst b/docs/source/algorithms/EnggSaveSinglePeakFitResultsToHDF5-v1.rst index 59fb132fe95435aa3da340f54d38ee26664217a3..7ebaa71f349dc76823c8bc00d77204baa8891164 100644 --- a/docs/source/algorithms/EnggSaveSinglePeakFitResultsToHDF5-v1.rst +++ b/docs/source/algorithms/EnggSaveSinglePeakFitResultsToHDF5-v1.rst @@ -88,3 +88,7 @@ Output: Peaks dataset has 3 rows, for our 3 peaks First peak is at D spacing 1.0 Third peak X0 = 0.5 + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/algorithms/ExtractQENSMembers-v1.rst b/docs/source/algorithms/ExtractQENSMembers-v1.rst index e72c473e4b1e5ccdc2cc5dfc4071e95806f07d2c..dfdeca7434857a7ea3671b5a44f0df88c0e03311 100644 --- a/docs/source/algorithms/ExtractQENSMembers-v1.rst +++ b/docs/source/algorithms/ExtractQENSMembers-v1.rst @@ -121,3 +121,7 @@ Output: irs26176_graphite002_conv_1LFixF_s0_to_9_Members_Diff irs26176_graphite002_conv_1LFixF_s0_to_9_Members_LinearBackground irs26176_graphite002_conv_1LFixF_s0_to_9_Members_Lorentzian + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/algorithms/LoadLamp-v1.rst b/docs/source/algorithms/LoadLamp-v1.rst index ebcef6ad50a2b0d063f231d3ece4d63f05fb2f07..a21ddea0808ca2329731907cd230ff4e7d2919b6 100644 --- a/docs/source/algorithms/LoadLamp-v1.rst +++ b/docs/source/algorithms/LoadLamp-v1.rst @@ -40,3 +40,7 @@ Output: .. testcleanup:: ExLoadLamp mtd.clear() + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/algorithms/RotateInstrumentComponent-v1.rst b/docs/source/algorithms/RotateInstrumentComponent-v1.rst index 10b3d82729e5a7bd199a7c0d001b448b8345c30e..603ac1b2e0f37924b9c554ff86e971513b7830a1 100644 --- a/docs/source/algorithms/RotateInstrumentComponent-v1.rst +++ b/docs/source/algorithms/RotateInstrumentComponent-v1.rst @@ -177,7 +177,11 @@ Example 3: Rotating a single detector # Take element by element difference of the solid angles diff = sa2 - sa1 + # control linewidth of printed array to ensure it matches on different versions + pp_opts_orig = np.get_printoptions() + np.set_printoptions(linewidth=64) print(diff) + np.set_printoptions(**pp_opts_orig) print('The non-zero difference {:.13f} is due to detector {}'.format(diff[32], ws.getDetector(32).getID())) Output @@ -187,16 +191,17 @@ Output [0.0888151,-0.108221,-0.145] [0.0888151,-0.108221,-0.145] - [ 0. 0. 0. 0. 0. 0. 0. - 0. 0. 0. 0. 0. 0. 0. - 0. 0. 0. 0. 0. 0. 0. - 0. 0. 0. 0. 0. 0. 0. - 0. 0. 0. 0. -0.04645313 0. 0. - 0. 0. 0. 0. 0. 0. 0. - 0. 0. 0. 0. 0. 0. 0. - 0. 0. 0. 0. 0. 0. 0. - 0. 0. 0. 0. 0. 0. 0. - 0. ] + [ 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. -0.04645313 0. 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. 0. 0. + 0. 0. 0. 0. ] The non-zero difference -0.0464531276188 is due to detector 33 .. categories:: diff --git a/docs/source/algorithms/SaveGDA-v1.rst b/docs/source/algorithms/SaveGDA-v1.rst index b371f3b10b08e4c07fa98669a0bbb532c326ecaf..46262b715a823fb2e6161f060bbaa6b9e1f41192 100644 --- a/docs/source/algorithms/SaveGDA-v1.rst +++ b/docs/source/algorithms/SaveGDA-v1.rst @@ -118,3 +118,7 @@ Output: 40507 232 49 40546 181 44 40586 171 43 40626 206 47 40666 246 50 40706 161 40 40746 126 37 40786 124 37 40826 131 40 40866 221 48 40906 157 40 40946 169 41 + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/algorithms/VesuvioPeakPrediction-v1.rst b/docs/source/algorithms/VesuvioPeakPrediction-v1.rst index 6404c8dc759aeb07fdbeb4462a2faf0b242c433e..f5010078e9880f5c807ee451b2345de74735f471 100644 --- a/docs/source/algorithms/VesuvioPeakPrediction-v1.rst +++ b/docs/source/algorithms/VesuvioPeakPrediction-v1.rst @@ -58,3 +58,7 @@ Usage DeleteWorkspace('vesuvio_einstein_params') DeleteWorkspace('vesuvio_debye_params') + +.. categories:: + +.. sourcelink:: diff --git a/docs/source/api/python/mantid/kernel/DateAndTime.rst b/docs/source/api/python/mantid/kernel/DateAndTime.rst index 2298ddeb90438933e8087b7b75b7fc459681c93f..058d8a9ea652b120de2940af408afec7a43b495d 100644 --- a/docs/source/api/python/mantid/kernel/DateAndTime.rst +++ b/docs/source/api/python/mantid/kernel/DateAndTime.rst @@ -25,7 +25,7 @@ With :class:`numpy.datetime64`, finding the number of seconds between two times diff = (timeArray[-1]-timeArray[0]) / np.timedelta64(1, 's') -For converting :class:`numpy.datetime64` data to a string for the default facilty, use :func:`numpy.datetime_as_string` explicitly (assuming the times are a variable named ``times``) +For converting `numpy.datetime64 <https://docs.scipy.org/doc/numpy/reference/arrays.datetime.html>`_ data to a string for the default facilty, use :func:`numpy.datetime_as_string` explicitly (assuming the times are a variable named ``times``) .. code-block:: python @@ -33,7 +33,19 @@ For converting :class:`numpy.datetime64` data to a string for the default facilt tz = pytz.timezone(ConfigService.getFacility().timezone()) times = numpy.datetime_as_string(times, timezone=tz) -This is how numpy internally converts :class:`numpy.datetime64` to strings for printing, but the selection of timezone is explicit (using `pytz <https://pythonhosted.org/pytz/>`_) to allow for matching where the data was recorded rather than the system's timezone. +This is how numpy internally converts `numpy.datetime64 <https://docs.scipy.org/doc/numpy/reference/arrays.datetime.html>`_ to strings for printing, but the selection of timezone is explicit (using `pytz <https://pythonhosted.org/pytz/>`_) to allow for matching where the data was recorded rather than the system's timezone. + +Converting strings to ``datetime64`` +------------------------------------ + +How strings are interpreted by `numpy.datetime64 <https://docs.scipy.org/doc/numpy/reference/arrays.datetime.html>`_ `changed in version 1.11 <https://docs.scipy.org/doc/numpy/reference/arrays.datetime.html#changes-with-numpy-1-11>`_. Before this point, a string without timezone designation was assumed to be in local timezone. After this point it is assumed to be in UTC. Additionally as of version 1.11, NumPy started warning if a string was supplied with timezone designation. This means that on rhel7 (in EDT) + +.. code-block:: python + + repr(np.datetime64('1990-01-01T00:00Z')) + repr(np.datetime64('1990-01-01T00:00')) + +result in ``"numpy.datetime64('1989-12-31T19:00-0500')"`` and ``"numpy.datetime64('1990-01-01T00:00-0500')"`` respectively. However, in "newer" python results ``"numpy.datetime64('1990-01-01T00:00')"``, but the first version results in a ``DeprecationWarning``. .. module:`mantid.kernel` diff --git a/docs/source/images/WorkspaceWidgetSortSize.png b/docs/source/images/WorkspaceWidgetSortSize.png new file mode 100644 index 0000000000000000000000000000000000000000..91127401ba4d7156a24a4c6d4fd57aa0392beeaa Binary files /dev/null and b/docs/source/images/WorkspaceWidgetSortSize.png differ diff --git a/docs/source/release/v3.13.0/framework.rst b/docs/source/release/v3.13.0/framework.rst index eb7b33baefb2a0c64d47742e8a5907633d3998eb..1b7b638a15f698727a5e7fd79ecf89df2d56eaf1 100644 --- a/docs/source/release/v3.13.0/framework.rst +++ b/docs/source/release/v3.13.0/framework.rst @@ -113,6 +113,7 @@ Bugfixes - Fit functions defined in a python script can be used with the new fit function API right after subscription. - Child algorithms now respect their parent algorithm's ``EnableLogging`` setting when invoked using the function-style calling. Previously, some messages could appear in the log even though ``EnableLogging`` was set to ``False``. - Fixed a bug in ``detectorSignedTwoTheta`` method in ``MatrixWorkspace`` where the sign of the angle depended on the axis pointing up, not on the actual theta-sing axis defined in the IDF. +- Fixed a bug where calling ``setCell`` method on a :ref:`PeaksWorkspace <PeaksWorkspace>` did not set the cell value. :ref:`Release 3.13.0 <v3.13.0>` diff --git a/docs/source/release/v3.13.0/indirect_inelastic.rst b/docs/source/release/v3.13.0/indirect_inelastic.rst index 023d053214282097744526bef46f9ef5f7b0d830..53d685a0b16f2d51843a9d975a5d67aae12cfa8b 100644 --- a/docs/source/release/v3.13.0/indirect_inelastic.rst +++ b/docs/source/release/v3.13.0/indirect_inelastic.rst @@ -52,6 +52,8 @@ New - The MSDFit, IqtFit and ConvFit interfaces now allow selection of discontinuous spectra ranges and masking energy ranges. - The JumpFit interface now allows masking of energy ranges. +- The QENS Fitting interfaces can now be provided multiple data-sets as input -- these data-sets can be viewed + individually within the interface (plot, fit result, etc.) Bugfixes ######## diff --git a/docs/source/release/v3.13.0/muon.rst b/docs/source/release/v3.13.0/muon.rst index 3f770113774bbe5a0a0659bc2c928fd5230417de..bc6d353fabe403235e5f01fee2d45c51efb3c9b5 100644 --- a/docs/source/release/v3.13.0/muon.rst +++ b/docs/source/release/v3.13.0/muon.rst @@ -21,6 +21,7 @@ Bugfixes - The run number is now updated before the periods, preventing irrelevant warnings from being produced. - In single fit the workspace can be changed. - In multiple fitting the function can be replaced without causing a crash. +- Loading current run in Muon Analysis and going to previous runs no longer creates an error in the simultaneous fit label. Algorithms ---------- @@ -39,5 +40,6 @@ Bugfixes - :ref:`MuonMaxent <algm-MuonMaxent>` and :ref:`PhaseQuad <algm-PhaseQuad>` no longer include dead detectors (zero counts) when calculating the frequency spectrum. - :ref:`RemoveExpDecay <algm-RemoveExpDecay>` will not alter data from a dead detectors (zero counts). - :ref:`CalMuonDetectorPhases <algm-CalMuonDetectorPhases>` will give an error code for dead detectors (zero counts) in the phase table. +- :ref:`MuonProcess <algm-MuonProcess>` always crops the data from `Xmin`. :ref:`Release 3.13.0 <v3.13.0>` diff --git a/docs/source/release/v3.13.0/ui.rst b/docs/source/release/v3.13.0/ui.rst index 8991ae674ed4944dba8fd2e5e621e2f65dfe08fa..da6ff22d23b502ed2b951eddf622b349e02b36a4 100644 --- a/docs/source/release/v3.13.0/ui.rst +++ b/docs/source/release/v3.13.0/ui.rst @@ -17,6 +17,16 @@ MantidPlot - MantidPlot's pyplot API has been removed. +Improvements +############ + +- Added functionality to sort based on Memory Size of the workspace + +.. figure:: ../../images/WorkspaceWidgetSortSize.png + :class: screenshot + :align: center + :figwidth: 70% + Bugfixes ######## diff --git a/qt/python/mantidqt/widgets/CMakeLists.txt b/qt/python/mantidqt/widgets/CMakeLists.txt index a098e7037e8dd520c44f1fe711ac00bc3cc35f2d..a69ab12eac2bad3197bc53a026ad474b53bb7363 100644 --- a/qt/python/mantidqt/widgets/CMakeLists.txt +++ b/qt/python/mantidqt/widgets/CMakeLists.txt @@ -13,7 +13,7 @@ find_package ( BoostPython REQUIRED ) list ( APPEND common_link_libs ${TCMALLOC_LIBRARIES_LINKTIME} - Kernel + ${CORE_MANTIDLIBS} ${POCO_LIBRARIES} ${PYTHON_LIBRARIES} ) diff --git a/qt/scientific_interfaces/Indirect/CMakeLists.txt b/qt/scientific_interfaces/Indirect/CMakeLists.txt index 37bc58694d72ab186d38f600ac9961acc9cf7b35..379a5234b65802b7973adb400b93c8894e395c49 100644 --- a/qt/scientific_interfaces/Indirect/CMakeLists.txt +++ b/qt/scientific_interfaces/Indirect/CMakeLists.txt @@ -5,6 +5,9 @@ set ( SRC_FILES ContainerSubtraction.cpp CorrectionsTab.cpp ConvFit.cpp + ConvFitAddWorkspaceDialog.cpp + ConvFitDataPresenter.cpp + ConvFitDataTablePresenter.cpp ConvFitModel.cpp DensityOfStates.cpp Elwin.cpp @@ -12,6 +15,7 @@ set ( SRC_FILES ISISCalibration.cpp ISISDiagnostics.cpp ISISEnergyTransfer.cpp + IndirectAddWorkspaceDialog.cpp IndirectBayes.cpp IndirectBayesTab.cpp IndirectCorrections.cpp @@ -19,10 +23,16 @@ set ( SRC_FILES IndirectDataAnalysisTab.cpp IndirectDataReduction.cpp IndirectDataReductionTab.cpp + IndirectDataTablePresenter.cpp IndirectDiffractionReduction.cpp IndirectFitAnalysisTab.cpp IndirectFitData.cpp + IndirectFitDataPresenter.cpp + IndirectFitDataView.cpp IndirectFitOutput.cpp + IndirectFitPlotModel.cpp + IndirectFitPlotPresenter.cpp + IndirectFitPlotView.cpp IndirectFittingModel.cpp IndirectLoadILL.cpp IndirectMolDyn.cpp @@ -43,6 +53,9 @@ set ( SRC_FILES IqtFit.cpp IqtFitModel.cpp JumpFit.cpp + JumpFitAddWorkspaceDialog.cpp + JumpFitDataPresenter.cpp + JumpFitDataTablePresenter.cpp JumpFitModel.cpp MSDFit.cpp MSDFitModel.cpp @@ -60,14 +73,19 @@ set ( INC_FILES CalculatePaalmanPings.h ContainerSubtraction.h ConvFit.h + ConvFitAddWorkspaceDialog.h + ConvFitDataPresenter.h + ConvFitDataTablePresenter.h ConvFitModel.h CorrectionsTab.h DensityOfStates.h Elwin.h + IAddWorkspaceDialog.h ILLEnergyTransfer.h ISISCalibration.h ISISDiagnostics.h ISISEnergyTransfer.h + IndirectAddWorkspaceDialog.h IndirectBayes.h IndirectBayesTab.h IndirectCorrections.h @@ -75,10 +93,16 @@ set ( INC_FILES IndirectDataAnalysisTab.h IndirectDataReduction.h IndirectDataReductionTab.h + IndirectDataTablePresenter.h IndirectDiffractionReduction.h IndirectFitAnalysisTab.h IndirectFitData.h + IndirectFitDataPresenter.h + IndirectFitDataView.h IndirectFitOutput.h + IndirectFitPlotModel.h + IndirectFitPlotPresenter.h + IndirectFitPlotView.h IndirectFittingModel.h IndirectLoadILL.h IndirectMolDyn.h @@ -99,6 +123,10 @@ set ( INC_FILES IqtFit.h IqtFitModel.h JumpFit.h + JumpFitAddWorkspaceDialog.h + JumpFitDataPresenter.h + JumpFitDataTablePresenter.h + LazyAsyncRunner.h JumpFitModel.h MSDFit.h MSDFitModel.h @@ -113,10 +141,15 @@ set ( MOC_FILES CalculatePaalmanPings.h ContainerSubtraction.h ConvFit.h + ConvFitAddWorkspaceDialog.h + ConvFitDataPresenter.h + ConvFitDataTablePresenter.h CorrectionsTab.h DensityOfStates.h Elwin.h ILLEnergyTransfer.h + IAddWorkspaceDialog.h + IndirectAddWorkspaceDialog.h IndirectBayes.h IndirectBayesTab.h IndirectCorrections.h @@ -124,8 +157,13 @@ set ( MOC_FILES IndirectDataAnalysisTab.h IndirectDataReduction.h IndirectDataReductionTab.h + IndirectDataTablePresenter.h IndirectDiffractionReduction.h IndirectFitAnalysisTab.h + IndirectFitDataPresenter.h + IndirectFitDataView.h + IndirectFitPlotPresenter.h + IndirectFitPlotView.h IndirectLoadILL.h IndirectMolDyn.h IndirectMoments.h @@ -144,6 +182,10 @@ set ( MOC_FILES Iqt.h IqtFit.h JumpFit.h + JumpFitAddWorkspaceDialog.h + JumpFitDataPresenter.h + JumpFitDataTablePresenter.h + LazyAsyncRunner.h MSDFit.h Quasi.h ResNorm.h @@ -159,14 +201,18 @@ set ( UI_FILES CalculatePaalmanPings.ui ContainerSubtraction.ui ConvFit.ui + ConvFitAddWorkspaceDialog.ui DensityOfStates.ui Elwin.ui ILLEnergyTransfer.ui + IndirectAddWorkspaceDialog.ui IndirectBayes.ui IndirectCorrections.ui IndirectDataAnalysis.ui IndirectDataReduction.ui IndirectDiffractionReduction.ui + IndirectFitDataView.ui + IndirectFitPreviewPlot.ui IndirectLoadILL.ui IndirectMolDyn.ui IndirectMoments.ui @@ -181,6 +227,7 @@ set ( UI_FILES Iqt.ui IqtFit.ui JumpFit.ui + JumpFitAddWorkspaceDialog.ui MSDFit.ui Quasi.ui ResNorm.ui diff --git a/qt/scientific_interfaces/Indirect/ConvFit.cpp b/qt/scientific_interfaces/Indirect/ConvFit.cpp index 9e7172a5c0f50d1d4e485f8189f32ae52c98311e..c2717185708c373d7639106b37cea31475aa51f7 100644 --- a/qt/scientific_interfaces/Indirect/ConvFit.cpp +++ b/qt/scientific_interfaces/Indirect/ConvFit.cpp @@ -1,4 +1,5 @@ #include "ConvFit.h" +#include "ConvFitDataPresenter.h" #include "../General/UserInputValidator.h" @@ -32,44 +33,23 @@ ConvFit::ConvFit(QWidget *parent) : IndirectFitAnalysisTab(new ConvFitModel, parent), m_uiForm(new Ui::ConvFit) { m_uiForm->setupUi(parent); -} - -void ConvFit::setupFitTab() { m_convFittingModel = dynamic_cast<ConvFitModel *>(fittingModel()); + + setFitDataPresenter(Mantid::Kernel::make_unique<ConvFitDataPresenter>( + m_convFittingModel, m_uiForm->fitDataView)); + setPlotView(m_uiForm->pvFitPlotView); setSpectrumSelectionView(m_uiForm->svSpectrumView); setFitPropertyBrowser(m_uiForm->fitPropertyBrowser); +} +void ConvFit::setupFitTab() { setDefaultPeakType("Lorentzian"); setConvolveMembers(true); - auto fitRangeSelector = m_uiForm->ppPlotTop->addRangeSelector("ConvFitRange"); - connect(fitRangeSelector, SIGNAL(minValueChanged(double)), this, - SLOT(xMinSelected(double))); - connect(fitRangeSelector, SIGNAL(maxValueChanged(double)), this, - SLOT(xMaxSelected(double))); - - auto backRangeSelector = m_uiForm->ppPlotTop->addRangeSelector( - "ConvFitBackRange", MantidWidgets::RangeSelector::YSINGLE); - backRangeSelector->setVisible(false); - backRangeSelector->setColour(Qt::darkGreen); - backRangeSelector->setRange(0.0, 1.0); - - auto hwhmRangeSelector = m_uiForm->ppPlotTop->addRangeSelector("ConvFitHWHM"); - hwhmRangeSelector->setColour(Qt::red); - - // Connections - connect(backRangeSelector, SIGNAL(minValueChanged(double)), this, - SLOT(backgLevel(double))); - connect(hwhmRangeSelector, SIGNAL(minValueChanged(double)), this, - SLOT(hwhmMinChanged(double))); - connect(hwhmRangeSelector, SIGNAL(maxValueChanged(double)), this, - SLOT(hwhmMaxChanged(double))); - - // Have FWHM Range linked to Fit Start/End Range - connect(fitRangeSelector, SIGNAL(rangeChanged(double, double)), - hwhmRangeSelector, SLOT(setRange(double, double))); - hwhmRangeSelector->setRange(-1.0, 1.0); - hwhmRangeSelector->setVisible(false); + setSampleWSSuffices({"_red", "_sqw"}); + setSampleFBSuffices({"_red.nxs", "_sqw.nxs"}); + setResolutionWSSuffices({"_res", "_red", "_sqw"}); + setResolutionFBSuffices({"_res.nxs", "_red.nxs", "_sqw.nxs"}); // Initialise fitTypeStrings m_fitStrings["None"] = ""; @@ -80,29 +60,30 @@ void ConvFit::setupFitTab() { m_fitStrings["ElasticDiffSphere"] = "EDS"; m_fitStrings["ElasticDiffRotDiscreteCircle"] = "EDC"; m_fitStrings["StretchedExpFT"] = "SFT"; + m_fitStrings["TeixeiraWaterSQE"] = "Teixeria1L"; - auto lorentzian = FunctionFactory::Instance().createFunction("Lorentzian"); + auto &functionFactory = FunctionFactory::Instance(); + auto lorentzian = functionFactory.createFunction("Lorentzian"); + auto teixeiraWater = functionFactory.createFunction("TeixeiraWaterSQE"); - auto elasticDiffSphere = - FunctionFactory::Instance().createFunction("ElasticDiffSphere"); + auto elasticDiffSphere = functionFactory.createFunction("ElasticDiffSphere"); auto inelasticDiffSphere = - FunctionFactory::Instance().createFunction("InelasticDiffSphere"); + functionFactory.createFunction("InelasticDiffSphere"); - auto elasticDiffRotDiscCircle = FunctionFactory::Instance().createFunction( - "ElasticDiffRotDiscreteCircle"); - auto inelasticDiffRotDiscCircle = FunctionFactory::Instance().createFunction( - "InelasticDiffRotDiscreteCircle"); + auto elasticDiffRotDiscCircle = + functionFactory.createFunction("ElasticDiffRotDiscreteCircle"); + auto inelasticDiffRotDiscCircle = + functionFactory.createFunction("InelasticDiffRotDiscreteCircle"); - auto stretchedExpFT = - FunctionFactory::Instance().createFunction("StretchedExpFT"); + auto stretchedExpFT = functionFactory.createFunction("StretchedExpFT"); - auto deltaFunction = - FunctionFactory::Instance().createFunction("DeltaFunction"); + auto deltaFunction = functionFactory.createFunction("DeltaFunction"); addCheckBoxFunctionGroup("Use Delta Function", {deltaFunction}); addComboBoxFunctionGroup("One Lorentzian", {lorentzian}); addComboBoxFunctionGroup("Two Lorentzians", {lorentzian, lorentzian}); + addComboBoxFunctionGroup("Teixeira Water", {teixeiraWater}); addComboBoxFunctionGroup("InelasticDiffSphere", {inelasticDiffSphere}); addComboBoxFunctionGroup("InelasticDiffRotDiscreteCircle", {inelasticDiffRotDiscCircle}); @@ -124,31 +105,9 @@ void ConvFit::setupFitTab() { m_properties["InstrumentResolution"] = m_dblManager->addProperty("InstrumentResolution"); - disablePlotGuess(); - disablePlotPreview(); - - // Replot input automatically when file / spec no changes - connect(m_uiForm->spPlotSpectrum, SIGNAL(valueChanged(int)), this, - SLOT(setSelectedSpectrum(int))); - - connect(m_uiForm->dsSampleInput, SIGNAL(dataReady(const QString &)), this, - SLOT(newDataLoaded(const QString &))); - connect(m_uiForm->dsResInput, SIGNAL(dataReady(const QString &)), this, - SLOT(setModelResolution(const QString &))); - connect(m_uiForm->dsResInput, SIGNAL(dataReady(const QString &)), this, - SLOT(updateGuessPlots())); - connect(m_uiForm->pbSingleFit, SIGNAL(clicked()), this, SLOT(singleFit())); - // Post Plot and Save connect(m_uiForm->pbSave, SIGNAL(clicked()), this, SLOT(saveClicked())); connect(m_uiForm->pbPlot, SIGNAL(clicked()), this, SLOT(plotClicked())); - connect(m_uiForm->pbPlotPreview, SIGNAL(clicked()), this, - SLOT(plotCurrentPreview())); - connect(m_uiForm->ckPlotGuess, SIGNAL(stateChanged(int)), this, - SLOT(updatePlotGuess())); - - connect(this, SIGNAL(parameterChanged(const Mantid::API::IFunction *)), this, - SLOT(parameterUpdated(const Mantid::API::IFunction *))); connect(this, SIGNAL(functionChanged()), this, SLOT(fitFunctionChanged())); } @@ -162,16 +121,6 @@ void ConvFit::setupFit(Mantid::API::IAlgorithm_sptr fitAlgorithm) { IndirectFitAnalysisTab::setupFit(fitAlgorithm); } -bool ConvFit::canPlotGuess() const { - return m_uiForm->dsResInput->isValid() && - IndirectFitAnalysisTab::canPlotGuess(); -} - -bool ConvFit::doPlotGuess() const { - return m_uiForm->ckPlotGuess->isEnabled() && - m_uiForm->ckPlotGuess->isChecked(); -} - void ConvFit::setModelResolution(const QString &resolutionName) { const auto name = resolutionName.toStdString(); const auto resolution = @@ -196,103 +145,10 @@ void ConvFit::updatePlotOptions() { IndirectFitAnalysisTab::updatePlotOptions(m_uiForm->cbPlotType); } -/** - * Validates the user's inputs in the ConvFit tab. - * @return If the validation was successful - */ -bool ConvFit::validate() { - if (!IndirectFitAnalysisTab::validate()) - return false; - - UserInputValidator uiv; - - uiv.checkDataSelectorIsValid("Sample Input", m_uiForm->dsSampleInput); - uiv.checkDataSelectorIsValid("Resolution Input", m_uiForm->dsResInput); - - uiv.checkValidRange("Fitting Range", std::make_pair(startX(), endX())); - - auto compositeModel = boost::dynamic_pointer_cast<CompositeFunction>(model()); - // Enforce the rule that at least one fit is needed; either a delta function, - // one or two Lorentzian functions, - // or both. (The resolution function must be convolved with a model.) - if (isEmptyModel()) - uiv.addErrorMessage("No fit function has been selected"); - else if (compositeModel && compositeModel->nFunctions() == 1 && - compositeModel->getFunction(0)->name() == "DeltaFunction") - uiv.addErrorMessage( - "Fit function is invalid; only a Delta Function has been supplied"); - - const auto error = uiv.generateErrorMessage(); - emit showMessageBox(error); - return error.isEmpty(); -} - -/** - * Reads in settings files - * @param settings The name of the QSettings object to retrieve data from - */ -void ConvFit::loadSettings(const QSettings &settings) { - m_uiForm->dsSampleInput->readSettings(settings.group()); - m_uiForm->dsResInput->readSettings(settings.group()); -} - -/** - * Called when new data has been loaded by the data selector. - * - * Configures ranges for spin boxes before raw plot is done. - * - * @param wsName Name of new workspace loaded - */ -void ConvFit::newDataLoaded(const QString &wsName) { - IndirectFitAnalysisTab::newInputDataLoaded(wsName); - updateHWHMFromResolution(); - - const int maxWsIndex = static_cast<int>( - m_convFittingModel->getWorkspace(0)->getNumberHistograms() - 1); - - m_uiForm->spPlotSpectrum->setMaximum(maxWsIndex); - m_uiForm->spPlotSpectrum->setMinimum(0); - m_uiForm->spPlotSpectrum->setValue(0); -} - void ConvFit::fitFunctionChanged() { - auto hwhmRangeSelector = m_uiForm->ppPlotTop->getRangeSelector("ConvFitHWHM"); - auto backRangeSelector = - m_uiForm->ppPlotTop->getRangeSelector("ConvFitBackRange"); - - if (selectedFitType().contains("Lorentzian")) - hwhmRangeSelector->setVisible(true); - else - hwhmRangeSelector->setVisible(false); - - if (backgroundName() == "None") - backRangeSelector->setVisible(false); - else - backRangeSelector->setVisible(true); m_convFittingModel->setFitTypeString(fitTypeString()); } -void ConvFit::parameterUpdated(const Mantid::API::IFunction *function) { - if (function == nullptr) - return; - - if (background() && function->asString() == background()->asString()) { - auto rangeSelector = - m_uiForm->ppPlotTop->getRangeSelector("ConvFitBackRange"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMinimum(function->getParameter("A0")); - } else if (function->hasParameter("FWHM")) { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("ConvFitHWHM"); - auto peakCentre = - lastParameterValue(function->name(), "PeakCentre").get_value_or(0); - auto hwhm = - lastParameterValue(function->name(), "FWHM").get_value_or(0) / 2.0; - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMaximum(peakCentre + hwhm); - rangeSelector->setMinimum(peakCentre - hwhm); - } -} - /** * Generate a string to describe the fit type selected by the user. * Used when naming the resultant workspaces. @@ -313,26 +169,6 @@ std::string ConvFit::fitTypeString() const { return fitType; } -/** - * Updates the plot in the GUI window - */ -void ConvFit::updatePreviewPlots() { - IndirectFitAnalysisTab::updatePlots(m_uiForm->ppPlotTop, - m_uiForm->ppPlotBottom); -} - -void ConvFit::updatePlotRange() { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("ConvFitRange"); - if (m_uiForm->ppPlotTop->hasCurve("Sample")) { - const auto range = m_uiForm->ppPlotTop->getCurveRange("Sample"); - rangeSelector->setRange(range.first, range.second); - } -} - -void ConvFit::disablePlotGuess() { m_uiForm->ckPlotGuess->setEnabled(false); } - -void ConvFit::enablePlotGuess() { m_uiForm->ckPlotGuess->setEnabled(true); } - void ConvFit::setPlotResultEnabled(bool enabled) { m_uiForm->pbPlot->setEnabled(enabled); m_uiForm->cbPlotType->setEnabled(enabled); @@ -342,74 +178,6 @@ void ConvFit::setSaveResultEnabled(bool enabled) { m_uiForm->pbSave->setEnabled(enabled); } -void ConvFit::enablePlotPreview() { m_uiForm->pbPlotPreview->setEnabled(true); } - -void ConvFit::disablePlotPreview() { - m_uiForm->pbPlotPreview->setEnabled(false); -} - -void ConvFit::addGuessPlot(Mantid::API::MatrixWorkspace_sptr workspace) { - m_uiForm->ppPlotTop->addSpectrum("Guess", workspace, 0, Qt::green); -} - -void ConvFit::removeGuessPlot() { - m_uiForm->ppPlotTop->removeSpectrum("Guess"); - m_uiForm->ckPlotGuess->setChecked(false); -} - -void ConvFit::startXChanged(double startX) { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("ConvFitRange"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMinimum(startX); -} - -void ConvFit::endXChanged(double endX) { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("ConvFitRange"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMaximum(endX); -} - -void ConvFit::hwhmMinChanged(double val) { - const auto peakCentre = - lastParameterValue("Lorentzian", "PeakCentre").get_value_or(0); - const double difference = peakCentre - val; - - auto hwhmRangeSelector = m_uiForm->ppPlotTop->getRangeSelector("ConvFitHWHM"); - MantidQt::API::SignalBlocker<QObject> blocker(hwhmRangeSelector); - hwhmRangeSelector->setMaximum(peakCentre + difference); - fwhmChanged(std::fabs(difference) * 2.0); -} - -void ConvFit::hwhmMaxChanged(double val) { - const double peakCentre = - lastParameterValue("Lorentzian", "PeakCentre").get_value_or(0); - const double difference = val - peakCentre; - - auto hwhmRangeSelector = m_uiForm->ppPlotTop->getRangeSelector("ConvFitHWHM"); - MantidQt::API::SignalBlocker<QObject> blocker(hwhmRangeSelector); - hwhmRangeSelector->setMinimum(peakCentre - difference); - fwhmChanged(std::fabs(difference) * 2.0); -} - -void ConvFit::fwhmChanged(double fwhm) { - // Update the property - m_convFittingModel->setDefaultParameterValue("FWHM", fwhm, 0); - setParameterValue("Lorentzian", "FWHM", fwhm); -} - -void ConvFit::backgLevel(double val) { - m_convFittingModel->setDefaultParameterValue("A0", val, 0); - setParameterValue("LinearBackground", "A0", val); - setParameterValue("FlatBackground", "A0", val); -} - -void ConvFit::updateHWHMFromResolution() { - auto resolution = - m_convFittingModel->getInstrumentResolution(0).get_value_or(0); - if (resolution > 0 && selectedFitType().contains("Lorentzian")) - hwhmMaxChanged(resolution / 2.0); -} - } // namespace IDA } // namespace CustomInterfaces } // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/ConvFit.h b/qt/scientific_interfaces/Indirect/ConvFit.h index 52e1d9983f1ce74e98f8ed3341e7e4c9d09f687d..f23d91b786a3c98be07ab8f9ed37d68a35bdc4e4 100644 --- a/qt/scientific_interfaces/Indirect/ConvFit.h +++ b/qt/scientific_interfaces/Indirect/ConvFit.h @@ -18,54 +18,28 @@ class DLLExport ConvFit : public IndirectFitAnalysisTab { public: ConvFit(QWidget *parent = nullptr); - bool canPlotGuess() const override; - - bool doPlotGuess() const override; - protected: void setPlotResultEnabled(bool enabled) override; void setSaveResultEnabled(bool enabled) override; - void enablePlotPreview() override; - void disablePlotPreview() override; - void addGuessPlot(Mantid::API::MatrixWorkspace_sptr workspace) override; - void removeGuessPlot() override; private: void setupFitTab() override; void setupFit(Mantid::API::IAlgorithm_sptr fitAlgorithm) override; - bool validate() override; - void loadSettings(const QSettings &settings) override; protected slots: void setModelResolution(const QString &resolutionName); - void newDataLoaded(const QString &wsName); - void updatePreviewPlots() override; - void updatePlotRange() override; - void startXChanged(double startX) override; - void endXChanged(double endX) override; - void backgLevel(double); - void hwhmMinChanged(double); - void hwhmMaxChanged(double); - void updateHWHMFromResolution(); void saveClicked(); void plotClicked(); void updatePlotOptions() override; void fitFunctionChanged(); - void parameterUpdated(const Mantid::API::IFunction *function); private: - void fwhmChanged(double fwhm); - - void disablePlotGuess() override; - void enablePlotGuess() override; - std::string fitTypeString() const; std::unique_ptr<Ui::ConvFit> m_uiForm; // ShortHand Naming for fit functions QHash<QString, std::string> m_fitStrings; - QHash<QString, std::string> m_fitTypeToFunction; ConvFitModel *m_convFittingModel; }; diff --git a/qt/scientific_interfaces/Indirect/ConvFit.ui b/qt/scientific_interfaces/Indirect/ConvFit.ui index 3489673fd1bee0fd5b24f62ae648b0c8df8f2ad6..d307c55349734c5d6b994824678d8a6aee273037 100644 --- a/qt/scientific_interfaces/Indirect/ConvFit.ui +++ b/qt/scientific_interfaces/Indirect/ConvFit.ui @@ -6,410 +6,179 @@ <rect> <x>0</x> <y>0</y> - <width>803</width> - <height>800</height> + <width>719</width> + <height>809</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_42"> + <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="QGroupBox" name="gbInput"> - <property name="title"> - <string>Input</string> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> - <layout class="QGridLayout" name="layoutFiles"> - <item row="0" column="0"> - <widget class="QLabel" name="lbSample"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Sample</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="lbResolution"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Resolution</string> - </property> - </widget> - </item> - <item row="0" column="1" colspan="2"> - <widget class="MantidQt::MantidWidgets::DataSelector" name="dsSampleInput" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoLoad" stdset="0"> - <bool>true</bool> - </property> - <property name="workspaceSuffixes" stdset="0"> - <stringlist> - <string>_red</string> - <string>_sqw</string> - </stringlist> - </property> - <property name="fileBrowserSuffixes" stdset="0"> - <stringlist> - <string>_red.nxs</string> - <string>_sqw.nxs</string> - <string>.sqw</string> - <string>.red</string> - </stringlist> - </property> - <property name="showLoad" stdset="0"> - <bool>false</bool> - </property> - <property name="ShowGroups" stdset="0"> - <bool>false</bool> - </property> - </widget> - </item> - <item row="1" column="1" colspan="2"> - <widget class="MantidQt::MantidWidgets::DataSelector" name="dsResInput" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoLoad" stdset="0"> - <bool>true</bool> - </property> - <property name="workspaceSuffixes" stdset="0"> - <stringlist> - <string>_res</string> - <string>_red</string> - <string>_sqw</string> - </stringlist> - </property> - <property name="fileBrowserSuffixes" stdset="0"> - <stringlist> - <string>_res.nxs</string> - <string>_red.nxs</string> - <string>_sqw.nxs</string> - </stringlist> - </property> - <property name="showLoad" stdset="0"> - <bool>false</bool> - </property> - <property name="ShowGroups" stdset="0"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="loPlotArea" stretch="1,1"> - <property name="sizeConstraint"> - <enum>QLayout::SetNoConstraint</enum> + <property name="childrenCollapsible"> + <bool>false</bool> </property> - <item> - <widget class="MantidQt::MantidWidgets::IndirectFitPropertyBrowser" name="fitPropertyBrowser"> - <widget class="QWidget" name="dockWidgetContents_2"/> - </widget> - </item> - <item> - <layout class="QVBoxLayout" name="loMiniPlot" stretch="0,0"> - <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> - </property> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectFitDataView" name="fitDataView" native="true"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + <widget class="QWidget" name="layoutWidget"> + <layout class="QVBoxLayout" name="verticalLayout"> <item> - <layout class="QVBoxLayout" name="loPlots" stretch="5,3"> + <layout class="QHBoxLayout" name="loPlotArea" stretch="1,1"> <property name="sizeConstraint"> - <enum>QLayout::SetDefaultConstraint</enum> + <enum>QLayout::SetNoConstraint</enum> </property> <item> - <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotTop" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>125</height> - </size> - </property> - <property name="showLegend" stdset="0"> - <bool>true</bool> - </property> - <property name="canvasColour" stdset="0"> - <color> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </property> + <widget class="MantidQt::MantidWidgets::IndirectFitPropertyBrowser" name="fitPropertyBrowser"> + <widget class="QWidget" name="dockWidgetContents_2"/> </widget> </item> <item> - <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotBottom" native="true"> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectFitPlotView" name="pvFitPlotView" native="true"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="loSpectrumView" stretch="0"> + <property name="topMargin"> + <number>0</number> + </property> + <item> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView" name="svSpectrumView" native="true"> <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>1</horstretch> + <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>0</width> - <height>75</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>16777215</width> - <height>16777215</height> + <height>0</height> </size> </property> - <property name="showLegend" stdset="0"> - <bool>true</bool> - </property> - <property name="canvasColour" stdset="0"> - <color> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </property> </widget> </item> </layout> </item> <item> - <layout class="QHBoxLayout" name="loPlotOptions"> - <property name="sizeConstraint"> - <enum>QLayout::SetNoConstraint</enum> + <widget class="QGroupBox" name="gbOutput"> + <property name="title"> + <string>Output</string> </property> - <item> - <widget class="QLabel" name="lbPlotSpectrum"> - <property name="text"> - <string>Plot Spectrum:</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spPlotSpectrum"> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>0</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbSingleFit"> - <property name="text"> - <string>Fit Single Spectrum</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbPlotPreview"> - <property name="text"> - <string>Plot Current Preview</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QCheckBox" name="ckPlotGuess"> - <property name="text"> - <string>Plot Guess</string> - </property> - </widget> - </item> - </layout> + <layout class="QHBoxLayout" name="horizontalLayout_10"> + <item> + <widget class="QLabel" name="lbPlotType"> + <property name="text"> + <string>Plot Output:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="cbPlotType"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>120</width> + <height>0</height> + </size> + </property> + <item> + <property name="text"> + <string>All</string> + </property> + </item> + <item> + <property name="text"> + <string>Amplitude</string> + </property> + </item> + <item> + <property name="text"> + <string>FWHM</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QPushButton" name="pbPlot"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Plot</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_1"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pbSave"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Save Result</string> + </property> + </widget> + </item> + </layout> + </widget> </item> </layout> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout" stretch="0"> - <property name="topMargin"> - <number>0</number> - </property> - <item> - <widget class="MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView" name="svSpectrumView" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>1</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QGroupBox" name="gbOutput"> - <property name="title"> - <string>Output</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_10"> - <item> - <widget class="QLabel" name="lbPlotType"> - <property name="text"> - <string>Plot Output:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="cbPlotType"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>120</width> - <height>0</height> - </size> - </property> - <item> - <property name="text"> - <string>All</string> - </property> - </item> - <item> - <property name="text"> - <string>Amplitude</string> - </property> - </item> - <item> - <property name="text"> - <string>FWHM</string> - </property> - </item> - </widget> - </item> - <item> - <widget class="QPushButton" name="pbPlot"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Plot</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_1"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbSave"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Save</string> - </property> - </widget> - </item> - </layout> + </widget> </widget> </item> </layout> </widget> <customwidgets> <customwidget> - <class>MantidQt::MantidWidgets::DataSelector</class> + <class>MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView</class> <extends>QWidget</extends> - <header>MantidQtWidgets/Common/DataSelector.h</header> + <header>IndirectSpectrumSelectionView.h</header> + <container>1</container> </customwidget> <customwidget> - <class>MantidQt::MantidWidgets::PreviewPlot</class> - <extends>QWidget</extends> - <header>MantidQtWidgets/LegacyQwt/PreviewPlot.h</header> + <class>MantidQt::MantidWidgets::IndirectFitPropertyBrowser</class> + <extends>QDockWidget</extends> + <header>MantidQtWidgets/Common/IndirectFitPropertyBrowser.h</header> <container>1</container> </customwidget> <customwidget> - <class>MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView</class> + <class>MantidQt::CustomInterfaces::IDA::IndirectFitPlotView</class> <extends>QWidget</extends> - <header>IndirectSpectrumSelectionView.h</header> + <header>IndirectFitPlotView.h</header> <container>1</container> </customwidget> <customwidget> - <class>MantidQt::MantidWidgets::IndirectFitPropertyBrowser</class> - <extends>QDockWidget</extends> - <header>MantidQtWidgets/Common/IndirectFitPropertyBrowser.h</header> + <class>MantidQt::CustomInterfaces::IDA::IndirectFitDataView</class> + <extends>QWidget</extends> + <header>IndirectFitDataView.h</header> <container>1</container> </customwidget> </customwidgets> diff --git a/qt/scientific_interfaces/Indirect/ConvFitAddWorkspaceDialog.cpp b/qt/scientific_interfaces/Indirect/ConvFitAddWorkspaceDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5cf6689ae030dd0887eec8091e111f4aca55a262 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/ConvFitAddWorkspaceDialog.cpp @@ -0,0 +1,138 @@ +#include "ConvFitAddWorkspaceDialog.h" + +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/MatrixWorkspace.h" + +#include <boost/optional.hpp> + +namespace { +using namespace Mantid::API; + +MatrixWorkspace_sptr getWorkspace(const std::string &name) { + return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name); +} + +boost::optional<std::size_t> maximumIndex(MatrixWorkspace_sptr workspace) { + if (workspace) { + const auto numberOfHistograms = workspace->getNumberHistograms(); + if (numberOfHistograms > 0) + return numberOfHistograms - 1; + } + return boost::none; +} + +QString getIndexString(MatrixWorkspace_sptr workspace) { + const auto maximum = maximumIndex(workspace); + if (maximum) + return QString("0-%1").arg(*maximum); + return ""; +} + +QString getIndexString(const std::string &workspaceName) { + return getIndexString(getWorkspace(workspaceName)); +} + +std::unique_ptr<QRegExpValidator> createValidator(const QString ®ex, + QObject *parent) { + return Mantid::Kernel::make_unique<QRegExpValidator>(QRegExp(regex), parent); +} + +QString OR(const QString &lhs, const QString &rhs) { + return "(" + lhs + "|" + rhs + ")"; +} + +QString NATURAL_NUMBER(std::size_t digits) { + return OR("0", "[1-9][0-9]{," + QString::number(digits - 1) + "}"); +} + +namespace Regexes { +const QString EMPTY = "^$"; +const QString SPACE = "(\\s)*"; +const QString COMMA = SPACE + "," + SPACE; +const QString MINUS = "\\-"; + +const QString NUMBER = NATURAL_NUMBER(4); +const QString NATURAL_RANGE = "(" + NUMBER + MINUS + NUMBER + ")"; +const QString NATURAL_OR_RANGE = OR(NATURAL_RANGE, NUMBER); +const QString SPECTRA_LIST = + "(" + NATURAL_OR_RANGE + "(" + COMMA + NATURAL_OR_RANGE + ")*)"; +} // namespace Regexes +} // namespace + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +ConvFitAddWorkspaceDialog::ConvFitAddWorkspaceDialog(QWidget *parent) + : IAddWorkspaceDialog(parent) { + m_uiForm.setupUi(this); + m_uiForm.leWorkspaceIndices->setValidator( + createValidator(Regexes::SPECTRA_LIST, this).release()); + setAllSpectraSelectionEnabled(false); + + connect(m_uiForm.dsWorkspace, SIGNAL(dataReady(const QString &)), this, + SLOT(workspaceChanged(const QString &))); + connect(m_uiForm.ckAllSpectra, SIGNAL(stateChanged(int)), this, + SLOT(selectAllSpectra(int))); +} + +std::string ConvFitAddWorkspaceDialog::workspaceName() const { + return m_uiForm.dsWorkspace->getCurrentDataName().toStdString(); +} + +std::string ConvFitAddWorkspaceDialog::resolutionName() const { + return m_uiForm.dsResolution->getCurrentDataName().toStdString(); +} + +std::string ConvFitAddWorkspaceDialog::workspaceIndices() const { + return m_uiForm.leWorkspaceIndices->text().toStdString(); +} + +void ConvFitAddWorkspaceDialog::setWSSuffices(const QStringList &suffices) { + m_uiForm.dsWorkspace->setWSSuffixes(suffices); +} + +void ConvFitAddWorkspaceDialog::setFBSuffices(const QStringList &suffices) { + m_uiForm.dsWorkspace->setFBSuffixes(suffices); +} + +void ConvFitAddWorkspaceDialog::setResolutionWSSuffices( + const QStringList &suffices) { + m_uiForm.dsResolution->setWSSuffixes(suffices); +} + +void ConvFitAddWorkspaceDialog::setResolutionFBSuffices( + const QStringList &suffices) { + m_uiForm.dsResolution->setFBSuffixes(suffices); +} + +void ConvFitAddWorkspaceDialog::selectAllSpectra(int state) { + if (state == Qt::Checked) { + m_uiForm.leWorkspaceIndices->setText(getIndexString(workspaceName())); + m_uiForm.leWorkspaceIndices->setEnabled(false); + } else + m_uiForm.leWorkspaceIndices->setEnabled(true); +} + +void ConvFitAddWorkspaceDialog::workspaceChanged(const QString &workspaceName) { + const auto name = workspaceName.toStdString(); + const auto workspace = getWorkspace(name); + if (workspace) + setWorkspace(name); + else + setAllSpectraSelectionEnabled(false); +} + +void ConvFitAddWorkspaceDialog::setWorkspace(const std::string &workspace) { + setAllSpectraSelectionEnabled(true); + if (m_uiForm.ckAllSpectra->isChecked()) + m_uiForm.leWorkspaceIndices->setText(getIndexString(workspace)); +} + +void ConvFitAddWorkspaceDialog::setAllSpectraSelectionEnabled(bool doEnable) { + m_uiForm.ckAllSpectra->setEnabled(doEnable); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/ConvFitAddWorkspaceDialog.h b/qt/scientific_interfaces/Indirect/ConvFitAddWorkspaceDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..8d7ecbc751fce613997d22f0a683b917d6a94d6d --- /dev/null +++ b/qt/scientific_interfaces/Indirect/ConvFitAddWorkspaceDialog.h @@ -0,0 +1,40 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_CONVFITADDWORKSPACEDIALOG_H_ +#define MANTIDQTCUSTOMINTERFACES_CONVFITADDWORKSPACEDIALOG_H_ + +#include "IAddWorkspaceDialog.h" +#include "ui_ConvFitAddWorkspaceDialog.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class ConvFitAddWorkspaceDialog : public IAddWorkspaceDialog { + Q_OBJECT +public: + explicit ConvFitAddWorkspaceDialog(QWidget *parent); + + std::string workspaceName() const override; + std::string resolutionName() const; + std::string workspaceIndices() const; + + void setWSSuffices(const QStringList &suffices) override; + void setFBSuffices(const QStringList &suffices) override; + void setResolutionWSSuffices(const QStringList &suffices); + void setResolutionFBSuffices(const QStringList &suffices); + +private slots: + void selectAllSpectra(int state); + void workspaceChanged(const QString &workspaceName); + +private: + void setWorkspace(const std::string &workspace); + void setAllSpectraSelectionEnabled(bool doEnable); + + Ui::ConvFitAddWorkspaceDialog m_uiForm; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACES_CONVFITADDWORKSPACEDIALOG_H_ */ diff --git a/qt/scientific_interfaces/Indirect/ConvFitAddWorkspaceDialog.ui b/qt/scientific_interfaces/Indirect/ConvFitAddWorkspaceDialog.ui new file mode 100644 index 0000000000000000000000000000000000000000..e1634010baceac7f32c3a4d3eebe5d8e4af6b115 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/ConvFitAddWorkspaceDialog.ui @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConvFitAddWorkspaceDialog</class> + <widget class="QDialog" name="ConvFitAddWorkspaceDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>393</width> + <height>181</height> + </rect> + </property> + <property name="windowTitle"> + <string>Indirect Data Selection</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="gbSelectData"> + <property name="title"> + <string>Select data</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QGridLayout" name="loInput"> + <item row="0" column="0"> + <widget class="QLabel" name="lbWorkspace"> + <property name="text"> + <string>Workspace</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="lbResolution"> + <property name="text"> + <string>Resolution</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QCheckBox" name="ckAllSpectra"> + <property name="text"> + <string>All Spectra</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="MantidQt::MantidWidgets::DataSelector" name="dsWorkspace" native="true"> + <property name="ShowGroups" stdset="0"> + <bool>false</bool> + </property> + <property name="showLoad" stdset="0"> + <bool>false</bool> + </property> + <property name="autoLoad" stdset="0"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="MantidQt::MantidWidgets::DataSelector" name="dsResolution" native="true"> + <property name="ShowGroups" stdset="0"> + <bool>false</bool> + </property> + <property name="showLoad" stdset="0"> + <bool>false</bool> + </property> + <property name="autoLoad" stdset="0"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="lbWorkspaceIndices"> + <property name="text"> + <string>Workspace Indices</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="leWorkspaceIndices"/> + </item> + </layout> + </item> + <item> + <spacer name="spacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>MantidQt::MantidWidgets::DataSelector</class> + <extends>QWidget</extends> + <header>MantidQtWidgets/Common/DataSelector.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ConvFitAddWorkspaceDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>ConvFitAddWorkspaceDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/qt/scientific_interfaces/Indirect/ConvFitDataPresenter.cpp b/qt/scientific_interfaces/Indirect/ConvFitDataPresenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..744e8fed8007a3341500c3164f50a703a9759cb2 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/ConvFitDataPresenter.cpp @@ -0,0 +1,61 @@ +#include "ConvFitDataPresenter.h" +#include "ConvFitAddWorkspaceDialog.h" +#include "ConvFitDataTablePresenter.h" + +#include "MantidKernel/make_unique.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +ConvFitDataPresenter::ConvFitDataPresenter(ConvFitModel *model, + IndirectFitDataView *view) + : IndirectFitDataPresenter( + model, view, Mantid::Kernel::make_unique<ConvFitDataTablePresenter>( + model, view->getDataTable())), + m_convModel(model) { + setResolutionHidden(false); + + connect(view, SIGNAL(resolutionLoaded(const QString &)), this, + SLOT(setModelResolution(const QString &))); + connect(view, SIGNAL(resolutionLoaded(const QString &)), this, + SIGNAL(singleResolutionLoaded())); +} + +void ConvFitDataPresenter::setModelResolution(const QString &name) { + m_convModel->setResolution(name.toStdString(), + m_convModel->numberOfWorkspaces() - 1); +} + +void ConvFitDataPresenter::addDataToModel(IAddWorkspaceDialog const *dialog) { + if (const auto convDialog = + dynamic_cast<ConvFitAddWorkspaceDialog const *>(dialog)) { + addWorkspace(convDialog, m_convModel); + m_convModel->setResolution(convDialog->resolutionName(), + m_convModel->numberOfWorkspaces() - 1); + } +} + +void ConvFitDataPresenter::addWorkspace(ConvFitAddWorkspaceDialog const *dialog, + IndirectFittingModel *model) { + model->addWorkspace(dialog->workspaceName(), dialog->workspaceIndices()); +} + +void ConvFitDataPresenter::addModelData(const std::string &name) { + IndirectFitDataPresenter::addModelData(name); + const auto resolution = getView()->getSelectedResolution(); + if (!resolution.empty()) + m_convModel->setResolution(resolution, 0); +} + +std::unique_ptr<IAddWorkspaceDialog> +ConvFitDataPresenter::getAddWorkspaceDialog(QWidget *parent) const { + auto dialog = Mantid::Kernel::make_unique<ConvFitAddWorkspaceDialog>(parent); + dialog->setResolutionFBSuffices(getView()->getResolutionFBSuffices()); + dialog->setResolutionWSSuffices(getView()->getResolutionWSSuffices()); + return std::move(dialog); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/ConvFitDataPresenter.h b/qt/scientific_interfaces/Indirect/ConvFitDataPresenter.h new file mode 100644 index 0000000000000000000000000000000000000000..c475d8b2efc1d48b0e585111592c026030fe3e32 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/ConvFitDataPresenter.h @@ -0,0 +1,56 @@ +#ifndef MANTIDQTCUSTOMINTERFACESIDA_CONVFITDATAPRESENTER_H_ +#define MANTIDQTCUSTOMINTERFACESIDA_CONVFITDATAPRESENTER_H_ + +#include "ConvFitModel.h" +#include "IndirectFitDataPresenter.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class ConvFitAddWorkspaceDialog; + +/** + A presenter. + Copyright © 2015-2016 ISIS Rutherford Appleton Laboratory, NScD + Oak Ridge National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport ConvFitDataPresenter : public IndirectFitDataPresenter { + Q_OBJECT +public: + ConvFitDataPresenter(ConvFitModel *model, IndirectFitDataView *view); + +private slots: + void setModelResolution(const QString &name); + +protected: + void addModelData(const std::string &name) override; + +private: + void addDataToModel(IAddWorkspaceDialog const *dialog) override; + std::unique_ptr<IAddWorkspaceDialog> + getAddWorkspaceDialog(QWidget *parent) const override; + void addWorkspace(ConvFitAddWorkspaceDialog const *dialog, + IndirectFittingModel *model); + + ConvFitModel *m_convModel; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACESIDA_CONVFITDATAPRESENTER_H_ */ diff --git a/qt/scientific_interfaces/Indirect/ConvFitDataTablePresenter.cpp b/qt/scientific_interfaces/Indirect/ConvFitDataTablePresenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b9a190b25508dd94a5a25a33d3f33f0b0804182 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/ConvFitDataTablePresenter.cpp @@ -0,0 +1,68 @@ +#include "ConvFitDataTablePresenter.h" + +#include <QComboBox> +#include <QHeaderView> + +namespace { +QStringList convFitHeaders() { + QStringList headers; + headers << "Workspace" + << "Resolution" + << "WS Index" + << "StartX" + << "EndX" + << "Exclude"; + return headers; +} +} // namespace + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +ConvFitDataTablePresenter::ConvFitDataTablePresenter(ConvFitModel *model, + QTableWidget *dataTable) + : IndirectDataTablePresenter(model, dataTable, convFitHeaders()), + m_convFitModel(model) { + auto header = dataTable->horizontalHeader(); + header->setResizeMode(1, QHeaderView::Stretch); +} + +int ConvFitDataTablePresenter::workspaceIndexColumn() const { return 2; } + +int ConvFitDataTablePresenter::startXColumn() const { return 3; } + +int ConvFitDataTablePresenter::endXColumn() const { return 4; } + +int ConvFitDataTablePresenter::excludeColumn() const { return 5; } + +std::string ConvFitDataTablePresenter::getResolutionName(int row) const { + return getString(row, 1); +} + +void ConvFitDataTablePresenter::addTableEntry(std::size_t dataIndex, + std::size_t spectrum, int row) { + IndirectDataTablePresenter::addTableEntry(dataIndex, spectrum, row); + + const auto resolution = m_convFitModel->getResolution(dataIndex); + const auto name = resolution ? resolution->getName() : ""; + auto cell = Mantid::Kernel::make_unique<QTableWidgetItem>( + QString::fromStdString(name)); + auto flags = cell->flags(); + flags ^= Qt::ItemIsEditable; + cell->setFlags(flags); + setCell(std::move(cell), row, 1); +} + +void ConvFitDataTablePresenter::updateTableEntry(std::size_t dataIndex, + std::size_t spectrum, + int row) { + IndirectDataTablePresenter::updateTableEntry(dataIndex, spectrum, row); + + const auto &name = m_convFitModel->getResolution(dataIndex)->getName(); + setCellText(QString::fromStdString(name), row, 1); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/ConvFitDataTablePresenter.h b/qt/scientific_interfaces/Indirect/ConvFitDataTablePresenter.h new file mode 100644 index 0000000000000000000000000000000000000000..1dac3990807002eb409311649769267376035f79 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/ConvFitDataTablePresenter.h @@ -0,0 +1,59 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_CONVFITDATATABLEPRESENTER_H_ +#define MANTIDQTCUSTOMINTERFACES_CONVFITDATATABLEPRESENTER_H_ + +#include "ConvFitModel.h" +#include "IndirectDataTablePresenter.h" + +#include <QTableWidget> + +#include <cstddef> +#include <unordered_map> + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +/** + Presenter for a table of convolution fitting data. + Copyright © 2015-2016 ISIS Rutherford Appleton Laboratory, NScD + Oak Ridge National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport ConvFitDataTablePresenter : public IndirectDataTablePresenter { + Q_OBJECT +public: + ConvFitDataTablePresenter(ConvFitModel *model, QTableWidget *dataTable); + +protected: + void addTableEntry(std::size_t dataIndex, std::size_t spectrum, + int row) override; + void updateTableEntry(std::size_t dataIndex, std::size_t spectrum, + int row) override; + +private: + int workspaceIndexColumn() const override; + int startXColumn() const override; + int endXColumn() const override; + int excludeColumn() const override; + std::string getResolutionName(int row) const; + + ConvFitModel *m_convFitModel; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACES_CONVFITDATATABLEPRESENTER_H_ */ diff --git a/qt/scientific_interfaces/Indirect/ConvFitModel.cpp b/qt/scientific_interfaces/Indirect/ConvFitModel.cpp index f51210d03e956388fb5c4219cc757e28db10b79c..9ef04ff0383f915faf192a075e55a6a679ab068d 100644 --- a/qt/scientific_interfaces/Indirect/ConvFitModel.cpp +++ b/qt/scientific_interfaces/Indirect/ConvFitModel.cpp @@ -302,7 +302,7 @@ IAlgorithm_sptr addSampleLogAlgorithm(Workspace_sptr workspace, } struct AddSampleLogRunner { - AddSampleLogRunner(MatrixWorkspace_sptr resultWorkspace, + AddSampleLogRunner(Workspace_sptr resultWorkspace, WorkspaceGroup_sptr resultGroup) : m_resultWorkspace(resultWorkspace), m_resultGroup(resultGroup) {} @@ -313,7 +313,7 @@ struct AddSampleLogRunner { } private: - MatrixWorkspace_sptr m_resultWorkspace; + Workspace_sptr m_resultWorkspace; WorkspaceGroup_sptr m_resultGroup; }; @@ -405,7 +405,7 @@ IAlgorithm_sptr ConvFitModel::simultaneousFitAlgorithm() const { std::string ConvFitModel::sequentialFitOutputName() const { if (isMultiFit()) - return "MultiConvFit_" + m_fitType + m_backgroundString; + return "MultiConvFit_" + m_fitType + m_backgroundString + "_Result"; return createOutputName( "%1%_conv_" + m_fitType + m_backgroundString + "_s%2%", "_to_", 0); } @@ -584,7 +584,7 @@ void ConvFitModel::addSampleLogs() { IndirectFitOutput ConvFitModel::createFitOutput( WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, const FitDataIterator &fitDataBegin, + WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) const { auto output = IndirectFitOutput(resultGroup, parameterTable, resultWorkspace, fitDataBegin, fitDataEnd); @@ -595,7 +595,7 @@ IndirectFitOutput ConvFitModel::createFitOutput( IndirectFitOutput ConvFitModel::createFitOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum) const { auto output = IndirectFitOutput(resultGroup, parameterTable, resultWorkspace, @@ -612,7 +612,7 @@ void ConvFitModel::addOutput(Mantid::API::IAlgorithm_sptr fitAlgorithm) { void ConvFitModel::addOutput(IndirectFitOutput *fitOutput, WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) const { fitOutput->addOutput(resultGroup, parameterTable, resultWorkspace, @@ -624,7 +624,7 @@ void ConvFitModel::addOutput(IndirectFitOutput *fitOutput, void ConvFitModel::addOutput(IndirectFitOutput *fitOutput, WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum) const { fitOutput->addOutput(resultGroup, parameterTable, resultWorkspace, fitData, diff --git a/qt/scientific_interfaces/Indirect/ConvFitModel.h b/qt/scientific_interfaces/Indirect/ConvFitModel.h index 593c3ba37245f07bb4c68801fa772c1a24a50965..91015857116411ea2974d92e6819fb2ad0a3ac13 100644 --- a/qt/scientific_interfaces/Indirect/ConvFitModel.h +++ b/qt/scientific_interfaces/Indirect/ConvFitModel.h @@ -46,26 +46,26 @@ private: IndirectFitOutput createFitOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) const override; IndirectFitOutput createFitOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum) const override; void addOutput(IndirectFitOutput *fitOutput, Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) const override; void addOutput(IndirectFitOutput *fitOutput, Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum) const override; void addExtendedResolution(std::size_t index); void addSampleLogs(); diff --git a/qt/scientific_interfaces/Indirect/IAddWorkspaceDialog.h b/qt/scientific_interfaces/Indirect/IAddWorkspaceDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..9024c2912f458e070f943ec766c2cf4023d4d838 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IAddWorkspaceDialog.h @@ -0,0 +1,22 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_IADDWORKSPACEDIALOG_H_ +#define MANTIDQTCUSTOMINTERFACES_IADDWORKSPACEDIALOG_H_ + +#include <QDialog> + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class IAddWorkspaceDialog : public QDialog { +public: + IAddWorkspaceDialog(QWidget *parent) : QDialog(parent) {} + virtual std::string workspaceName() const = 0; + virtual void setWSSuffices(const QStringList &suffices) = 0; + virtual void setFBSuffices(const QStringList &suffices) = 0; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACES_IADDWORKSPACEDIALOG_H_ */ diff --git a/qt/scientific_interfaces/Indirect/ISISEnergyTransfer.cpp b/qt/scientific_interfaces/Indirect/ISISEnergyTransfer.cpp index 057e230faa8a7871c45ea6a0f7e5dc7dcc9d39a9..7a48c3b849527844d75cf79bd1202f949df1065d 100644 --- a/qt/scientific_interfaces/Indirect/ISISEnergyTransfer.cpp +++ b/qt/scientific_interfaces/Indirect/ISISEnergyTransfer.cpp @@ -194,6 +194,10 @@ QString ISISEnergyTransfer::validateDetectorGrouping() { if (!m_uiForm.dsMapFile->isValid()) return "Mapping file is invalid."; } + if (m_uiForm.cbGroupingOptions->currentText() == "Custom") { + if (m_uiForm.leCustomGroups->text() == "") + return "Please supply a custom grouping for detectors."; + } return ""; } diff --git a/qt/scientific_interfaces/Indirect/ISISEnergyTransfer.ui b/qt/scientific_interfaces/Indirect/ISISEnergyTransfer.ui index 173915abf6765bc47bb5a98c60a17f4a01281981..09dbd67a51858b8dff66a090ca56671eb441771d 100644 --- a/qt/scientific_interfaces/Indirect/ISISEnergyTransfer.ui +++ b/qt/scientific_interfaces/Indirect/ISISEnergyTransfer.ui @@ -215,12 +215,12 @@ </property> <item> <property name="text"> - <string>Custom</string> + <string>Individual</string> </property> </item> <item> <property name="text"> - <string>Individual</string> + <string>Custom</string> </property> </item> <item> diff --git a/qt/scientific_interfaces/Indirect/IndirectAddWorkspaceDialog.cpp b/qt/scientific_interfaces/Indirect/IndirectAddWorkspaceDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8c7bb177c071a20d5587c43c7178101668f26b6c --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectAddWorkspaceDialog.cpp @@ -0,0 +1,125 @@ +#include "IndirectAddWorkspaceDialog.h" + +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/MatrixWorkspace.h" + +#include <boost/optional.hpp> + +namespace { +using namespace Mantid::API; + +MatrixWorkspace_sptr getWorkspace(const std::string &name) { + return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(name); +} + +boost::optional<std::size_t> maximumIndex(MatrixWorkspace_sptr workspace) { + if (workspace) { + const auto numberOfHistograms = workspace->getNumberHistograms(); + if (numberOfHistograms > 0) + return numberOfHistograms - 1; + } + return boost::none; +} + +QString getIndexString(MatrixWorkspace_sptr workspace) { + const auto maximum = maximumIndex(workspace); + if (maximum) { + if (*maximum > 0) + return QString("0-%1").arg(*maximum); + return "0"; + } + return ""; +} + +QString getIndexString(const std::string &workspaceName) { + return getIndexString(getWorkspace(workspaceName)); +} + +std::unique_ptr<QRegExpValidator> createValidator(const QString ®ex, + QObject *parent) { + return Mantid::Kernel::make_unique<QRegExpValidator>(QRegExp(regex), parent); +} + +QString OR(const QString &lhs, const QString &rhs) { + return "(" + lhs + "|" + rhs + ")"; +} + +QString NATURAL_NUMBER(std::size_t digits) { + return OR("0", "[1-9][0-9]{," + QString::number(digits - 1) + "}"); +} + +const QString EMPTY = "^$"; +const QString SPACE = "(\\s)*"; +const QString COMMA = SPACE + "," + SPACE; +const QString MINUS = "\\-"; + +const QString NUMBER = NATURAL_NUMBER(4); +const QString NATURAL_RANGE = "(" + NUMBER + MINUS + NUMBER + ")"; +const QString NATURAL_OR_RANGE = OR(NATURAL_RANGE, NUMBER); +const QString SPECTRA_LIST = + "(" + NATURAL_OR_RANGE + "(" + COMMA + NATURAL_OR_RANGE + ")*)"; +} // namespace + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +AddWorkspaceDialog::AddWorkspaceDialog(QWidget *parent) + : IAddWorkspaceDialog(parent) { + m_uiForm.setupUi(this); + m_uiForm.leWorkspaceIndices->setValidator( + createValidator(SPECTRA_LIST, this).release()); + setAllSpectraSelectionEnabled(false); + + connect(m_uiForm.dsWorkspace, SIGNAL(dataReady(const QString &)), this, + SLOT(workspaceChanged(const QString &))); + connect(m_uiForm.ckAllSpectra, SIGNAL(stateChanged(int)), this, + SLOT(selectAllSpectra(int))); +} + +std::string AddWorkspaceDialog::workspaceName() const { + return m_uiForm.dsWorkspace->getCurrentDataName().toStdString(); +} + +std::string AddWorkspaceDialog::workspaceIndices() const { + return m_uiForm.leWorkspaceIndices->text().toStdString(); +} + +void AddWorkspaceDialog::setWSSuffices(const QStringList &suffices) { + m_uiForm.dsWorkspace->setWSSuffixes(suffices); +} + +void AddWorkspaceDialog::setFBSuffices(const QStringList &suffices) { + m_uiForm.dsWorkspace->setFBSuffixes(suffices); +} + +void AddWorkspaceDialog::selectAllSpectra(int state) { + if (state == Qt::Checked) { + m_uiForm.leWorkspaceIndices->setText(getIndexString(workspaceName())); + m_uiForm.leWorkspaceIndices->setEnabled(false); + } else + m_uiForm.leWorkspaceIndices->setEnabled(true); +} + +void AddWorkspaceDialog::workspaceChanged(const QString &workspaceName) { + const auto name = workspaceName.toStdString(); + const auto workspace = getWorkspace(name); + if (workspace) + setWorkspace(name); + else + setAllSpectraSelectionEnabled(false); +} + +void AddWorkspaceDialog::setWorkspace(const std::string &workspace) { + setAllSpectraSelectionEnabled(true); + if (m_uiForm.ckAllSpectra->isChecked()) + m_uiForm.leWorkspaceIndices->setText(getIndexString(workspace)); +} + +void AddWorkspaceDialog::setAllSpectraSelectionEnabled(bool doEnable) { + m_uiForm.ckAllSpectra->setEnabled(doEnable); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IndirectAddWorkspaceDialog.h b/qt/scientific_interfaces/Indirect/IndirectAddWorkspaceDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..696f7b8d1c2cdb0da03a5e58c345e66a79e6dd14 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectAddWorkspaceDialog.h @@ -0,0 +1,37 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_INDIRECTADDWORKSPACEDIALOG_H_ +#define MANTIDQTCUSTOMINTERFACES_INDIRECTADDWORKSPACEDIALOG_H_ + +#include "IAddWorkspaceDialog.h" +#include "ui_IndirectAddWorkspaceDialog.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class AddWorkspaceDialog : public IAddWorkspaceDialog { + Q_OBJECT +public: + explicit AddWorkspaceDialog(QWidget *parent); + + std::string workspaceName() const override; + std::string workspaceIndices() const; + + void setWSSuffices(const QStringList &suffices) override; + void setFBSuffices(const QStringList &suffices) override; + +private slots: + void selectAllSpectra(int state); + void workspaceChanged(const QString &workspaceName); + +private: + void setWorkspace(const std::string &workspace); + void setAllSpectraSelectionEnabled(bool doEnable); + + Ui::IndirectAddWorkspaceDialog m_uiForm; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACES_INDIRECTADDWORKSPACEDIALOG_H_ */ diff --git a/qt/scientific_interfaces/Indirect/IndirectAddWorkspaceDialog.ui b/qt/scientific_interfaces/Indirect/IndirectAddWorkspaceDialog.ui new file mode 100644 index 0000000000000000000000000000000000000000..05630bc5a5fa146e31ba0e9f6ca9876bf94b4f08 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectAddWorkspaceDialog.ui @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>IndirectAddWorkspaceDialog</class> + <widget class="QDialog" name="IndirectAddWorkspaceDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>393</width> + <height>181</height> + </rect> + </property> + <property name="windowTitle"> + <string>Indirect Data Selection</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="gbSelectData"> + <property name="title"> + <string>Select data</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QGridLayout" name="loInput"> + <item row="0" column="1"> + <widget class="MantidQt::MantidWidgets::DataSelector" name="dsWorkspace" native="true"> + <property name="ShowGroups" stdset="0"> + <bool>false</bool> + </property> + <property name="showLoad" stdset="0"> + <bool>false</bool> + </property> + <property name="autoLoad" stdset="0"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="lbWorkspaceIndices"> + <property name="text"> + <string>Workspace Indices</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="lbWorkspace"> + <property name="text"> + <string>Workspace</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="leWorkspaceIndices"/> + </item> + <item row="2" column="1"> + <widget class="QCheckBox" name="ckAllSpectra"> + <property name="text"> + <string>All Spectra</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="spacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>MantidQt::MantidWidgets::DataSelector</class> + <extends>QWidget</extends> + <header>MantidQtWidgets/Common/DataSelector.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>IndirectAddWorkspaceDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>IndirectAddWorkspaceDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/qt/scientific_interfaces/Indirect/IndirectDataAnalysis.ui b/qt/scientific_interfaces/Indirect/IndirectDataAnalysis.ui index 0f9eef8556c5e3efea0f5585b800683d8c2aa1cd..f04cd6e8d582e0ca086bab0bd58fc7169b35f45b 100644 --- a/qt/scientific_interfaces/Indirect/IndirectDataAnalysis.ui +++ b/qt/scientific_interfaces/Indirect/IndirectDataAnalysis.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>757</width> + <width>799</width> <height>764</height> </rect> </property> @@ -83,7 +83,7 @@ </widget> <widget class="QWidget" name="tabJumpFit"> <attribute name="title"> - <string>JumpFit</string> + <string>F(Q) Fit</string> </attribute> </widget> </widget> diff --git a/qt/scientific_interfaces/Indirect/IndirectDataTablePresenter.cpp b/qt/scientific_interfaces/Indirect/IndirectDataTablePresenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef1012c7077d4c51ac7fbd4dc297a86ef00bdc9b --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectDataTablePresenter.cpp @@ -0,0 +1,587 @@ +#include "IndirectDataTablePresenter.h" + +#include "MantidQtWidgets/Common/SignalBlocker.h" + +#include <boost/numeric/conversion/cast.hpp> + +#include <QHeaderView> +#include <QItemDelegate> +#include <QLineEdit> +#include <QRegExpValidator> + +namespace { +using MantidQt::CustomInterfaces::IDA::Spectra; +using MantidQt::CustomInterfaces::IDA::DiscontinuousSpectra; + +namespace Regexes { +const QString EMPTY = "^$"; +const QString SPACE = "(\\s)*"; +const QString COMMA = SPACE + "," + SPACE; +const QString NATURAL_NUMBER = "(0|[1-9][0-9]*)"; +const QString REAL_NUMBER = "(-?" + NATURAL_NUMBER + "(\\.[0-9]*)?)"; +const QString REAL_RANGE = "(" + REAL_NUMBER + COMMA + REAL_NUMBER + ")"; +const QString MASK_LIST = + "(" + REAL_RANGE + "(" + COMMA + REAL_RANGE + ")*" + ")|" + EMPTY; +} // namespace Regexes + +class ExcludeRegionDelegate : public QItemDelegate { +public: + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, + const QModelIndex &) const override { + auto lineEdit = Mantid::Kernel::make_unique<QLineEdit>(parent); + auto validator = Mantid::Kernel::make_unique<QRegExpValidator>( + QRegExp(Regexes::MASK_LIST), parent); + lineEdit->setValidator(validator.release()); + return lineEdit.release(); + } + + void setEditorData(QWidget *editor, const QModelIndex &index) const override { + const auto value = index.model()->data(index, Qt::EditRole).toString(); + static_cast<QLineEdit *>(editor)->setText(value); + } + + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const override { + QLineEdit *lineEdit = static_cast<QLineEdit *>(editor); + model->setData(index, lineEdit->text(), Qt::EditRole); + } + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, + const QModelIndex &) const override { + editor->setGeometry(option.rect); + } +}; + +QStringList defaultHeaders() { + QStringList headers; + headers << "Workspace" + << "WS Index" + << "StartX" + << "EndX" + << "Exclude"; + return headers; +} + +QString makeNumber(double d) { return QString::number(d, 'g', 16); } + +std::string +pairsToString(const std::vector<std::pair<std::size_t, std::size_t>> &pairs) { + std::vector<std::string> pairStrings; + for (auto const &value : pairs) { + if (value.first == value.second) + pairStrings.emplace_back(std::to_string(value.first)); + else + pairStrings.emplace_back(std::to_string(value.first) + "-" + + std::to_string(value.second)); + } + return boost::algorithm::join(pairStrings, ","); +} + +boost::optional<Spectra> +pairsToSpectra(const std::vector<std::pair<std::size_t, std::size_t>> &pairs) { + if (pairs.empty()) + return boost::none; + else if (pairs.size() == 1) + return boost::optional<Spectra>(pairs[0]); + return boost::optional<Spectra>( + DiscontinuousSpectra<std::size_t>(pairsToString(pairs))); +} + +QVariant getVariant(std::size_t i) { + return QVariant::fromValue<qulonglong>(i); +} +} // namespace + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +IndirectDataTablePresenter::IndirectDataTablePresenter( + IndirectFittingModel *model, QTableWidget *dataTable) + : IndirectDataTablePresenter(model, dataTable, defaultHeaders()) {} + +IndirectDataTablePresenter::IndirectDataTablePresenter( + IndirectFittingModel *model, QTableWidget *dataTable, + const QStringList &headers) + : m_model(model), m_dataTable(dataTable) { + setHorizontalHeaders(headers); + m_dataTable->setItemDelegateForColumn( + headers.size() - 1, + Mantid::Kernel::make_unique<ExcludeRegionDelegate>().release()); + m_dataTable->verticalHeader()->setVisible(false); + + connect(m_dataTable, SIGNAL(cellChanged(int, int)), this, + SLOT(setModelFittingRange(int, int))); + connect(m_dataTable, SIGNAL(cellChanged(int, int)), this, + SLOT(updateAllFittingRangeFrom(int, int))); +} + +bool IndirectDataTablePresenter::tableDatasetsMatchModel() const { + if (m_dataPositions.size() != m_model->numberOfWorkspaces()) + return false; + + for (auto i = 0u; i < m_dataPositions.size(); ++i) { + if (m_model->getWorkspace(i)->getName() != + getWorkspaceName(m_dataPositions[i])) + return false; + } + return true; +} + +bool IndirectDataTablePresenter::isTableEmpty() const { + return m_dataPositions.empty(); +} + +int IndirectDataTablePresenter::workspaceIndexColumn() const { return 1; } + +int IndirectDataTablePresenter::startXColumn() const { return 2; } + +int IndirectDataTablePresenter::endXColumn() const { return 3; } + +int IndirectDataTablePresenter::excludeColumn() const { return 4; } + +double IndirectDataTablePresenter::startX(int row) const { + return getDouble(row, startXColumn()); +} + +double IndirectDataTablePresenter::endX(int row) const { + return getDouble(row, endXColumn()); +} + +std::string IndirectDataTablePresenter::getExcludeString(int row) const { + return getString(row, excludeColumn()); +} + +std::string IndirectDataTablePresenter::getWorkspaceName(int row) const { + return getString(row, 0); +} + +std::size_t IndirectDataTablePresenter::getWorkspaceIndex(int row) const { + const auto item = m_dataTable->item(row, workspaceIndexColumn()); + return static_cast<std::size_t>(item->text().toULongLong()); +} + +double IndirectDataTablePresenter::getDouble(int row, int column) const { + return getText(row, column).toDouble(); +} + +std::string IndirectDataTablePresenter::getString(int row, int column) const { + return getText(row, column).toStdString(); +} + +QString IndirectDataTablePresenter::getText(int row, int column) const { + return m_dataTable->item(row, column)->text(); +} + +int IndirectDataTablePresenter::getNextPosition(std::size_t index) const { + if (m_dataPositions.size() > index + 1) + return m_dataPositions[index + 1]; + return m_dataTable->rowCount(); +} + +int IndirectDataTablePresenter::getFirstRow(std::size_t dataIndex) const { + if (m_dataPositions.size() > dataIndex) + return m_dataPositions[dataIndex]; + return -1; +} + +std::size_t IndirectDataTablePresenter::getDataIndex(int row) const { + return m_dataTable->item(row, 0)->data(Qt::UserRole).toULongLong(); +} + +boost::optional<Spectra> +IndirectDataTablePresenter::getSpectra(std::size_t dataIndex) const { + if (m_dataPositions.size() > dataIndex) + return getSpectra(m_dataPositions[dataIndex], getNextPosition(dataIndex)); + return boost::none; +} + +boost::optional<Spectra> IndirectDataTablePresenter::getSpectra(int start, + int end) const { + std::vector<std::pair<std::size_t, std::size_t>> spectraPairs; + while (start < end) { + std::size_t minimum = getWorkspaceIndex(start); + std::size_t maximum = minimum; + + while (++start < end && getWorkspaceIndex(start) == maximum + 1) + ++maximum; + spectraPairs.emplace_back(minimum, maximum); + } + return pairsToSpectra(spectraPairs); +} + +boost::optional<int> +IndirectDataTablePresenter::getRowIndex(std::size_t dataIndex, + int spectrumIndex) const { + const auto position = m_dataPositions[dataIndex] + spectrumIndex; + if (getNextPosition(dataIndex) > position) + return position; + return boost::none; +} + +void IndirectDataTablePresenter::setStartX(double startX, std::size_t dataIndex, + int spectrumIndex) { + if (FittingMode::SEQUENTIAL == m_model->getFittingMode()) + setStartX(startX); + else if (auto row = getRowIndex(dataIndex, spectrumIndex)) + setStartX(startX, *row); +} + +void IndirectDataTablePresenter::setEndX(double endX, std::size_t dataIndex, + int spectrumIndex) { + if (FittingMode::SEQUENTIAL == m_model->getFittingMode()) + setEndX(endX); + else if (auto row = getRowIndex(dataIndex, spectrumIndex)) + setEndX(endX, *row); +} + +void IndirectDataTablePresenter::setExclude(const std::string &exclude, + std::size_t dataIndex, + int spectrumIndex) { + if (FittingMode::SEQUENTIAL == m_model->getFittingMode()) + setExcludeRegion(exclude); + else if (auto row = getRowIndex(dataIndex, spectrumIndex)) + setExcludeRegion(exclude, *row); +} + +void IndirectDataTablePresenter::addData(std::size_t index) { + if (m_dataPositions.size() > index) + updateData(index); + else + addNewData(index); +} + +void IndirectDataTablePresenter::addNewData(std::size_t index) { + MantidQt::API::SignalBlocker<QObject> blocker(m_dataTable); + const auto start = m_dataTable->rowCount(); + + const auto addRow = + [&](std::size_t spectrum) { addTableEntry(index, spectrum); }; + m_model->applySpectra(index, addRow); + + if (m_model->numberOfWorkspaces() > m_dataPositions.size()) + m_dataPositions.emplace_back(start); +} + +void IndirectDataTablePresenter::updateData(std::size_t index) { + if (m_dataPositions.size() > index) + updateExistingData(index); + else + addNewData(index); +} + +void IndirectDataTablePresenter::updateExistingData(std::size_t index) { + MantidQt::API::SignalBlocker<QObject> blocker(m_dataTable); + auto position = m_dataPositions[index]; + const auto nextPosition = getNextPosition(index); + const auto initialSize = nextPosition - position; + + const auto updateRow = [&](std::size_t spectrum) { + if (position < nextPosition) + updateTableEntry(index, spectrum, position++); + else + addTableEntry(index, spectrum, position++); + }; + m_model->applySpectra(index, updateRow); + + collapseData(position, nextPosition, initialSize, index); +} + +void IndirectDataTablePresenter::collapseData(int from, int to, int initialSize, + std::size_t dataIndex) { + const auto shift = from - to; + if (shift != 0) { + for (auto i = from; i < to; ++i) + removeTableEntry(from); + + if (initialSize + shift == 0 && m_dataPositions.size() > dataIndex) { + m_dataPositions.erase(m_dataPositions.begin() + dataIndex); + shiftDataPositions(shift, dataIndex, m_dataPositions.size()); + updateDataPositionsInCells(dataIndex, m_dataPositions.size()); + } else + shiftDataPositions(shift, dataIndex + 1, m_dataPositions.size()); + } +} + +void IndirectDataTablePresenter::removeSelectedData() { + MantidQt::API::SignalBlocker<QObject> blocker(m_dataTable); + auto selectedIndices = m_dataTable->selectionModel()->selectedIndexes(); + const auto modifiedIndicesAndCount = removeTableRows(selectedIndices); + const auto &modifiedCount = modifiedIndicesAndCount.second; + auto &modifiedIndices = modifiedIndicesAndCount.first; + + for (auto i = 0u; i < modifiedIndices.size(); ++i) + shiftDataPositions(-static_cast<int>(modifiedCount[i]), + modifiedIndices[i] + 1, m_dataPositions.size()); + + if (!modifiedIndices.empty()) { + updateFromRemovedIndices(modifiedIndices); + updateDataPositionsInCells( + modifiedIndices.back() > 0 ? modifiedIndices.back() - 1 : 0, + m_dataPositions.size()); + } +} + +void IndirectDataTablePresenter::updateFromRemovedIndices( + const std::vector<std::size_t> &indices) { + for (const auto &index : indices) { + const auto spectra = getSpectra(index); + if (spectra) + m_model->setSpectra(*spectra, index); + else { + const auto numberOfWorkspaces = m_model->numberOfWorkspaces(); + m_model->removeWorkspace(index); + m_dataPositions.erase(m_dataPositions.begin() + index); + + if (m_model->numberOfWorkspaces() == numberOfWorkspaces - 2) + m_dataPositions.erase(m_dataPositions.begin() + index); + } + } +} + +std::pair<std::vector<std::size_t>, std::vector<std::size_t>> +IndirectDataTablePresenter::removeTableRows(QModelIndexList &selectedRows) { + std::vector<std::size_t> modifiedIndices; + std::vector<std::size_t> modifiedCount; + int previous = -1; + + qSort(selectedRows); + for (auto i = selectedRows.count() - 1; i >= 0; --i) { + const auto current = selectedRows[i].row(); + if (current != previous) { + auto modifiedIndex = removeTableEntry(current); + + if (!modifiedIndices.empty() && modifiedIndices.back() == modifiedIndex) + ++modifiedCount.back(); + else { + modifiedIndices.emplace_back(modifiedIndex); + modifiedCount.emplace_back(1); + } + previous = current; + } + } + return {modifiedIndices, modifiedCount}; +} + +void IndirectDataTablePresenter::setModelFittingRange(int row, int column) { + const auto workspaceIndex = getWorkspaceIndex(row); + const auto dataIndex = getDataIndex(row); + + if (startXColumn() == column) + setModelStartXAndEmit(getDouble(row, column), dataIndex, workspaceIndex); + else if (endXColumn() == column) + setModelEndXAndEmit(getDouble(row, column), dataIndex, workspaceIndex); + else if (excludeColumn() == column) + setModelExcludeAndEmit(getString(row, column), dataIndex, workspaceIndex); +} + +void IndirectDataTablePresenter::setModelStartXAndEmit( + double startX, std::size_t dataIndex, std::size_t workspaceIndex) { + m_model->setStartX(startX, dataIndex, workspaceIndex); + emit startXChanged(startX, dataIndex, workspaceIndex); +} + +void IndirectDataTablePresenter::setModelEndXAndEmit( + double endX, std::size_t dataIndex, std::size_t workspaceIndex) { + m_model->setEndX(endX, dataIndex, workspaceIndex); + emit endXChanged(endX, dataIndex, workspaceIndex); +} + +void IndirectDataTablePresenter::setModelExcludeAndEmit( + const std::string &exclude, std::size_t dataIndex, + std::size_t workspaceIndex) { + m_model->setExcludeRegion(exclude, dataIndex, workspaceIndex); + emit excludeRegionChanged(exclude, dataIndex, workspaceIndex); +} + +void IndirectDataTablePresenter::setGlobalFittingRange(bool global) { + if (global) + enableGlobalFittingRange(); + else + disableGlobalFittingRange(); +} + +void IndirectDataTablePresenter::updateAllFittingRangeFrom(int row, + int column) { + MantidQt::API::SignalBlocker<QObject> blocker(m_dataTable); + if (startXColumn() == column) + setStartX(getDouble(row, column)); + else if (endXColumn() == column) + setEndX(getDouble(row, column)); + else if (excludeColumn() == column) + setExcludeRegion(getText(row, column)); +} + +void IndirectDataTablePresenter::enableGlobalFittingRange() { + MantidQt::API::SignalBlocker<QObject> blocker(m_dataTable); + const auto range = m_model->getFittingRange(0, 0); + setStartX(range.first); + setEndX(range.second); + setExcludeRegion(m_model->getExcludeRegion(0, 0)); + connect(m_dataTable, SIGNAL(cellChanged(int, int)), this, + SLOT(updateAllFittingRangeFrom(int, int))); +} + +void IndirectDataTablePresenter::disableGlobalFittingRange() { + disconnect(m_dataTable, SIGNAL(cellChanged(int, int)), this, + SLOT(updateAllFittingRangeFrom(int, int))); +} + +void IndirectDataTablePresenter::enableTable() { + m_dataTable->setEnabled(true); +} + +void IndirectDataTablePresenter::disableTable() { + m_dataTable->setDisabled(true); +} + +void IndirectDataTablePresenter::clearTable() { + m_dataTable->setRowCount(0); + m_dataPositions.clear(); +} + +void IndirectDataTablePresenter::setStartX(double startX, int index) { + MantidQt::API::SignalBlocker<QObject> blocker(m_dataTable); + if (FittingMode::SEQUENTIAL == m_model->getFittingMode()) + setStartX(startX); + else + m_dataTable->item(index, startXColumn())->setText(makeNumber(startX)); +} + +void IndirectDataTablePresenter::setEndX(double endX, int index) { + MantidQt::API::SignalBlocker<QObject> blocker(m_dataTable); + if (FittingMode::SEQUENTIAL == m_model->getFittingMode()) + setEndX(endX); + else + m_dataTable->item(index, endXColumn())->setText(makeNumber(endX)); +} + +void IndirectDataTablePresenter::setExcludeRegion(const std::string &exclude, + int index) { + MantidQt::API::SignalBlocker<QObject> blocker(m_dataTable); + if (FittingMode::SEQUENTIAL == m_model->getFittingMode()) + setExcludeRegion(exclude); + else + m_dataTable->item(index, excludeColumn()) + ->setText(QString::fromStdString(exclude)); +} + +void IndirectDataTablePresenter::setStartX(double startX) { + setColumnValues(startXColumn(), makeNumber(startX)); +} + +void IndirectDataTablePresenter::setEndX(double endX) { + setColumnValues(endXColumn(), makeNumber(endX)); +} + +void IndirectDataTablePresenter::setExcludeRegion(const std::string &exclude) { + setExcludeRegion(QString::fromStdString(exclude)); +} + +void IndirectDataTablePresenter::setExcludeRegion(const QString &exclude) { + setColumnValues(excludeColumn(), exclude); +} + +void IndirectDataTablePresenter::setColumnValues(int column, + const QString &value) { + MantidQt::API::SignalBlocker<QObject> blocker(m_dataTable); + for (int i = 0; i < m_dataTable->rowCount(); ++i) + m_dataTable->item(i, column)->setText(value); +} + +void IndirectDataTablePresenter::setHorizontalHeaders( + const QStringList &headers) { + m_dataTable->setColumnCount(headers.size()); + m_dataTable->setHorizontalHeaderLabels(headers); + + auto header = m_dataTable->horizontalHeader(); + header->setResizeMode(0, QHeaderView::Stretch); +} + +void IndirectDataTablePresenter::addTableEntry(std::size_t dataIndex, + std::size_t spectrum) { + const auto row = m_dataTable->rowCount(); + addTableEntry(dataIndex, spectrum, row); + m_dataTable->item(row, 0)->setData(Qt::UserRole, getVariant(dataIndex)); +} + +void IndirectDataTablePresenter::addTableEntry(std::size_t dataIndex, + std::size_t spectrum, int row) { + m_dataTable->insertRow(row); + + const auto &name = m_model->getWorkspace(dataIndex)->getName(); + auto cell = Mantid::Kernel::make_unique<QTableWidgetItem>( + QString::fromStdString(name)); + auto flags = cell->flags(); + flags ^= Qt::ItemIsEditable; + cell->setFlags(flags); + setCell(std::move(cell), row, 0); + + cell = + Mantid::Kernel::make_unique<QTableWidgetItem>(QString::number(spectrum)); + cell->setFlags(flags); + setCell(std::move(cell), row, workspaceIndexColumn()); + + const auto range = m_model->getFittingRange(dataIndex, spectrum); + cell = Mantid::Kernel::make_unique<QTableWidgetItem>(makeNumber(range.first)); + setCell(std::move(cell), row, startXColumn()); + + cell = + Mantid::Kernel::make_unique<QTableWidgetItem>(makeNumber(range.second)); + setCell(std::move(cell), row, endXColumn()); + + const auto exclude = m_model->getExcludeRegion(dataIndex, spectrum); + cell = Mantid::Kernel::make_unique<QTableWidgetItem>( + QString::fromStdString(exclude)); + setCell(std::move(cell), row, excludeColumn()); +} + +void IndirectDataTablePresenter::setCell(std::unique_ptr<QTableWidgetItem> cell, + int row, int column) { + m_dataTable->setItem(row, column, cell.release()); +} + +void IndirectDataTablePresenter::updateTableEntry(std::size_t dataIndex, + std::size_t spectrum, + int row) { + const auto &name = m_model->getWorkspace(dataIndex)->getName(); + setCellText(QString::fromStdString(name), row, 0); + setCellText(QString::number(spectrum), row, workspaceIndexColumn()); + + const auto range = m_model->getFittingRange(dataIndex, spectrum); + setCellText(makeNumber(range.first), row, startXColumn()); + setCellText(makeNumber(range.second), row, endXColumn()); + + const auto exclude = m_model->getExcludeRegion(dataIndex, spectrum); + setCellText(QString::fromStdString(exclude), row, excludeColumn()); +} + +void IndirectDataTablePresenter::setCellText(const QString &text, int row, + int column) { + m_dataTable->item(row, column)->setText(text); +} + +std::size_t IndirectDataTablePresenter::removeTableEntry(int row) { + const auto dataIndex = m_dataTable->item(row, 0)->data(Qt::UserRole); + m_dataTable->removeRow(row); + return dataIndex.toULongLong(); +} + +void IndirectDataTablePresenter::shiftDataPositions(int shift, std::size_t from, + std::size_t to) { + for (auto i = from; i < to; ++i) + m_dataPositions[i] += shift; +} + +void IndirectDataTablePresenter::updateDataPositionsInCells(std::size_t from, + std::size_t to) { + for (auto i = from; i < to; ++i) { + const auto nextPosition = getNextPosition(i); + for (auto row = m_dataPositions[i]; row < nextPosition; ++row) + m_dataTable->item(row, 0)->setData(Qt::UserRole, getVariant(i)); + } +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IndirectDataTablePresenter.h b/qt/scientific_interfaces/Indirect/IndirectDataTablePresenter.h new file mode 100644 index 0000000000000000000000000000000000000000..6bb7c05cb7fd24f5ea2cc1667e31088c2848ca67 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectDataTablePresenter.h @@ -0,0 +1,139 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_INDIRECTDATATABLEPRESENTER_H_ +#define MANTIDQTCUSTOMINTERFACES_INDIRECTDATATABLEPRESENTER_H_ + +#include "IndirectFittingModel.h" + +#include <QTableWidget> + +#include <cstddef> +#include <unordered_map> + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +/** + Presenter for a table of indirect fitting data. + Copyright © 2015-2016 ISIS Rutherford Appleton Laboratory, NScD + Oak Ridge National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport IndirectDataTablePresenter : public QObject { + Q_OBJECT +public: + IndirectDataTablePresenter(IndirectFittingModel *model, + QTableWidget *dataTable); + + IndirectDataTablePresenter(IndirectFittingModel *model, + QTableWidget *dataTable, + const QStringList &headers); + + bool tableDatasetsMatchModel() const; + bool isTableEmpty() const; + + void setStartX(double startX, std::size_t dataIndex, int spectrumIndex); + void setEndX(double endX, std::size_t dataIndex, int spectrumIndex); + void setExclude(const std::string &exclude, std::size_t dataIndex, + int spectrumIndex); + +signals: + void startXChanged(double, std::size_t, std::size_t); + void endXChanged(double, std::size_t, std::size_t); + void excludeRegionChanged(const std::string &, std::size_t, std::size_t); + +public slots: + void addData(std::size_t index); + void updateData(std::size_t index); + void removeSelectedData(); + void setStartX(double startX, int index); + void setEndX(double endX, int index); + void setExcludeRegion(const std::string &exclude, int index); + void setGlobalFittingRange(bool global); + void enableTable(); + void disableTable(); + void clearTable(); + +private slots: + void setModelFittingRange(int row, int column); + void updateAllFittingRangeFrom(int row, int column); + +protected: + int getFirstRow(std::size_t dataIndex) const; + std::string getString(int row, int column) const; + + virtual void addTableEntry(std::size_t dataIndex, std::size_t spectrum, + int row); + void setCell(std::unique_ptr<QTableWidgetItem> cell, int row, int column); + virtual void updateTableEntry(std::size_t dataIndex, std::size_t spectrum, + int row); + void setCellText(const QString &text, int row, int column); + +private: + virtual int workspaceIndexColumn() const; + virtual int startXColumn() const; + virtual int endXColumn() const; + virtual int excludeColumn() const; + double startX(int row) const; + double endX(int row) const; + std::string getExcludeString(int row) const; + std::string getWorkspaceName(int row) const; + std::size_t getWorkspaceIndex(int row) const; + double getDouble(int row, int column) const; + QString getText(int row, int column) const; + int getNextPosition(std::size_t index) const; + std::size_t getDataIndex(int row) const; + boost::optional<Spectra> getSpectra(std::size_t dataIndex) const; + boost::optional<Spectra> getSpectra(int start, int end) const; + boost::optional<int> getRowIndex(std::size_t dataIndex, + int spectrumIndex) const; + + void setModelStartXAndEmit(double startX, std::size_t dataIndex, + std::size_t workspaceIndex); + void setModelEndXAndEmit(double endX, std::size_t dataIndex, + std::size_t workspaceIndex); + void setModelExcludeAndEmit(const std::string &exclude, std::size_t dataIndex, + std::size_t workspaceIndex); + + void enableGlobalFittingRange(); + void disableGlobalFittingRange(); + + void updateExistingData(std::size_t index); + void addNewData(std::size_t index); + void addTableEntry(std::size_t dataIndex, std::size_t spectrum); + std::size_t removeTableEntry(int row); + std::pair<std::vector<std::size_t>, std::vector<std::size_t>> + removeTableRows(QModelIndexList &selectedRows); + void setStartX(double startX); + void setEndX(double endX); + void setExcludeRegion(const std::string &exclude); + void setExcludeRegion(const QString &exclude); + void setColumnValues(int column, const QString &value); + void setHorizontalHeaders(const QStringList &headers); + + void collapseData(int from, int to, int initialSize, std::size_t dataIndex); + void updateFromRemovedIndices(const std::vector<std::size_t> &indices); + void shiftDataPositions(int value, std::size_t from, std::size_t to); + void updateDataPositionsInCells(std::size_t from, std::size_t to); + + std::vector<int> m_dataPositions; + IndirectFittingModel *m_model; + QTableWidget *m_dataTable; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACES_INDIRECTDATATABLEPRESENTER_H_ */ diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp index 98befdcb04f681dc761828c8d46d1c09d5ac5509..b8dec5575c00be88ed1f977b043c9bbd352a454f 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp +++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp @@ -11,6 +11,7 @@ #include "MantidKernel/make_unique.h" #include "MantidQtWidgets/Common/PropertyHandler.h" +#include "MantidQtWidgets/Common/SignalBlocker.h" #include <QString> #include <QtCore> @@ -47,58 +48,187 @@ namespace IDA { */ IndirectFitAnalysisTab::IndirectFitAnalysisTab(IndirectFittingModel *model, QWidget *parent) - : IndirectDataAnalysisTab(parent), m_inputAndGuessWorkspace(nullptr), - m_fittingModel(model) {} + : IndirectDataAnalysisTab(parent), m_fittingModel(model) {} void IndirectFitAnalysisTab::setup() { setupFitTab(); + updateResultOptions(); + + connect(m_dataPresenter.get(), + SIGNAL(startXChanged(double, std::size_t, std::size_t)), this, + SLOT(tableStartXChanged(double, std::size_t, std::size_t))); + connect(m_dataPresenter.get(), + SIGNAL(endXChanged(double, std::size_t, std::size_t)), this, + SLOT(tableEndXChanged(double, std::size_t, std::size_t))); + connect(m_dataPresenter.get(), + SIGNAL(excludeRegionChanged(const std::string &, std::size_t, + std::size_t)), + this, SLOT(tableExcludeChanged(const std::string &, std::size_t, + std::size_t))); + connect(m_dataPresenter.get(), SIGNAL(singleResolutionLoaded()), this, + SLOT(setModelFitFunction())); connect(m_fitPropertyBrowser, SIGNAL(fitScheduled()), this, - SLOT(executeSingleFit())); + SLOT(singleFit())); connect(m_fitPropertyBrowser, SIGNAL(sequentialFitScheduled()), this, SLOT(executeFit())); + connect(m_fitPropertyBrowser, SIGNAL(startXChanged(double)), this, + SLOT(setModelStartX(double))); + connect(m_fitPropertyBrowser, SIGNAL(endXChanged(double)), this, + SLOT(setModelEndX(double))); + connect(m_fitPropertyBrowser, SIGNAL(parameterChanged(const Mantid::API::IFunction *)), this, - SLOT(emitParameterChanged(const Mantid::API::IFunction *))); - connect(m_fitPropertyBrowser, - SIGNAL(parameterChanged(const Mantid::API::IFunction *)), this, - SLOT(updateGuessPlots())); + SIGNAL(parameterChanged(const Mantid::API::IFunction *))); connect(m_fitPropertyBrowser, SIGNAL(customBoolChanged(const QString &, bool)), this, - SLOT(emitCustomBoolChanged(const QString &, bool))); - - connect(m_fitPropertyBrowser, SIGNAL(startXChanged(double)), this, - SLOT(startXChanged(double))); - connect(m_fitPropertyBrowser, SIGNAL(endXChanged(double)), this, - SLOT(endXChanged(double))); - connect(m_fitPropertyBrowser, SIGNAL(startXChanged(double)), this, - SLOT(setModelStartX(double))); - connect(m_fitPropertyBrowser, SIGNAL(endXChanged(double)), this, - SLOT(setModelEndX(double))); - connect(m_fitPropertyBrowser, SIGNAL(xRangeChanged(double, double)), this, - SLOT(updateGuessPlots())); + SIGNAL(customBoolChanged(const QString &, bool))); connect(m_fitPropertyBrowser, SIGNAL(functionChanged()), this, SLOT(setModelFitFunction())); connect(m_fitPropertyBrowser, SIGNAL(functionChanged()), this, - SLOT(updateParameterValues())); - connect(m_fitPropertyBrowser, SIGNAL(functionChanged()), this, - SLOT(emitFunctionChanged())); - connect(m_fitPropertyBrowser, SIGNAL(functionChanged()), this, - SLOT(updatePreviewPlots())); + SIGNAL(functionChanged())); connect(m_fitPropertyBrowser, SIGNAL(functionChanged()), this, SLOT(updatePlotOptions())); connect(m_fitPropertyBrowser, SIGNAL(functionChanged()), this, - SLOT(updateGuessPlots())); + SLOT(updateResultOptions())); connect(m_fitPropertyBrowser, SIGNAL(functionChanged()), this, + SLOT(updateParameterValues())); + connect(m_plotPresenter.get(), SIGNAL(selectedFitDataChanged(std::size_t)), + this, SLOT(updateParameterValues())); + connect(m_plotPresenter.get(), SIGNAL(plotSpectrumChanged(std::size_t)), this, + SLOT(updateParameterValues())); + + connect(m_plotPresenter.get(), + SIGNAL(fitSingleSpectrum(std::size_t, std::size_t)), this, + SLOT(singleFit(std::size_t, std::size_t))); + connect(m_plotPresenter.get(), + SIGNAL(runAsPythonScript(const QString &, bool)), this, + SIGNAL(runAsPythonScript(const QString &, bool))); + + connect(m_dataPresenter.get(), SIGNAL(dataChanged()), this, SLOT(updateResultOptions())); - connect(m_fitPropertyBrowser, SIGNAL(plotGuess()), this, - SLOT(plotGuessInWindow())); - connect(m_fitPropertyBrowser, SIGNAL(browserClosed()), this, - SLOT(clearGuessWindowPlot())); + connectDataAndSpectrumPresenters(); + connectDataAndPlotPresenters(); + connectDataAndFitBrowserPresenters(); + connectSpectrumAndPlotPresenters(); + connectFitBrowserAndPlotPresenter(); +} + +void IndirectFitAnalysisTab::connectDataAndPlotPresenters() { + connect(m_dataPresenter.get(), SIGNAL(multipleDataViewSelected()), + m_plotPresenter.get(), SLOT(showMultipleDataSelection())); + connect(m_dataPresenter.get(), SIGNAL(singleDataViewSelected()), + m_plotPresenter.get(), SLOT(hideMultipleDataSelection())); + + connect(m_dataPresenter.get(), SIGNAL(dataAdded()), m_plotPresenter.get(), + SLOT(appendLastDataToSelection())); + connect(m_dataPresenter.get(), SIGNAL(dataRemoved()), m_plotPresenter.get(), + SLOT(updateDataSelection())); + + connect(m_dataPresenter.get(), SIGNAL(dataChanged()), m_plotPresenter.get(), + SLOT(updateAvailableSpectra())); + connect(m_dataPresenter.get(), SIGNAL(dataChanged()), m_plotPresenter.get(), + SLOT(updatePlots())); + connect(m_dataPresenter.get(), SIGNAL(dataChanged()), m_plotPresenter.get(), + SLOT(updateGuess())); + + connect(m_dataPresenter.get(), SIGNAL(singleResolutionLoaded()), + m_plotPresenter.get(), SLOT(updatePlots())); + connect(m_dataPresenter.get(), SIGNAL(singleResolutionLoaded()), + m_plotPresenter.get(), SLOT(updateGuess())); + + connect(m_plotPresenter.get(), SIGNAL(startXChanged(double)), this, + SLOT(setDataTableStartX(double))); + connect(m_plotPresenter.get(), SIGNAL(endXChanged(double)), this, + SLOT(setDataTableEndX(double))); +} + +void IndirectFitAnalysisTab::connectSpectrumAndPlotPresenters() { + connect(m_plotPresenter.get(), SIGNAL(selectedFitDataChanged(std::size_t)), + m_spectrumPresenter.get(), SLOT(setActiveModelIndex(std::size_t))); + connect(m_plotPresenter.get(), SIGNAL(noFitDataSelected()), + m_spectrumPresenter.get(), SLOT(disableView())); + connect(m_spectrumPresenter.get(), SIGNAL(spectraChanged(std::size_t)), + m_plotPresenter.get(), SLOT(updateSelectedDataName())); + connect(m_spectrumPresenter.get(), SIGNAL(spectraChanged(std::size_t)), + m_plotPresenter.get(), SLOT(updateAvailableSpectra())); +} + +void IndirectFitAnalysisTab::connectFitBrowserAndPlotPresenter() { + connect(m_plotPresenter.get(), SIGNAL(selectedFitDataChanged(std::size_t)), + this, SLOT(setBrowserWorkspace(std::size_t))); + connect(m_plotPresenter.get(), SIGNAL(plotSpectrumChanged(std::size_t)), this, + SLOT(setBrowserWorkspaceIndex(std::size_t))); + + connect(m_fitPropertyBrowser, SIGNAL(startXChanged(double)), + m_plotPresenter.get(), SLOT(setStartX(double))); + connect(m_fitPropertyBrowser, SIGNAL(endXChanged(double)), + m_plotPresenter.get(), SLOT(setEndX(double))); + + connect(m_plotPresenter.get(), SIGNAL(startXChanged(double)), this, + SLOT(setBrowserStartX(double))); + connect(m_plotPresenter.get(), SIGNAL(endXChanged(double)), this, + SLOT(setBrowserEndX(double))); + connect(m_plotPresenter.get(), SIGNAL(fwhmChanged(double)), this, + SLOT(updateFitBrowserParameterValues())); + connect(m_plotPresenter.get(), SIGNAL(backgroundChanged(double)), this, + SLOT(updateFitBrowserParameterValues())); + + connect(m_fitPropertyBrowser, SIGNAL(xRangeChanged(double, double)), + m_plotPresenter.get(), SLOT(updateGuess())); + connect(m_plotPresenter.get(), SIGNAL(fwhmChanged(double)), + m_plotPresenter.get(), SLOT(updateGuess())); + connect(m_plotPresenter.get(), SIGNAL(backgroundChanged(double)), + m_plotPresenter.get(), SLOT(updateGuess())); + + connect(m_fitPropertyBrowser, + SIGNAL(parameterChanged(const Mantid::API::IFunction *)), + m_plotPresenter.get(), SLOT(updateRangeSelectors())); + connect(m_fitPropertyBrowser, + SIGNAL(parameterChanged(const Mantid::API::IFunction *)), + m_plotPresenter.get(), SLOT(updateGuess())); + + connect(m_fitPropertyBrowser, SIGNAL(functionChanged()), + m_plotPresenter.get(), SLOT(updatePlots())); + connect(m_fitPropertyBrowser, SIGNAL(functionChanged()), + m_plotPresenter.get(), SLOT(updateGuess())); + + connect(m_fitPropertyBrowser, SIGNAL(plotGuess()), m_plotPresenter.get(), + SLOT(enablePlotGuessInSeparateWindow())); +} + +void IndirectFitAnalysisTab::connectDataAndSpectrumPresenters() { + connect(m_dataPresenter.get(), SIGNAL(singleDataViewSelected()), + m_spectrumPresenter.get(), SLOT(setActiveIndexToZero())); + connect(m_dataPresenter.get(), SIGNAL(dataChanged()), + m_spectrumPresenter.get(), SLOT(updateSpectra())); + connect(m_spectrumPresenter.get(), SIGNAL(spectraChanged(std::size_t)), + m_dataPresenter.get(), SLOT(updateSpectraInTable(std::size_t))); + connect(m_spectrumPresenter.get(), SIGNAL(maskChanged(const std::string &)), + this, SLOT(setDataTableExclude(const std::string &))); +} + +void IndirectFitAnalysisTab::connectDataAndFitBrowserPresenters() { + connect(m_dataPresenter.get(), SIGNAL(dataChanged()), this, + SLOT(updateBrowserFittingRange())); + connect(m_fitPropertyBrowser, SIGNAL(startXChanged(double)), this, + SLOT(setDataTableStartX(double))); + connect(m_fitPropertyBrowser, SIGNAL(endXChanged(double)), this, + SLOT(setDataTableEndX(double))); +} + +void IndirectFitAnalysisTab::setFitDataPresenter( + std::unique_ptr<IndirectFitDataPresenter> presenter) { + m_dataPresenter = std::move(presenter); +} + +void IndirectFitAnalysisTab::setPlotView(IndirectFitPlotView *view) { + m_plotPresenter = Mantid::Kernel::make_unique<IndirectFitPlotPresenter>( + m_fittingModel.get(), view); } void IndirectFitAnalysisTab::setSpectrumSelectionView( @@ -114,42 +244,44 @@ void IndirectFitAnalysisTab::setFitPropertyBrowser( m_fitPropertyBrowser = browser; } -IndirectFittingModel *IndirectFitAnalysisTab::fittingModel() const { - return m_fittingModel.get(); +void IndirectFitAnalysisTab::loadSettings(const QSettings &settings) { + m_dataPresenter->loadSettings(settings); } -/** - * @return The selected background function in this indirect fit analysis tab. - */ -IFunction_sptr IndirectFitAnalysisTab::background() const { - return m_fitPropertyBrowser->background(); +void IndirectFitAnalysisTab::setSampleWSSuffices(const QStringList &suffices) { + m_dataPresenter->setSampleWSSuffices(suffices); } -/** - * @return The selected model function in this indirect fit analysis tab. - * The model is specified to be the complete composite function, with - * the background removed. - */ -IFunction_sptr IndirectFitAnalysisTab::model() const { - auto composite = m_fitPropertyBrowser->compositeFunction(); - CompositeFunction_sptr model(new CompositeFunction); - for (auto i = 0u; i < composite->nFunctions(); ++i) - model->addFunction(composite->getFunction(i)); +void IndirectFitAnalysisTab::setSampleFBSuffices(const QStringList &suffices) { + m_dataPresenter->setSampleFBSuffices(suffices); +} - auto index = m_fitPropertyBrowser->backgroundIndex(); - if (index) - model->removeFunction(*index); +void IndirectFitAnalysisTab::setResolutionWSSuffices( + const QStringList &suffices) { + m_dataPresenter->setResolutionWSSuffices(suffices); +} - if (model->nFunctions() == 1) - return model->getFunction(0); - return model; +void IndirectFitAnalysisTab::setResolutionFBSuffices( + const QStringList &suffices) { + m_dataPresenter->setResolutionFBSuffices(suffices); } -/** - * @return The function index of the selected background. - */ -boost::optional<size_t> IndirectFitAnalysisTab::backgroundIndex() const { - return m_fitPropertyBrowser->backgroundIndex(); +std::size_t IndirectFitAnalysisTab::getSelectedDataIndex() const { + return m_plotPresenter->getSelectedDataIndex(); +} + +std::size_t IndirectFitAnalysisTab::getSelectedSpectrum() const { + return m_plotPresenter->getSelectedSpectrum(); +} + +bool IndirectFitAnalysisTab::isRangeCurrentlySelected( + std::size_t dataIndex, std::size_t spectrum) const { + return FittingMode::SEQUENTIAL == m_fittingModel->getFittingMode() || + m_plotPresenter->isCurrentlySelected(dataIndex, spectrum); +} + +IndirectFittingModel *IndirectFitAnalysisTab::fittingModel() const { + return m_fittingModel.get(); } /** @@ -170,97 +302,98 @@ size_t IndirectFitAnalysisTab::numberOfCustomFunctions( return m_fitPropertyBrowser->numberOfCustomFunctions(functionName); } -/** - * @return The selected Start-X value in the indirect fit analysis tab. - */ -double IndirectFitAnalysisTab::startX() const { - return m_fitPropertyBrowser->startX(); +void IndirectFitAnalysisTab::setModelFitFunction() { + try { + m_fittingModel->setFitFunction(m_fitPropertyBrowser->getFittingFunction()); + } catch (const std::out_of_range &) { + m_fittingModel->setFitFunction(m_fitPropertyBrowser->compositeFunction()); + } } -/** - * @return The selected End-X value in the indirect fit analysis tab. - */ -double IndirectFitAnalysisTab::endX() const { - return m_fitPropertyBrowser->endX(); +void IndirectFitAnalysisTab::setModelStartX(double startX) { + const auto dataIndex = getSelectedDataIndex(); + if (m_fittingModel->numberOfWorkspaces() > dataIndex) { + m_fittingModel->setStartX(startX, dataIndex, getSelectedSpectrum()); + } else { + setBrowserStartX(0); + setBrowserEndX(0); + } } -/** - * @param functionName The name of the function containing the parameter. - * @param parameterName The name of the parameter whose value to retrieve. - * @return All values of the parameter with the specified name, in - * the function with the specified name. - */ -std::vector<double> -IndirectFitAnalysisTab::parameterValue(const std::string &functionName, - const std::string ¶meterName) const { - return m_fitPropertyBrowser->parameterValue(functionName, parameterName); +void IndirectFitAnalysisTab::setModelEndX(double endX) { + const auto dataIndex = getSelectedDataIndex(); + if (m_fittingModel->numberOfWorkspaces() > dataIndex) { + m_fittingModel->setEndX(endX, dataIndex, getSelectedSpectrum()); + } else { + setBrowserStartX(0); + setBrowserEndX(0); + } } -/** - * @param functionName The name of the function containing the parameter. - * @param parameterName The name of the parameter whose value to retrieve. - * @return The value of the parameter with the specified name, in - * the last function with the specified name. - */ -boost::optional<double> IndirectFitAnalysisTab::lastParameterValue( - const std::string &functionName, const std::string ¶meterName) const { - const auto values = parameterValue(functionName, parameterName); - return values.empty() ? boost::none : boost::make_optional(values.back()); +void IndirectFitAnalysisTab::setDataTableStartX(double startX) { + m_dataPresenter->setStartX(startX, m_plotPresenter->getSelectedDataIndex(), + m_plotPresenter->getSelectedSpectrumIndex()); } -/** - * @return True if the selected model is empty, false otherwise. - */ -bool IndirectFitAnalysisTab::isEmptyModel() const { - auto modelFunction = model(); - auto compositeModel = - boost::dynamic_pointer_cast<CompositeFunction>(modelFunction); - return compositeModel && compositeModel->nFunctions() == 0; +void IndirectFitAnalysisTab::setDataTableEndX(double endX) { + m_dataPresenter->setEndX(endX, m_plotPresenter->getSelectedDataIndex(), + m_plotPresenter->getSelectedSpectrumIndex()); } -/** - * @return The name of the selected background. - */ -QString IndirectFitAnalysisTab::backgroundName() const { - return m_fitPropertyBrowser->backgroundName(); +void IndirectFitAnalysisTab::setDataTableExclude(const std::string &exclude) { + m_dataPresenter->setExclude(exclude, m_plotPresenter->getSelectedDataIndex(), + m_plotPresenter->getSelectedSpectrumIndex()); } -/** - * @return True if a guess plot can be fit, false otherwise. - */ -bool IndirectFitAnalysisTab::canPlotGuess() const { - return !isEmptyModel() && inputWorkspace(); +void IndirectFitAnalysisTab::setBrowserStartX(double startX) { + MantidQt::API::SignalBlocker<QObject> blocker(m_fitPropertyBrowser); + m_fitPropertyBrowser->setStartX(startX); } -bool IndirectFitAnalysisTab::validate() { - UserInputValidator validator; - m_spectrumPresenter->validate(validator); +void IndirectFitAnalysisTab::setBrowserEndX(double endX) { + MantidQt::API::SignalBlocker<QObject> blocker(m_fitPropertyBrowser); + m_fitPropertyBrowser->setEndX(endX); +} - const auto invalidFunction = m_fittingModel->isInvalidFunction(); - if (invalidFunction) - validator.addErrorMessage(QString::fromStdString(*invalidFunction)); +void IndirectFitAnalysisTab::updateBrowserFittingRange() { + const auto range = m_fittingModel->getFittingRange(getSelectedDataIndex(), + getSelectedSpectrum()); + setBrowserStartX(range.first); + setBrowserEndX(range.second); +} - const auto error = validator.generateErrorMessage(); - emit showMessageBox(error); - return error.isEmpty(); +void IndirectFitAnalysisTab::setBrowserWorkspace(std::size_t dataIndex) { + const auto name = m_fittingModel->getWorkspace(dataIndex)->getName(); + m_fitPropertyBrowser->setWorkspaceName(QString::fromStdString(name)); } -void IndirectFitAnalysisTab::setModelFitFunction() { - try { - m_fittingModel->setFitFunction(m_fitPropertyBrowser->getFittingFunction()); - } catch (const std::out_of_range &) { - m_fittingModel->setFitFunction(m_fitPropertyBrowser->compositeFunction()); +void IndirectFitAnalysisTab::setBrowserWorkspaceIndex(std::size_t spectrum) { + m_fitPropertyBrowser->setWorkspaceIndex(boost::numeric_cast<int>(spectrum)); +} + +void IndirectFitAnalysisTab::tableStartXChanged(double startX, + std::size_t dataIndex, + std::size_t spectrum) { + if (isRangeCurrentlySelected(dataIndex, spectrum)) { + m_plotPresenter->setStartX(startX); + setBrowserStartX(startX); } } -void IndirectFitAnalysisTab::setModelStartX(double startX) { - if (m_fittingModel->getWorkspace(0)) - m_fittingModel->setStartX(startX, 0, selectedSpectrum()); +void IndirectFitAnalysisTab::tableEndXChanged(double endX, + std::size_t dataIndex, + std::size_t spectrum) { + if (isRangeCurrentlySelected(dataIndex, spectrum)) { + m_plotPresenter->setEndX(endX); + setBrowserEndX(endX); + } } -void IndirectFitAnalysisTab::setModelEndX(double endX) { - if (m_fittingModel->getWorkspace(0)) - m_fittingModel->setEndX(endX, 0, selectedSpectrum()); +void IndirectFitAnalysisTab::tableExcludeChanged(const std::string &, + std::size_t dataIndex, + std::size_t spectrum) { + if (isRangeCurrentlySelected(dataIndex, spectrum)) + m_spectrumPresenter->displayBinMask(); } /** @@ -505,26 +638,6 @@ void IndirectFitAnalysisTab::setCustomSettingChangesFunction( changesFunction); } -/** - * Sets the selected spectrum for this indirect fit analysis tab. - */ -void IndirectFitAnalysisTab::setSelectedSpectrum(int spectrum) { - disconnect(m_fitPropertyBrowser, - SIGNAL(parameterChanged(const Mantid::API::IFunction *)), this, - SLOT(updateGuessPlots())); - - m_fitPropertyBrowser->setWorkspaceIndex(spectrum); - IndirectDataAnalysisTab::setSelectedSpectrum(spectrum); - updateParameterValues( - m_fittingModel->getFitParameters(0, selectedSpectrum())); - updatePreviewPlots(); - updateGuessPlots(); - - connect(m_fitPropertyBrowser, - SIGNAL(parameterChanged(const Mantid::API::IFunction *)), this, - SLOT(updateGuessPlots())); -} - void IndirectFitAnalysisTab::updateFitOutput(bool error) { disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(updateFitOutput(bool))); @@ -552,74 +665,29 @@ void IndirectFitAnalysisTab::updateSingleFitOutput(bool error) { void IndirectFitAnalysisTab::fitAlgorithmComplete(bool error) { setSaveResultEnabled(!error); setPlotResultEnabled(!error); - m_spectrumPresenter->enableView(); updateParameterValues(); - updatePreviewPlots(); - updatePlotRange(); + m_spectrumPresenter->enableView(); + m_plotPresenter->updatePlots(); + updatePlotOptions(); connect(m_fitPropertyBrowser, - SIGNAL(parameterChanged(const Mantid::API::IFunction *)), this, - SLOT(updateGuessPlots())); + SIGNAL(parameterChanged(const Mantid::API::IFunction *)), + m_plotPresenter.get(), SLOT(updateGuess())); disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(fitAlgorithmComplete(bool))); } -/** - * Handles the event in which the minimum-X value has been selected. - * - * @param xMax The selected minimum-X value. - */ -void IndirectFitAnalysisTab::xMinSelected(double xMin) { - m_fitPropertyBrowser->setStartX(xMin); -} - -/** - * Handles the event in which the maximum-X value has been selected. - * - * @param xMax The selected maximum-X value. - */ -void IndirectFitAnalysisTab::xMaxSelected(double xMax) { - m_fitPropertyBrowser->setEndX(xMax); -} - -/* - * Performs necessary state changes when new input data is loaded in - * this fit analysis tab. - * - Sets preview plot and input workspaces. - * - Updates default property values. - * - Updates property table. - * - Updates preview plots. - * - * @param wsName The name of the loaded input workspace. - */ -void IndirectFitAnalysisTab::newInputDataLoaded(const QString &wsName) { - m_fittingModel->clearWorkspaces(); - - try { - m_fittingModel->addWorkspace(wsName.toStdString()); - } catch (const std::runtime_error &err) { - emit showMessageBox("Unable to load workspace:\n" + - QString::fromStdString(err.what())); - } - - m_spectrumPresenter->setActiveModelIndex(0); - setInputWorkspace(m_fittingModel->getWorkspace(0)); - enablePlotPreview(); - setPlotResultEnabled(false); - setSaveResultEnabled(false); - updateParameterValues(m_fittingModel->getDefaultParameters(0)); - setPreviewPlotWorkspace(m_fittingModel->getWorkspace(0)); - updatePreviewPlots(); - updatePlotRange(); - m_fitPropertyBrowser->setWorkspaceName(wsName); +void IndirectFitAnalysisTab::updateFitBrowserParameterValues() { + MantidQt::API::SignalBlocker<QObject> blocker(m_fitPropertyBrowser); + m_fitPropertyBrowser->updateParameters(); } /** * Updates the parameter values and errors in the fit property browser. */ void IndirectFitAnalysisTab::updateParameterValues() { - updateParameterValues( - m_fittingModel->getParameterValues(0, selectedSpectrum())); + updateParameterValues(m_fittingModel->getParameterValues( + getSelectedDataIndex(), getSelectedSpectrum())); } /** @@ -632,9 +700,12 @@ void IndirectFitAnalysisTab::updateParameterValues( try { auto fitFunction = m_fitPropertyBrowser->getFittingFunction(); updateParameters(fitFunction, parameters); + + MantidQt::API::SignalBlocker<QObject> blocker(m_fitPropertyBrowser); m_fitPropertyBrowser->updateParameters(); - if (m_fittingModel->isPreviouslyFit(0, selectedSpectrum())) + if (m_fittingModel->isPreviouslyFit(getSelectedDataIndex(), + getSelectedSpectrum())) m_fitPropertyBrowser->updateErrors(); else m_fitPropertyBrowser->clearErrors(); @@ -657,72 +728,61 @@ void IndirectFitAnalysisTab::saveResult() { m_fittingModel->saveResult(); } * @param plotType The plot type specifying what to plot. */ void IndirectFitAnalysisTab::plotResult(const QString &plotType) { - const auto resultWorkspace = m_fittingModel->getResultWorkspace(); - if (resultWorkspace) { - const auto resultName = QString::fromStdString(resultWorkspace->getName()); - - // Handle plot result - if (plotType.compare("All") == 0) { - const auto specEnd = resultWorkspace->getNumberHistograms(); - for (auto i = 0u; i < specEnd; ++i) - IndirectTab::plotSpectrum(resultName, static_cast<int>(i)); - } else { - const auto labels = IndirectTab::extractAxisLabels(resultWorkspace, 1); - - for (const auto ¶meter : m_fittingModel->getFitParameterNames()) { - if (boost::contains(parameter, plotType)) { - auto it = labels.find(parameter); - if (it != labels.end()) - IndirectTab::plotSpectrum(resultName, static_cast<int>(it->second)); - } - } - } + const auto resultWorkspaces = m_fittingModel->getResultWorkspace(); + if (resultWorkspaces) { + if (plotType.compare("All") == 0) + plotAll(resultWorkspaces); + else + plotParameter(resultWorkspaces, plotType.toStdString()); } } -/* - * Fills the specified combo-box, with the possible parameters which - * can be plot separately. - * - * @param comboBox The combo box to fill. - */ -void IndirectFitAnalysisTab::fillPlotTypeComboBox(QComboBox *comboBox) { - comboBox->clear(); - comboBox->addItem("All"); +void IndirectFitAnalysisTab::plotAll( + Mantid::API::WorkspaceGroup_sptr workspaces) { + for (auto &&workspace : *workspaces) + plotAll(boost::dynamic_pointer_cast<MatrixWorkspace>(workspace)); +} - QSet<QString> parameters; - for (const auto ¶meter : m_fitPropertyBrowser->getParameterNames()) - parameters.insert(parameter.right(parameter.lastIndexOf('.'))); - comboBox->addItems(parameters.toList()); +void IndirectFitAnalysisTab::plotParameter( + Mantid::API::WorkspaceGroup_sptr workspaces, const std::string ¶meter) { + for (auto &&workspace : *workspaces) + plotParameter(boost::dynamic_pointer_cast<MatrixWorkspace>(workspace), + parameter); } -/* - * Updates the preview plots in this fit analysis tab, given the name - * of the output workspace from a fit. - * - * @param fitPreviewPlot The preview plot widget in which to plot the fit. - * @param diffPreviewPlot The preview plot widget in which to plot the - * difference between the fit and sample data. - */ -void IndirectFitAnalysisTab::updatePlots( - MantidQt::MantidWidgets::PreviewPlot *fitPreviewPlot, - MantidQt::MantidWidgets::PreviewPlot *diffPreviewPlot) { - auto location = m_fittingModel->getResultLocation(0, selectedSpectrum()); - auto workspace = location ? location->result.lock() : nullptr; - - if (workspace) - IndirectDataAnalysisTab::updatePlot(workspace->getName(), location->index, - fitPreviewPlot, diffPreviewPlot); - else - IndirectDataAnalysisTab::updatePlot("", fitPreviewPlot, diffPreviewPlot); +void IndirectFitAnalysisTab::plotAll( + Mantid::API::MatrixWorkspace_sptr workspace) { + const auto name = QString::fromStdString(workspace->getName()); + for (auto i = 0u; i < workspace->getNumberHistograms(); ++i) + IndirectTab::plotSpectrum(name, static_cast<int>(i)); +} + +void IndirectFitAnalysisTab::plotParameter( + Mantid::API::MatrixWorkspace_sptr workspace, + const std::string ¶meterToPlot) { + const auto name = QString::fromStdString(workspace->getName()); + const auto labels = IndirectTab::extractAxisLabels(workspace, 1); + + for (const auto ¶meter : m_fittingModel->getFitParameterNames()) { + if (boost::contains(parameter, parameterToPlot)) { + auto it = labels.find(parameter); + if (it != labels.end()) + IndirectTab::plotSpectrum(name, static_cast<int>(it->second)); + } + } } /** * Executes the single fit algorithm defined in this indirect fit analysis tab. */ void IndirectFitAnalysisTab::singleFit() { + singleFit(getSelectedDataIndex(), getSelectedSpectrum()); +} + +void IndirectFitAnalysisTab::singleFit(std::size_t dataIndex, + std::size_t spectrum) { if (validate()) - runSingleFit(m_fittingModel->getSingleFit(0, selectedSpectrum())); + runSingleFit(m_fittingModel->getSingleFit(dataIndex, spectrum)); } /** @@ -734,10 +794,26 @@ void IndirectFitAnalysisTab::executeFit() { runFitAlgorithm(m_fittingModel->getFittingAlgorithm()); } +bool IndirectFitAnalysisTab::validate() { + UserInputValidator validator; + m_dataPresenter->validate(validator); + m_spectrumPresenter->validate(validator); + + const auto invalidFunction = m_fittingModel->isInvalidFunction(); + if (invalidFunction) + validator.addErrorMessage(QString::fromStdString(*invalidFunction)); + + const auto error = validator.generateErrorMessage(); + emit showMessageBox(error); + return error.isEmpty(); +} + /** * Called when the 'Run' button is called in the IndirectTab. */ -void IndirectFitAnalysisTab::run() { executeFit(); } +void IndirectFitAnalysisTab::run() { + runFitAlgorithm(m_fittingModel->getFittingAlgorithm()); +} void IndirectFitAnalysisTab::setAlgorithmProperties( IAlgorithm_sptr fitAlgorithm) const { @@ -779,14 +855,15 @@ void IndirectFitAnalysisTab::runSingleFit(IAlgorithm_sptr fitAlgorithm) { void IndirectFitAnalysisTab::setupFit(IAlgorithm_sptr fitAlgorithm) { disconnect(m_fitPropertyBrowser, - SIGNAL(parameterChanged(const Mantid::API::IFunction *)), this, - SLOT(updateGuessPlots())); + SIGNAL(parameterChanged(const Mantid::API::IFunction *)), + m_plotPresenter.get(), SLOT(updateGuess())); setAlgorithmProperties(fitAlgorithm); m_fittingAlgorithm = fitAlgorithm; m_spectrumPresenter->disableView(); m_batchAlgoRunner->addAlgorithm(fitAlgorithm); + connect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(fitAlgorithmComplete(bool))); } @@ -797,9 +874,7 @@ void IndirectFitAnalysisTab::setupFit(IAlgorithm_sptr fitAlgorithm) { * @param cbPlotType The combo box. */ void IndirectFitAnalysisTab::updatePlotOptions(QComboBox *cbPlotType) { - setPlotOptions( - cbPlotType, - m_fitPropertyBrowser->compositeFunction()->getParameterNames()); + setPlotOptions(cbPlotType, m_fittingModel->getFitParameterNames()); } /** @@ -845,364 +920,12 @@ void IndirectFitAnalysisTab::setPlotOptions( * enabled/disabled. */ void IndirectFitAnalysisTab::updateResultOptions() { - const bool isFit = m_fittingModel->isPreviouslyFit(0, selectedSpectrum()); + const bool isFit = m_fittingModel->isPreviouslyFit(getSelectedDataIndex(), + getSelectedSpectrum()); setPlotResultEnabled(isFit); setSaveResultEnabled(isFit); } -/** - * Updates the guess plots - both in the interface and separate window. - */ -void IndirectFitAnalysisTab::updateGuessPlots() { - if (canPlotGuess()) - enablePlotGuess(); - else - disablePlotGuess(); - - if (doPlotGuess() || m_inputAndGuessWorkspace) - updateGuessPlots(m_fittingModel->getFittingFunction()); -} - -/** - * Updates the guess plots - both in the interface and separate window, using - * the specified function for the guess. - * - * @param guessFunction The function to use for the guess. - */ -void IndirectFitAnalysisTab::updateGuessPlots(IFunction_sptr guessFunction) { - if (m_fittingModel->getWorkspace(0) && guessFunction) { - auto guessWS = createGuessWorkspace(guessFunction, selectedSpectrum()); - - if (guessWS->x(0).size() >= 2) { - updatePlotGuess(guessWS); - m_plotWindowGuessRunner.addCallback( - [this, guessWS]() { updatePlotGuessInWindow(guessWS); }); - } - } -} - -/** - * Updates the guess plot within the interface. - */ -void IndirectFitAnalysisTab::updatePlotGuess() { - updatePlotGuess(createGuessWorkspace(m_fittingModel->getFittingFunction(), - selectedSpectrum())); -} - -/** - * Updates the guess plot within the interface, using the specified guess - * workspace. - * - * @parma workspace The guess workspace. - */ -void IndirectFitAnalysisTab::updatePlotGuess(MatrixWorkspace_sptr workspace) { - if (doPlotGuess()) - addGuessPlot(workspace); - else - removeGuessPlot(); -} - -/** - * Updates the guess plot in a separate window, if one exists. - */ -void IndirectFitAnalysisTab::updatePlotGuessInWindow() { - updatePlotGuessInWindow(createGuessWorkspace( - m_fittingModel->getFittingFunction(), selectedSpectrum())); -} - -/** - * Updates the guess plot in a separate window, if one exists, using the - * specified guess workspace. - * - * @param workspace The guess workspace. - */ -void IndirectFitAnalysisTab::updatePlotGuessInWindow( - MatrixWorkspace_sptr workspace) { - if (m_inputAndGuessWorkspace) { - const auto guessWSName = "__" + inputWorkspace()->getName() + "_guess_ws"; - - if (m_inputAndGuessWorkspace->getName() != guessWSName) - clearGuessWindowPlot(); - else - m_inputAndGuessWorkspace = createInputAndGuessWorkspace(workspace); - } -} - -/** - * Plots the current guess in a seperate plot window. - */ -void IndirectFitAnalysisTab::plotGuessInWindow() { - clearGuessWindowPlot(); - auto guessWS = createGuessWorkspace(m_fittingModel->getFittingFunction(), - selectedSpectrum()); - m_inputAndGuessWorkspace = createInputAndGuessWorkspace(guessWS); - - if (m_inputAndGuessWorkspace) - plotSpectrum(QString::fromStdString(m_inputAndGuessWorkspace->getName()), 0, - 1); -} - -/** - * Clears the guess window plot and deletes the associated workspace. - */ -void IndirectFitAnalysisTab::clearGuessWindowPlot() { - if (m_inputAndGuessWorkspace) { - deleteWorkspace(m_inputAndGuessWorkspace); - m_inputAndGuessWorkspace.reset(); - } -} - -/** - * Creates a workspace containing the input and guess data, to be used - * for plotting the guess in a separate window. - * - * @return A workspace containing the input and guess data. - */ -MatrixWorkspace_sptr IndirectFitAnalysisTab::createInputAndGuessWorkspace( - MatrixWorkspace_sptr guessWorkspace) { - const auto guessWSName = "__" + inputWorkspace()->getName() + "_guess_ws"; - return createInputAndGuessWorkspace(inputWorkspace(), guessWorkspace, - guessWSName); -} - -/** - * Creates a workspace containing the input and guess data, to be used - * for plotting the guess in a separate window, and adds it to the ADS. - * - * @param inputWS The input workspace. - * @param guessWorkspace The guess workspace. - * @param outputName The name of the workspace in the ADS. - * @return A workspace containing the input and guess data. - */ -MatrixWorkspace_sptr IndirectFitAnalysisTab::createInputAndGuessWorkspace( - MatrixWorkspace_sptr inputWS, MatrixWorkspace_sptr guessWorkspace, - const std::string &outputName) const { - auto inputAndGuess = createInputAndGuessWorkspace(inputWS, guessWorkspace); - AnalysisDataService::Instance().addOrReplace(outputName, inputAndGuess); - return inputAndGuess; -} - -/** - * Creates a workspace containing the input and guess data, to be used - * for plotting the guess in a separate window. - * - * @param inputWS The input workspace. - * @param guessWorkspace The guess workspace. - * @return A workspace containing the input and guess data. - */ -MatrixWorkspace_sptr IndirectFitAnalysisTab::createInputAndGuessWorkspace( - MatrixWorkspace_sptr inputWS, MatrixWorkspace_sptr guessWorkspace) const { - ensureAppendCompatibility(inputWS, guessWorkspace); - const auto spectrum = selectedSpectrum(); - - auto extracted = - extractSpectra(inputWS, spectrum, spectrum, startX(), endX()); - auto inputAndGuess = appendSpectra(extracted, guessWorkspace); - - auto axis = Mantid::Kernel::make_unique<TextAxis>(2); - axis->setLabel(0, "Sample"); - axis->setLabel(1, "Guess"); - inputAndGuess->replaceAxis(1, axis.release()); - return inputAndGuess; -} - -/** - * Ensures one workspace is able to be appended to another. - * - * @param inputWS The workspace to maintain compatibility with. - * @param spectraWS The workspace to make compatible. - */ -void IndirectFitAnalysisTab::ensureAppendCompatibility( - MatrixWorkspace_sptr inputWS, MatrixWorkspace_sptr spectraWS) const { - spectraWS->setInstrument(inputWS->getInstrument()); - spectraWS->replaceAxis(0, inputWS->getAxis(0)->clone(spectraWS.get())); - spectraWS->setDistribution(inputWS->isDistribution()); -} - -/** - * Extracts spectra from a specified workspace. - * - * @param inputWS The workspace to extract spectra from. - * @param startIndex The index of the first spectrum to be retained. - * @param endIndex The index of the last spectrum to be retained. - * @param startX The x-value that is within the first bin to be retained. - * @param endX The x-value that is within the last bin to be retained. - * @return A workspace containing the extracted spectra. - */ -MatrixWorkspace_sptr -IndirectFitAnalysisTab::extractSpectra(MatrixWorkspace_sptr inputWS, - int startIndex, int endIndex, - double startX, double endX) const { - auto extractSpectraAlg = - AlgorithmManager::Instance().create("ExtractSpectra"); - extractSpectraAlg->initialize(); - extractSpectraAlg->setChild(true); - extractSpectraAlg->setLogging(false); - extractSpectraAlg->setProperty("InputWorkspace", inputWS); - extractSpectraAlg->setProperty("StartWorkspaceIndex", startIndex); - extractSpectraAlg->setProperty("XMin", startX); - extractSpectraAlg->setProperty("XMax", endX); - extractSpectraAlg->setProperty("EndWorkspaceIndex", endIndex); - extractSpectraAlg->setProperty("OutputWorkspace", "__extracted"); - extractSpectraAlg->execute(); - return extractSpectraAlg->getProperty("OutputWorkspace"); -} - -/** - * Appends the spectra of a specified workspace to another specified workspace. - * - * @param inputWS The workspace to append to. - * @param spectraWS The workspace containing the spectra to append. - * @return A workspace containing the spectra of the second workspace - * appended to the first. - */ -MatrixWorkspace_sptr -IndirectFitAnalysisTab::appendSpectra(MatrixWorkspace_sptr inputWS, - MatrixWorkspace_sptr spectraWS) const { - auto appendSpectraAlg = AlgorithmManager::Instance().create("AppendSpectra"); - appendSpectraAlg->initialize(); - appendSpectraAlg->setChild(true); - appendSpectraAlg->setLogging(false); - appendSpectraAlg->setProperty("InputWorkspace1", inputWS); - appendSpectraAlg->setProperty("InputWorkspace2", spectraWS); - appendSpectraAlg->setProperty("OutputWorkspace", "__appended"); - appendSpectraAlg->execute(); - return appendSpectraAlg->getProperty("OutputWorkspace"); -} - -/** - * Deletes the specified workspace. - * - * @param workspace The workspace to delete. - */ -void IndirectFitAnalysisTab::deleteWorkspace( - MatrixWorkspace_sptr workspace) const { - auto deleteWorkspaceAlg = - AlgorithmManager::Instance().create("DeleteWorkspace"); - deleteWorkspaceAlg->initialize(); - deleteWorkspaceAlg->setChild(true); - deleteWorkspaceAlg->setLogging(false); - deleteWorkspaceAlg->setProperty("Workspace", workspace); - deleteWorkspaceAlg->execute(); -} - -/** - * Crops the specified workspace. - * - * @param inputWS The workspace to crop. - * @param startX The x-value that is within the first bin to be retained. - * @param endX The x-value that is within the last bin to be retained. - * @param startIndex The index of the first entry to be retained. - * @param endIndex The index of the last entry to be retained. - * @return The cropped workspace. - */ -MatrixWorkspace_sptr -IndirectFitAnalysisTab::cropWorkspace(MatrixWorkspace_sptr inputWS, - double startX, double endX, - int startIndex, int endIndex) const { - IAlgorithm_sptr cropWorkspaceAlg = - AlgorithmManager::Instance().create("CropWorkspace"); - cropWorkspaceAlg->initialize(); - cropWorkspaceAlg->setChild(true); - cropWorkspaceAlg->setLogging(false); - cropWorkspaceAlg->setProperty("InputWorkspace", inputWS); - cropWorkspaceAlg->setProperty("XMin", startX); - cropWorkspaceAlg->setProperty("XMax", endX); - cropWorkspaceAlg->setProperty("StartWorkspaceIndex", startIndex); - cropWorkspaceAlg->setProperty("EndWorkspaceIndex", endIndex); - cropWorkspaceAlg->setProperty("OutputWorkspace", "__cropped"); - cropWorkspaceAlg->execute(); - return cropWorkspaceAlg->getProperty("OutputWorkspace"); -} - -/* - * Creates a guess workspace, for approximating a fit with the specified - * function on the input workspace. - * - * @param func The function to fit. - * @param wsIndex The index of the input workspace to create a guess for. - * @return A guess workspace containing the guess data for the fit. - */ -MatrixWorkspace_sptr -IndirectFitAnalysisTab::createGuessWorkspace(IFunction_const_sptr func, - int wsIndex) const { - auto croppedWS = - cropWorkspace(inputWorkspace(), startX(), endX(), wsIndex, wsIndex); - const auto dataY = computeOutput(func, croppedWS->points(0).rawData()); - - if (dataY.empty()) - return WorkspaceFactory::Instance().create("Workspace2D", 1, 1, 1); - - IAlgorithm_sptr createWsAlg = - createWorkspaceAlgorithm("__GuessAnon", 1, croppedWS->dataX(0), dataY); - createWsAlg->execute(); - return createWsAlg->getProperty("OutputWorkspace"); -} - -/* - * Computes the output vector of applying the specified function to - * the specified input vector. - * - * @param func The function to apply. - * @param dataX Vector of input data. - * @return Vector containing values calculated from applying - * the specified function to the input data. - */ -std::vector<double> -IndirectFitAnalysisTab::computeOutput(IFunction_const_sptr func, - const std::vector<double> &dataX) const { - if (dataX.empty()) - return std::vector<double>(); - - FunctionDomain1DVector domain(dataX); - FunctionValues outputData(domain); - func->function(domain, outputData); - - std::vector<double> dataY(dataX.size()); - for (size_t i = 0; i < dataY.size(); i++) { - dataY[i] = outputData.getCalculated(i); - } - return dataY; -} - -/* - * Generates and returns an algorithm for creating a workspace, with - * the specified name, number of spectra and containing the specified - * x data and y data. - * - * @param workspaceName The name of the workspace to create. - * @param numSpec The number of spectra in the workspace to create. - * @param dataX The x data to add to the created workspace. - * @param dataY The y data to add to the created workspace. - * @return An algorithm for creating the workspace. - */ -IAlgorithm_sptr IndirectFitAnalysisTab::createWorkspaceAlgorithm( - const std::string &workspaceName, int numSpec, - const std::vector<double> &dataX, const std::vector<double> &dataY) const { - IAlgorithm_sptr createWsAlg = - AlgorithmManager::Instance().create("CreateWorkspace"); - createWsAlg->initialize(); - createWsAlg->setChild(true); - createWsAlg->setLogging(false); - createWsAlg->setProperty("OutputWorkspace", workspaceName); - createWsAlg->setProperty("NSpec", numSpec); - createWsAlg->setProperty("DataX", dataX); - createWsAlg->setProperty("DataY", dataY); - return createWsAlg; -} - -void IndirectFitAnalysisTab::emitFunctionChanged() { emit functionChanged(); } - -void IndirectFitAnalysisTab::emitParameterChanged( - const Mantid::API::IFunction *function) { - emit parameterChanged(function); -} - -void IndirectFitAnalysisTab::emitCustomBoolChanged(const QString &key, - bool value) { - emit customBoolChanged(key, value); -} - } // namespace IDA } // namespace CustomInterfaces } // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h index e54f65951fa616b6c657c9576c1231f410ca89f5..e47bd499b132d90a069c475f8f6a73db838fe247 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h +++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h @@ -2,6 +2,8 @@ #define MANTIDQTCUSTOMINTERFACESIDA_IFATAB_H_ #include "IndirectDataAnalysisTab.h" +#include "IndirectFitDataPresenter.h" +#include "IndirectFitPlotPresenter.h" #include "IndirectFittingModel.h" #include "IndirectSpectrumSelectionPresenter.h" #include "IndirectSpectrumSelectionView.h" @@ -19,61 +21,6 @@ namespace MantidQt { namespace CustomInterfaces { namespace IDA { -class DLLExport QtLazyAsyncRunnerBase : public QObject { - Q_OBJECT - -signals: - void finished(); - void finishedLazy(); - -protected slots: - void currentFinishedBase() { currentFinished(); } - -protected: - virtual void currentFinished() = 0; -}; - -template <typename Callback> -class DLLExport QtLazyAsyncRunner : public QtLazyAsyncRunnerBase { -public: - using ReturnType = typename std::result_of<Callback()>::type; - - explicit QtLazyAsyncRunner() - : m_current(), m_next(boost::none), m_initialized(false) { - connect(&m_current, SIGNAL(finished()), this, SLOT(currentFinishedBase())); - } - - void addCallback(Callback &&callback) { - if (m_next.is_initialized()) - m_next = boost::none; - - if (m_current.isFinished() || !m_initialized) - m_current.setFuture(QtConcurrent::run(callback)); - else - m_next = std::forward<Callback>(callback); - m_initialized = true; - } - - bool isFinished() const { return m_current.isFinished(); } - - ReturnType result() const { return m_current.result(); } - -protected: - void currentFinished() override { - if (m_next.is_initialized()) { - m_current.setFuture(QtConcurrent::run(*m_next)); - m_next = boost::none; - emit finished(); - } else - emit finishedLazy(); - } - -private: - QFutureWatcher<ReturnType> m_current; - boost::optional<Callback> m_next; - bool m_initialized; -}; - class DLLExport IndirectFitAnalysisTab : public IndirectDataAnalysisTab { Q_OBJECT @@ -82,38 +29,21 @@ public: IndirectFitAnalysisTab(IndirectFittingModel *model, QWidget *parent = nullptr); + void setFitDataPresenter(std::unique_ptr<IndirectFitDataPresenter> presenter); + void setPlotView(IndirectFitPlotView *view); void setSpectrumSelectionView(IndirectSpectrumSelectionView *view); void setFitPropertyBrowser(MantidWidgets::IndirectFitPropertyBrowser *browser); - Mantid::API::IFunction_sptr background() const; - - Mantid::API::IFunction_sptr model() const; - - boost::optional<size_t> backgroundIndex() const; + std::size_t getSelectedDataIndex() const; + std::size_t getSelectedSpectrum() const; + bool isRangeCurrentlySelected(std::size_t dataIndex, + std::size_t spectrum) const; QString selectedFitType() const; size_t numberOfCustomFunctions(const std::string &functionName) const; - double startX() const; - - double endX() const; - - std::vector<double> parameterValue(const std::string &functionName, - const std::string ¶meterName) const; - boost::optional<double> - lastParameterValue(const std::string &functionName, - const std::string ¶meterName) const; - - bool isEmptyModel() const; - - QString backgroundName() const; - - virtual bool canPlotGuess() const; - - virtual bool doPlotGuess() const = 0; - void setConvolveMembers(bool convolveMembers); void updateTies(); @@ -176,47 +106,26 @@ public: bool changesFunction); protected: - bool validate() override; IndirectFittingModel *fittingModel() const; - void plotResult(const QString &plotType); - - void fillPlotTypeComboBox(QComboBox *comboBox); + void setSampleWSSuffices(const QStringList &suffices); + void setSampleFBSuffices(const QStringList &suffices); + void setResolutionWSSuffices(const QStringList &suffices); + void setResolutionFBSuffices(const QStringList &suffices); - void updatePlots(MantidWidgets::PreviewPlot *fitPreviewPlot, - MantidWidgets::PreviewPlot *diffPreviewPlot); + void plotResult(const QString &plotType); + void plotAll(Mantid::API::WorkspaceGroup_sptr workspaces); + void plotParameter(Mantid::API::WorkspaceGroup_sptr workspace, + const std::string ¶meter); + void plotAll(Mantid::API::MatrixWorkspace_sptr workspace); + void plotParameter(Mantid::API::MatrixWorkspace_sptr workspace, + const std::string ¶meter); void setAlgorithmProperties(Mantid::API::IAlgorithm_sptr fitAlgorithm) const; void runFitAlgorithm(Mantid::API::IAlgorithm_sptr fitAlgorithm); void runSingleFit(Mantid::API::IAlgorithm_sptr fitAlgorithm); virtual void setupFit(Mantid::API::IAlgorithm_sptr fitAlgorithm); - virtual void addGuessPlot(Mantid::API::MatrixWorkspace_sptr workspace) = 0; - virtual void removeGuessPlot() = 0; - void updateGuessPlots(Mantid::API::IFunction_sptr guessFunction); - - void updatePlotGuess(Mantid::API::MatrixWorkspace_sptr workspace); - void updatePlotGuessInWindow(Mantid::API::MatrixWorkspace_sptr workspace); - - Mantid::API::MatrixWorkspace_sptr createInputAndGuessWorkspace( - Mantid::API::MatrixWorkspace_sptr guessWorkspace); - - Mantid::API::MatrixWorkspace_sptr - createInputAndGuessWorkspace(Mantid::API::MatrixWorkspace_sptr inputWS, - Mantid::API::MatrixWorkspace_sptr guessWorkspace, - const std::string &outputName) const; - - Mantid::API::MatrixWorkspace_sptr createInputAndGuessWorkspace( - Mantid::API::MatrixWorkspace_sptr inputWS, - Mantid::API::MatrixWorkspace_sptr guessWorkspace) const; - - Mantid::API::MatrixWorkspace_sptr - createGuessWorkspace(Mantid::API::IFunction_const_sptr func, - int wsIndex) const; - - std::vector<double> computeOutput(Mantid::API::IFunction_const_sptr func, - const std::vector<double> &dataX) const; - void updatePlotOptions(QComboBox *cbPlotType); void setPlotOptions(QComboBox *cbPlotType, @@ -227,106 +136,68 @@ protected: virtual void setPlotResultEnabled(bool enabled) = 0; virtual void setSaveResultEnabled(bool enabled) = 0; - virtual void enablePlotPreview() = 0; - virtual void disablePlotPreview() = 0; signals: void functionChanged(); - void parameterChanged(const Mantid::API::IFunction *); - void customBoolChanged(const QString &key, bool value); protected slots: void setModelFitFunction(); void setModelStartX(double startX); - void setModelEndX(double endX); + void setModelEndX(double startX); + void setDataTableStartX(double startX); + void setDataTableEndX(double endX); + void setDataTableExclude(const std::string &exclude); + void setBrowserStartX(double startX); + void setBrowserEndX(double endX); + void updateBrowserFittingRange(); + void setBrowserWorkspace(std::size_t dataIndex); + void setBrowserWorkspaceIndex(std::size_t spectrum); + void tableStartXChanged(double startX, std::size_t dataIndex, + std::size_t spectrum); + void tableEndXChanged(double endX, std::size_t dataIndex, + std::size_t spectrum); + void tableExcludeChanged(const std::string &exclude, std::size_t dataIndex, + std::size_t spectrum); void updateFitOutput(bool error); void updateSingleFitOutput(bool error); void fitAlgorithmComplete(bool error); - void clearGuessWindowPlot(); - - void setSelectedSpectrum(int spectrum) override; - - virtual void startXChanged(double startX) = 0; - - virtual void endXChanged(double endX) = 0; - - void xMinSelected(double xMin); - - void xMaxSelected(double xMax); - - virtual void updatePlotRange() = 0; - void singleFit(); - + void singleFit(std::size_t dataIndex, std::size_t spectrum); void executeFit(); - void newInputDataLoaded(const QString &wsName); - + void updateFitBrowserParameterValues(); void updateParameterValues(); - void updateParameterValues( const std::unordered_map<std::string, ParameterValue> ¶meters); - virtual void updatePreviewPlots() = 0; - - void updateGuessPlots(); - - void updatePlotGuess(); - - void updatePlotGuessInWindow(); - - void plotGuessInWindow(); - virtual void updatePlotOptions() = 0; - void emitFunctionChanged(); - - void emitParameterChanged(const Mantid::API::IFunction *); - - void emitCustomBoolChanged(const QString &key, bool value); - void updateResultOptions(); - void saveResult(); private: /// Overidden by child class. void setup() override; + void loadSettings(const QSettings &settings) override; virtual void setupFitTab() = 0; + bool validate() override; void run() override; - void loadSettings(const QSettings &settings) override = 0; - virtual void disablePlotGuess() = 0; - virtual void enablePlotGuess() = 0; - void - ensureAppendCompatibility(Mantid::API::MatrixWorkspace_sptr inputWS, - Mantid::API::MatrixWorkspace_sptr spectraWS) const; - - Mantid::API::IAlgorithm_sptr - createWorkspaceAlgorithm(const std::string &workspaceName, int numSpec, - const std::vector<double> &dataX, - const std::vector<double> &dataY) const; - Mantid::API::MatrixWorkspace_sptr - extractSpectra(Mantid::API::MatrixWorkspace_sptr inputWS, int startIndex, - int endIndex, double startX, double endX) const; - Mantid::API::MatrixWorkspace_sptr - appendSpectra(Mantid::API::MatrixWorkspace_sptr inputWS, - Mantid::API::MatrixWorkspace_sptr spectraWS) const; - Mantid::API::MatrixWorkspace_sptr - cropWorkspace(Mantid::API::MatrixWorkspace_sptr inputWS, double startX, - double endX, int startIndex, int endIndex) const; - void deleteWorkspace(Mantid::API::MatrixWorkspace_sptr workspace) const; - - Mantid::API::MatrixWorkspace_sptr m_inputAndGuessWorkspace; - QtLazyAsyncRunner<std::function<void()>> m_plotWindowGuessRunner; + void connectDataAndPlotPresenters(); + void connectSpectrumAndPlotPresenters(); + void connectFitBrowserAndPlotPresenter(); + void connectDataAndSpectrumPresenters(); + void connectDataAndFitBrowserPresenters(); std::unique_ptr<IndirectFittingModel> m_fittingModel; MantidWidgets::IndirectFitPropertyBrowser *m_fitPropertyBrowser; + std::unique_ptr<IndirectFitDataPresenter> m_dataPresenter; + std::unique_ptr<IndirectFitPlotPresenter> m_plotPresenter; std::unique_ptr<IndirectSpectrumSelectionPresenter> m_spectrumPresenter; Mantid::API::IAlgorithm_sptr m_fittingAlgorithm; diff --git a/qt/scientific_interfaces/Indirect/IndirectFitData.cpp b/qt/scientific_interfaces/Indirect/IndirectFitData.cpp index 587f823a71a8e2fc12b76e7de66afeeddf967202..c50891e76dffc2d60c8eec28451299d4b3ebe392 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitData.cpp +++ b/qt/scientific_interfaces/Indirect/IndirectFitData.cpp @@ -63,7 +63,7 @@ struct CheckZeroSpectrum : boost::static_visitor<bool> { struct NumberOfSpectra : boost::static_visitor<std::size_t> { std::size_t operator()(const std::pair<std::size_t, std::size_t> &spectra) const { - return spectra.second - spectra.first; + return 1 + (spectra.second - spectra.first); } std::size_t @@ -173,7 +173,9 @@ namespace IDA { IndirectFitData::IndirectFitData(MatrixWorkspace_sptr workspace, const Spectra &spectra) - : m_workspace(workspace), m_spectra(spectra) {} + : m_workspace(workspace), m_spectra(DiscontinuousSpectra<std::size_t>("")) { + setSpectra(spectra); +} std::string IndirectFitData::displayName(const std::string &formatString, @@ -185,7 +187,10 @@ IndirectFitData::displayName(const std::string &formatString, auto formatted = boost::format(formatString); formatted = tryPassFormatArgument(formatted, workspaceName); formatted = tryPassFormatArgument(formatted, spectraString); - return formatted.str(); + + auto name = formatted.str(); + std::replace(name.begin(), name.end(), ',', '+'); + return name; } std::string IndirectFitData::displayName(const std::string &formatString, @@ -199,7 +204,7 @@ std::string IndirectFitData::displayName(const std::string &formatString, } Mantid::API::MatrixWorkspace_sptr IndirectFitData::workspace() const { - return m_workspace.lock(); + return m_workspace; } const Spectra &IndirectFitData::spectra() const { return m_spectra; } @@ -221,7 +226,7 @@ IndirectFitData::getRange(std::size_t spectrum) const { const auto range = m_ranges.find(spectrum); if (range != m_ranges.end()) return range->second; - return getBinRange(m_workspace.lock()); + return getBinRange(m_workspace); } std::string IndirectFitData::getExcludeRegion(std::size_t spectrum) const { @@ -266,18 +271,21 @@ void IndirectFitData::setStartX(double startX, std::size_t spectrum) { const auto range = m_ranges.find(spectrum); if (range != m_ranges.end()) range->second.first = startX; - else if (const auto workspace = m_workspace.lock()) - m_ranges[spectrum] = std::make_pair(startX, workspace->x(0).back()); - throw std::runtime_error("Unable to set StartX: Workspace no longer exists."); + else if (m_workspace) + m_ranges[spectrum] = std::make_pair(startX, m_workspace->x(0).back()); + else + throw std::runtime_error( + "Unable to set StartX: Workspace no longer exists."); } void IndirectFitData::setEndX(double endX, std::size_t spectrum) { const auto range = m_ranges.find(spectrum); if (range != m_ranges.end()) range->second.second = endX; - else if (const auto workspace = m_workspace.lock()) - m_ranges[spectrum] = std::make_pair(workspace->x(0).front(), endX); - throw std::runtime_error("Unable to set EndX: Workspace no longer exists."); + else if (m_workspace) + m_ranges[spectrum] = std::make_pair(m_workspace->x(0).front(), endX); + else + throw std::runtime_error("Unable to set EndX: Workspace no longer exists."); } void IndirectFitData::setExcludeRegionString(const std::string &excludeRegion, @@ -287,8 +295,8 @@ void IndirectFitData::setExcludeRegionString(const std::string &excludeRegion, IndirectFitData &IndirectFitData::combine(const IndirectFitData &fitData) { m_workspace = fitData.m_workspace; - m_spectra = - boost::apply_visitor(CombineSpectra(), m_spectra, fitData.m_spectra); + setSpectra( + boost::apply_visitor(CombineSpectra(), m_spectra, fitData.m_spectra)); m_excludeRegions.insert(std::begin(fitData.m_excludeRegions), std::end(fitData.m_excludeRegions)); m_ranges.insert(std::begin(fitData.m_ranges), std::end(fitData.m_ranges)); diff --git a/qt/scientific_interfaces/Indirect/IndirectFitData.h b/qt/scientific_interfaces/Indirect/IndirectFitData.h index a31d87d8770da9a48c4b82fce4f569d3ca1c3217..f418c928a7b0c2c47dc6d468c49d6c7e2bfaba10 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitData.h +++ b/qt/scientific_interfaces/Indirect/IndirectFitData.h @@ -9,6 +9,8 @@ #include <boost/variant/static_visitor.hpp> #include <boost/weak_ptr.hpp> +#include <cctype> + namespace MantidQt { namespace CustomInterfaces { namespace IDA { @@ -31,7 +33,11 @@ std::vector<T> vectorFromString(const std::string &listString) { template <typename T> class DiscontinuousSpectra { public: explicit DiscontinuousSpectra(const std::string &str) - : m_str(str), m_vec(vectorFromString<T>(str)) {} + : m_str(str), m_vec(vectorFromString<T>(str)) { + m_str.erase(std::remove_if(m_str.begin(), m_str.end(), + static_cast<int (*)(int)>(std::isspace)), + m_str.end()); + } DiscontinuousSpectra(const DiscontinuousSpectra &vec) : m_str(vec.m_str), m_vec(vec.m_vec) {} DiscontinuousSpectra(DiscontinuousSpectra &&vec) @@ -78,7 +84,7 @@ using Spectra = boost::variant<DiscontinuousSpectra<std::size_t>, std::pair<std::size_t, std::size_t>>; template <typename F> struct ApplySpectra : boost::static_visitor<> { - explicit ApplySpectra(F &&functor) : m_functor(functor) {} + explicit ApplySpectra(F &&functor) : m_functor(std::forward<F>(functor)) {} void operator()(const std::pair<std::size_t, std::size_t> &spectra) const { for (auto spectrum = spectra.first; spectrum <= spectra.second; ++spectrum) @@ -96,8 +102,8 @@ private: template <typename F> struct ApplyEnumeratedSpectra : boost::static_visitor<std::size_t> { - ApplyEnumeratedSpectra(F const &functor, std::size_t start = 0) - : m_start(start), m_functor(functor) {} + ApplyEnumeratedSpectra(F &&functor, std::size_t start = 0) + : m_start(start), m_functor(std::forward<F>(functor)) {} std::size_t operator()(const std::pair<std::size_t, std::size_t> &spectra) const { @@ -168,14 +174,13 @@ public: std::vector<double> excludeRegionsVector(std::size_t spectrum) const; template <typename F> void applySpectra(F &&functor) const { - boost::apply_visitor(ApplySpectra<F>(functor), m_spectra); + boost::apply_visitor(ApplySpectra<F>(std::forward<F>(functor)), m_spectra); } template <typename F> - std::size_t applyEnumeratedSpectra(F const &functor, - std::size_t start = 0) const { - return boost::apply_visitor(ApplyEnumeratedSpectra<F>(functor, start), - m_spectra); + std::size_t applyEnumeratedSpectra(F &&functor, std::size_t start = 0) const { + return boost::apply_visitor( + ApplyEnumeratedSpectra<F>(std::forward<F>(functor), start), m_spectra); } void setSpectra(const std::string &spectra); @@ -189,7 +194,7 @@ public: private: void validateSpectra(const Spectra &spectra); - boost::weak_ptr<Mantid::API::MatrixWorkspace> m_workspace; + Mantid::API::MatrixWorkspace_sptr m_workspace; Spectra m_spectra; std::unordered_map<std::size_t, std::string> m_excludeRegions; std::unordered_map<std::size_t, std::pair<double, double>> m_ranges; diff --git a/qt/scientific_interfaces/Indirect/IndirectFitDataPresenter.cpp b/qt/scientific_interfaces/Indirect/IndirectFitDataPresenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..472bc8b87e8582884a4fc0ec2f42e042670c9ba6 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitDataPresenter.cpp @@ -0,0 +1,198 @@ +#include "IndirectFitDataPresenter.h" +#include "IndirectAddWorkspaceDialog.h" + +#include "MantidKernel/make_unique.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +IndirectFitDataPresenter::IndirectFitDataPresenter(IndirectFittingModel *model, + IndirectFitDataView *view) + : IndirectFitDataPresenter( + model, view, Mantid::Kernel::make_unique<IndirectDataTablePresenter>( + model, view->getDataTable())) {} + +IndirectFitDataPresenter::IndirectFitDataPresenter( + IndirectFittingModel *model, IndirectFitDataView *view, + std::unique_ptr<IndirectDataTablePresenter> tablePresenter) + : m_model(model), m_view(view), + m_tablePresenter(std::move(tablePresenter)) { + connect(m_view, SIGNAL(singleDataViewSelected()), this, + SLOT(setModelFromSingleData())); + connect(m_view, SIGNAL(multipleDataViewSelected()), this, + SLOT(setModelFromMultipleData())); + + connect(m_view, SIGNAL(singleDataViewSelected()), this, + SIGNAL(singleDataViewSelected())); + connect(m_view, SIGNAL(multipleDataViewSelected()), this, + SIGNAL(multipleDataViewSelected())); + + connect(m_view, SIGNAL(sampleLoaded(const QString &)), this, + SLOT(setModelWorkspace(const QString &))); + connect(m_view, SIGNAL(sampleLoaded(const QString &)), this, + SIGNAL(singleSampleLoaded())); + connect(m_view, SIGNAL(sampleLoaded(const QString &)), this, + SIGNAL(dataChanged())); + + connect(m_view, SIGNAL(addClicked()), this, + SIGNAL(requestedAddWorkspaceDialog())); + connect(m_view, SIGNAL(addClicked()), this, SLOT(showAddWorkspaceDialog())); + + connect(m_view, SIGNAL(removeClicked()), m_tablePresenter.get(), + SLOT(removeSelectedData())); + connect(m_view, SIGNAL(removeClicked()), this, SIGNAL(dataRemoved())); + connect(m_view, SIGNAL(removeClicked()), this, SIGNAL(dataChanged())); + + connect(m_tablePresenter.get(), + SIGNAL(startXChanged(double, std::size_t, std::size_t)), this, + SIGNAL(startXChanged(double, std::size_t, std::size_t))); + connect(m_tablePresenter.get(), + SIGNAL(endXChanged(double, std::size_t, std::size_t)), this, + SIGNAL(endXChanged(double, std::size_t, std::size_t))); + connect(m_tablePresenter.get(), + SIGNAL(excludeRegionChanged(const std::string &, std::size_t, + std::size_t)), + this, SIGNAL(excludeRegionChanged(const std::string &, std::size_t, + std::size_t))); +} + +IndirectFitDataView const *IndirectFitDataPresenter::getView() const { + return m_view; +} + +void IndirectFitDataPresenter::setSampleWSSuffices( + const QStringList &suffices) { + m_view->setSampleWSSuffices(suffices); +} + +void IndirectFitDataPresenter::setSampleFBSuffices( + const QStringList &suffices) { + m_view->setSampleFBSuffices(suffices); +} + +void IndirectFitDataPresenter::setResolutionWSSuffices( + const QStringList &suffices) { + m_view->setResolutionWSSuffices(suffices); +} + +void IndirectFitDataPresenter::setResolutionFBSuffices( + const QStringList &suffices) { + m_view->setResolutionFBSuffices(suffices); +} + +void IndirectFitDataPresenter::setStartX(double startX, std::size_t dataIndex, + int spectrumIndex) { + m_tablePresenter->setStartX(startX, dataIndex, spectrumIndex); +} + +void IndirectFitDataPresenter::setEndX(double endX, std::size_t dataIndex, + int spectrumIndex) { + m_tablePresenter->setEndX(endX, dataIndex, spectrumIndex); +} + +void IndirectFitDataPresenter::setExclude(const std::string &exclude, + std::size_t dataIndex, + int spectrumIndex) { + m_tablePresenter->setExclude(exclude, dataIndex, spectrumIndex); +} + +void IndirectFitDataPresenter::setModelFromSingleData() { + m_multipleData = m_model->clearWorkspaces(); + m_model->setFittingData(std::move(m_singleData)); + emit dataChanged(); +} + +void IndirectFitDataPresenter::setModelFromMultipleData() { + m_singleData = m_model->clearWorkspaces(); + m_model->setFittingData(std::move(m_multipleData)); + emit dataChanged(); +} + +void IndirectFitDataPresenter::updateSpectraInTable(std::size_t dataIndex) { + if (m_view->isMultipleDataTabSelected()) + m_tablePresenter->updateData(dataIndex); +} + +void IndirectFitDataPresenter::updateDataInTable(std::size_t dataIndex) { + if (m_tablePresenter->isTableEmpty()) + m_tablePresenter->addData(dataIndex); + else + m_tablePresenter->updateData(dataIndex); +} + +void IndirectFitDataPresenter::setResolutionHidden(bool hide) { + m_view->setResolutionHidden(hide); +} + +void IndirectFitDataPresenter::setModelWorkspace(const QString &name) { + setSingleModelData(name.toStdString()); +} + +void IndirectFitDataPresenter::loadSettings(const QSettings &settings) { + m_view->readSettings(settings); +} + +UserInputValidator & +IndirectFitDataPresenter::validate(UserInputValidator &validator) { + return m_view->validate(validator); +} + +void IndirectFitDataPresenter::showAddWorkspaceDialog() { + const auto dialog = getAddWorkspaceDialog(m_view->parentWidget()); + dialog->setWSSuffices(m_view->getSampleWSSuffices()); + dialog->setFBSuffices(m_view->getSampleFBSuffices()); + dialogExecuted(dialog.get(), + static_cast<QDialog::DialogCode>(dialog->exec())); +} + +void IndirectFitDataPresenter::dialogExecuted(IAddWorkspaceDialog const *dialog, + QDialog::DialogCode result) { + if (result == QDialog::Accepted) + addData(dialog); +} + +std::unique_ptr<IAddWorkspaceDialog> +IndirectFitDataPresenter::getAddWorkspaceDialog(QWidget *parent) const { + return Mantid::Kernel::make_unique<AddWorkspaceDialog>(parent); +} + +void IndirectFitDataPresenter::addData(IAddWorkspaceDialog const *dialog) { + try { + addDataToModel(dialog); + m_tablePresenter->addData(m_model->numberOfWorkspaces() - 1); + emit dataAdded(); + emit dataChanged(); + } catch (const std::runtime_error &ex) { + displayWarning(ex.what()); + } +} + +void IndirectFitDataPresenter::addDataToModel( + IAddWorkspaceDialog const *dialog) { + if (const auto indirectDialog = + dynamic_cast<AddWorkspaceDialog const *>(dialog)) + m_model->addWorkspace(indirectDialog->workspaceName(), + indirectDialog->workspaceIndices()); +} + +void IndirectFitDataPresenter::setSingleModelData(const std::string &name) { + m_model->clearWorkspaces(); + addModelData(name); +} + +void IndirectFitDataPresenter::addModelData(const std::string &name) { + try { + m_model->addWorkspace(name); + } catch (const std::runtime_error &ex) { + displayWarning("Unable to load workspace:\n" + std::string(ex.what())); + } +} + +void IndirectFitDataPresenter::displayWarning(const std::string &warning) { + m_view->displayWarning(warning); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IndirectFitDataPresenter.h b/qt/scientific_interfaces/Indirect/IndirectFitDataPresenter.h new file mode 100644 index 0000000000000000000000000000000000000000..dcd98df89c5714e584039118e8901b0501a2f58a --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitDataPresenter.h @@ -0,0 +1,107 @@ +#ifndef MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITDATAPRESENTER_H_ +#define MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITDATAPRESENTER_H_ + +#include "IAddWorkspaceDialog.h" +#include "IndirectDataTablePresenter.h" +#include "IndirectFitDataView.h" +#include "IndirectFittingModel.h" + +#include "MantidAPI/MatrixWorkspace.h" + +#include <QObject> + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +/** + A presenter. + Copyright © 2015-2016 ISIS Rutherford Appleton Laboratory, NScD + Oak Ridge National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport IndirectFitDataPresenter : public QObject { + Q_OBJECT +public: + IndirectFitDataPresenter(IndirectFittingModel *model, + IndirectFitDataView *view); + + IndirectFitDataPresenter( + IndirectFittingModel *model, IndirectFitDataView *view, + std::unique_ptr<IndirectDataTablePresenter> tablePresenter); + + void setSampleWSSuffices(const QStringList &suffices); + void setSampleFBSuffices(const QStringList &suffices); + void setResolutionWSSuffices(const QStringList &suffices); + void setResolutionFBSuffices(const QStringList &suffices); + + void setStartX(double startX, std::size_t dataIndex, int spectrumIndex); + void setEndX(double endX, std::size_t dataIndex, int spectrumIndex); + void setExclude(const std::string &exclude, std::size_t dataIndex, + int spectrumIndex); + + void loadSettings(const QSettings &settings); + UserInputValidator &validate(UserInputValidator &validator); + +public slots: + void updateSpectraInTable(std::size_t dataIndex); + +protected slots: + void setModelWorkspace(const QString &name); + void setModelFromSingleData(); + void setModelFromMultipleData(); + void showAddWorkspaceDialog(); + +signals: + void singleSampleLoaded(); + void singleResolutionLoaded(); + void dataAdded(); + void dataRemoved(); + void dataChanged(); + void startXChanged(double, std::size_t, std::size_t); + void endXChanged(double, std::size_t, std::size_t); + void excludeRegionChanged(const std::string &, std::size_t, std::size_t); + void multipleDataViewSelected(); + void singleDataViewSelected(); + void requestedAddWorkspaceDialog(); + +protected: + IndirectFitDataView const *getView() const; + void addData(IAddWorkspaceDialog const *dialog); + virtual void addDataToModel(IAddWorkspaceDialog const *dialog); + void setSingleModelData(const std::string &name); + virtual void addModelData(const std::string &name); + void setResolutionHidden(bool hide); + void displayWarning(const std::string &warning); + virtual void dialogExecuted(IAddWorkspaceDialog const *dialog, + QDialog::DialogCode result); + +private: + virtual std::unique_ptr<IAddWorkspaceDialog> + getAddWorkspaceDialog(QWidget *parent) const; + void updateDataInTable(std::size_t dataIndex); + + IndirectFittingModel *m_model; + PrivateFittingData m_singleData; + PrivateFittingData m_multipleData; + IndirectFitDataView *m_view; + std::unique_ptr<IndirectDataTablePresenter> m_tablePresenter; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITDATAPRESENTER_H_ */ diff --git a/qt/scientific_interfaces/Indirect/IndirectFitDataView.cpp b/qt/scientific_interfaces/Indirect/IndirectFitDataView.cpp new file mode 100644 index 0000000000000000000000000000000000000000..16dffcf19f843828f90809cf07a0d5db35637f50 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitDataView.cpp @@ -0,0 +1,129 @@ +#include "IndirectFitDataView.h" + +using namespace Mantid::API; + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +IndirectFitDataView::IndirectFitDataView(QWidget *parent) + : QTabWidget(parent), m_dataForm(new Ui::IndirectFitDataForm) { + m_dataForm->setupUi(this); + m_dataForm->dsResolution->hide(); + m_dataForm->lbResolution->hide(); + + connect(m_dataForm->dsSample, SIGNAL(dataReady(const QString &)), this, + SIGNAL(sampleLoaded(const QString &))); + connect(m_dataForm->dsResolution, SIGNAL(dataReady(const QString &)), this, + SIGNAL(resolutionLoaded(const QString &))); + connect(m_dataForm->pbAdd, SIGNAL(clicked()), this, SIGNAL(addClicked())); + connect(m_dataForm->pbRemove, SIGNAL(clicked()), this, + SIGNAL(removeClicked())); + + connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitViewSelected(int))); +} + +QTableWidget *IndirectFitDataView::getDataTable() const { + return m_dataForm->tbFitData; +} + +bool IndirectFitDataView::isMultipleDataTabSelected() const { + return currentIndex() == 1; +} + +bool IndirectFitDataView::isResolutionHidden() const { + return m_dataForm->dsResolution->isHidden(); +} + +std::string IndirectFitDataView::getSelectedSample() const { + return m_dataForm->dsSample->getCurrentDataName().toStdString(); +} + +std::string IndirectFitDataView::getSelectedResolution() const { + return m_dataForm->dsResolution->getCurrentDataName().toStdString(); +} + +void IndirectFitDataView::readSettings(const QSettings &settings) { + const auto group = settings.group(); + m_dataForm->dsSample->readSettings(group); + m_dataForm->dsResolution->readSettings(group); +} + +void IndirectFitDataView::disableMultipleDataTab() { setTabEnabled(1, false); } + +QStringList IndirectFitDataView::getSampleWSSuffices() const { + return m_dataForm->dsSample->getWSSuffixes(); +} + +QStringList IndirectFitDataView::getSampleFBSuffices() const { + return m_dataForm->dsSample->getFBSuffixes(); +} + +QStringList IndirectFitDataView::getResolutionWSSuffices() const { + return m_dataForm->dsResolution->getWSSuffixes(); +} + +QStringList IndirectFitDataView::getResolutionFBSuffices() const { + return m_dataForm->dsResolution->getFBSuffixes(); +} + +void IndirectFitDataView::setSampleWSSuffices(const QStringList &suffices) { + m_dataForm->dsSample->setWSSuffixes(suffices); +} + +void IndirectFitDataView::setSampleFBSuffices(const QStringList &suffices) { + m_dataForm->dsSample->setFBSuffixes(suffices); +} + +void IndirectFitDataView::setResolutionWSSuffices(const QStringList &suffices) { + m_dataForm->dsResolution->setWSSuffixes(suffices); +} + +void IndirectFitDataView::setResolutionFBSuffices(const QStringList &suffices) { + m_dataForm->dsResolution->setFBSuffixes(suffices); +} + +UserInputValidator & +IndirectFitDataView::validate(UserInputValidator &validator) { + if (currentIndex() == 0) + return validateSingleData(validator); + return validateMultipleData(validator); +} + +UserInputValidator & +IndirectFitDataView::validateMultipleData(UserInputValidator &validator) { + if (m_dataForm->tbFitData->rowCount() == 0) + validator.addErrorMessage("No input data has been provided."); + return validator; +} + +UserInputValidator & +IndirectFitDataView::validateSingleData(UserInputValidator &validator) { + validator.checkDataSelectorIsValid("Sample Input", m_dataForm->dsSample); + + if (!isResolutionHidden()) + validator.checkDataSelectorIsValid("Resolution Input", + m_dataForm->dsResolution); + return validator; +} + +void IndirectFitDataView::displayWarning(const std::string &warning) { + QMessageBox::warning(parentWidget(), "MantidPlot - Warning", + QString::fromStdString(warning)); +} + +void IndirectFitDataView::setResolutionHidden(bool hide) { + m_dataForm->lbResolution->setHidden(hide); + m_dataForm->dsResolution->setHidden(hide); +} + +void IndirectFitDataView::emitViewSelected(int index) { + if (index == 0) + emit singleDataViewSelected(); + else + emit multipleDataViewSelected(); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IndirectFitDataView.h b/qt/scientific_interfaces/Indirect/IndirectFitDataView.h new file mode 100644 index 0000000000000000000000000000000000000000..020b211c7af8ba52feb28f9984daadb8f4f4ed94 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitDataView.h @@ -0,0 +1,84 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_INDIRECTFITDATAVIEW_H_ +#define MANTIDQTCUSTOMINTERFACES_INDIRECTFITDATAVIEW_H_ + +#include "ui_IndirectFitDataView.h" + +#include "../General/UserInputValidator.h" + +#include <QTabWidget> + +/** + Copyright © 2015-2016 ISIS Rutherford Appleton Laboratory, NScD + Oak Ridge National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class DLLExport IndirectFitDataView : public QTabWidget { + Q_OBJECT +public: + IndirectFitDataView(QWidget *parent); + ~IndirectFitDataView() override = default; + + QTableWidget *getDataTable() const; + bool isMultipleDataTabSelected() const; + bool isResolutionHidden() const; + void setResolutionHidden(bool hide); + void disableMultipleDataTab(); + + std::string getSelectedSample() const; + std::string getSelectedResolution() const; + + QStringList getSampleWSSuffices() const; + QStringList getSampleFBSuffices() const; + QStringList getResolutionWSSuffices() const; + QStringList getResolutionFBSuffices() const; + + void setSampleWSSuffices(const QStringList &suffices); + void setSampleFBSuffices(const QStringList &suffices); + void setResolutionWSSuffices(const QStringList &suffices); + void setResolutionFBSuffices(const QStringList &suffices); + + void readSettings(const QSettings &settings); + UserInputValidator &validate(UserInputValidator &validator); + +public slots: + void displayWarning(const std::string &warning); + +signals: + void sampleLoaded(const QString &); + void resolutionLoaded(const QString &); + void addClicked(); + void removeClicked(); + void multipleDataViewSelected(); + void singleDataViewSelected(); + +protected slots: + void emitViewSelected(int index); + +private: + UserInputValidator &validateMultipleData(UserInputValidator &validator); + UserInputValidator &validateSingleData(UserInputValidator &validator); + + std::unique_ptr<Ui::IndirectFitDataForm> m_dataForm; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACES_INDIRECTFITDATAVIEW_H_ */ diff --git a/qt/scientific_interfaces/Indirect/IndirectFitDataView.ui b/qt/scientific_interfaces/Indirect/IndirectFitDataView.ui new file mode 100644 index 0000000000000000000000000000000000000000..882a1463e4d1f3dc362b02e6275b01a0b911249f --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitDataView.ui @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>IndirectFitDataForm</class> + <widget class="QTabWidget" name="IndirectFitDataForm"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>654</width> + <height>222</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>TabWidget</string> + </property> + <property name="tabPosition"> + <enum>QTabWidget::North</enum> + </property> + <property name="tabShape"> + <enum>QTabWidget::Rounded</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <property name="elideMode"> + <enum>Qt::ElideLeft</enum> + </property> + <widget class="QWidget" name="loSingleInput"> + <attribute name="title"> + <string>Single Input</string> + </attribute> + <layout class="QGridLayout" name="gridLayout" columnstretch="0,1"> + <item row="0" column="0"> + <widget class="QLabel" name="lbSample"> + <property name="text"> + <string>Sample</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="lbResolution"> + <property name="text"> + <string>Resolution</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="MantidQt::MantidWidgets::DataSelector" name="dsSample" native="true"> + <property name="autoLoad" stdset="0"> + <bool>true</bool> + </property> + <property name="ShowGroups" stdset="0"> + <bool>false</bool> + </property> + <property name="showLoad" stdset="0"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="MantidQt::MantidWidgets::DataSelector" name="dsResolution" native="true"> + <property name="autoLoad" stdset="0"> + <bool>true</bool> + </property> + <property name="ShowGroups" stdset="0"> + <bool>false</bool> + </property> + <property name="showLoad" stdset="0"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Multiple Input</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTableWidget" name="tbFitData"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="loDataControl"> + <property name="rightMargin"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="pbAdd"> + <property name="text"> + <string>Add Workspace</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pbRemove"> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + <customwidgets> + <customwidget> + <class>MantidQt::MantidWidgets::DataSelector</class> + <extends>QWidget</extends> + <header>MantidQtWidgets/Common/DataSelector.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/qt/scientific_interfaces/Indirect/IndirectFitOutput.cpp b/qt/scientific_interfaces/Indirect/IndirectFitOutput.cpp index 603ea76f1408c0786ca429ec6c6b79f20249cf54..cd134668d34aefc76f2442e3b63e97e68c43fe7a 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitOutput.cpp +++ b/qt/scientific_interfaces/Indirect/IndirectFitOutput.cpp @@ -1,6 +1,12 @@ #include "IndirectFitOutput.h" +#include "MantidAPI/AnalysisDataService.h" #include "MantidAPI/TableRow.h" +#include "MantidAPI/TextAxis.h" + +#include <boost/functional/hash.hpp> + +#include <unordered_set> using namespace Mantid::API; @@ -41,13 +47,20 @@ typename Map::mapped_type &extractOrAddDefault(Map &map, const Key &key) { } template <typename F> -void applyEnumeratedData(const F &functor, const FitDataIterator &fitDataBegin, +void applyEnumeratedData(F &&functor, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) { std::size_t start = 0; for (auto it = fitDataBegin; it < fitDataEnd; ++it) start = (*it)->applyEnumeratedSpectra(functor(it->get()), start); } +template <typename F> +void applyData(F &&functor, const FitDataIterator &fitDataBegin, + const FitDataIterator &fitDataEnd) { + for (auto it = fitDataBegin; it < fitDataEnd; ++it) + (*it)->applySpectra(functor(it->get())); +} + void extractParametersFromTable( ITableWorkspace_sptr tableWs, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd, @@ -92,6 +105,116 @@ Map mapKeys(const Map &map, const KeyMap &keyMap) { } return newMap; } + +MatrixWorkspace_sptr getMatrixWorkspaceFromGroup(WorkspaceGroup_sptr group, + std::size_t index) { + if (group->size() > index) + return boost::dynamic_pointer_cast<MatrixWorkspace>(group->getItem(index)); + return nullptr; +} + +std::vector<std::string> getAxisLabels(TextAxis const *axis) { + std::vector<std::string> labels; + labels.reserve(axis->length()); + for (auto i = 0u; i < axis->length(); ++i) + labels.emplace_back(axis->label(i)); + return labels; +} + +std::vector<std::string> getAxisLabels(MatrixWorkspace_sptr workspace, + std::size_t index) { + auto axis = dynamic_cast<TextAxis *>(workspace->getAxis(index)); + if (axis) + return getAxisLabels(axis); + return std::vector<std::string>(); +} + +void renameResult(Workspace_sptr resultWorkspace, + const std::string &workspaceName) { + AnalysisDataService::Instance().rename(resultWorkspace->getName(), + workspaceName + "_Result"); +} + +void renameResult(Workspace_sptr resultWorkspace, + IndirectFitData const *fitData) { + const auto name = resultWorkspace->getName(); + const auto newName = fitData->displayName("%1%_s%2%_Result", "_to_"); + AnalysisDataService::Instance().rename(name, newName); +} + +void renameResultWithoutSpectra(WorkspaceGroup_sptr resultWorkspace, + const FitDataIterator &fitDataBegin, + const FitDataIterator &fitDataEnd) { + std::size_t index = 0; + MatrixWorkspace const *previous = nullptr; + + for (auto it = fitDataBegin; it < fitDataEnd; ++it) { + auto workspace = (*it)->workspace().get(); + if (workspace != previous) { + renameResult(resultWorkspace->getItem(index++), workspace->getName()); + previous = workspace; + } + } +} + +void renameResultWithSpectra(WorkspaceGroup_sptr resultWorkspace, + const FitDataIterator &fitDataBegin, + const FitDataIterator &fitDataEnd) { + std::size_t index = 0; + for (auto it = fitDataBegin; it < fitDataEnd; ++it) + renameResult(resultWorkspace->getItem(index++), it->get()); +} + +void renameResult(WorkspaceGroup_sptr resultWorkspace, + const FitDataIterator &fitDataBegin, + const FitDataIterator &fitDataEnd) { + if (static_cast<int>(resultWorkspace->size()) >= fitDataEnd - fitDataBegin) + renameResultWithSpectra(resultWorkspace, fitDataBegin, fitDataEnd); + else + renameResultWithoutSpectra(resultWorkspace, fitDataBegin, fitDataEnd); +} + +template <typename Map, typename Key> +typename Map::mapped_type &findOrCreateDefaultInMap(Map &map, const Key &key) { + auto valueIt = map.find(key); + if (valueIt != map.end()) + return valueIt->second; + return map[key] = typename Map::mapped_type(); +} + +struct UnstructuredResultAdder { +public: + UnstructuredResultAdder( + WorkspaceGroup_sptr resultGroup, ResultLocations &locations, + std::unordered_map<std::size_t, std::size_t> &defaultPositions, + std::size_t &index) + : m_resultGroup(resultGroup), m_locations(locations), + m_defaultPositions(defaultPositions), m_index(index) {} + + void operator()(std::size_t spectrum) const { + auto defaultIt = m_defaultPositions.find(spectrum); + if (defaultIt != m_defaultPositions.end()) + m_locations[spectrum] = ResultLocation(m_resultGroup, defaultIt->second); + else if (m_resultGroup->size() > m_index) { + m_locations[spectrum] = ResultLocation(m_resultGroup, m_index); + m_defaultPositions[spectrum] = m_index++; + } + } + +private: + WorkspaceGroup_sptr m_resultGroup; + ResultLocations &m_locations; + std::unordered_map<std::size_t, std::size_t> &m_defaultPositions; + std::size_t &m_index; +}; + +std::size_t numberOfSpectraIn(const FitDataIterator &fitDataBegin, + const FitDataIterator &fitDataEnd) { + std::size_t spectra = 0; + for (auto it = fitDataBegin; it < fitDataEnd; ++it) + spectra += (*it)->numberOfSpectra(); + return spectra; +} } // namespace namespace MantidQt { @@ -100,7 +223,7 @@ namespace IDA { IndirectFitOutput::IndirectFitOutput(WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) : m_resultGroup(resultGroup), m_resultWorkspace(resultWorkspace), @@ -111,7 +234,7 @@ IndirectFitOutput::IndirectFitOutput(WorkspaceGroup_sptr resultGroup, IndirectFitOutput::IndirectFitOutput(WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, IndirectFitData const *fitData, std::size_t spectrum) { m_parameters[fitData] = ParameterValues(); @@ -142,7 +265,15 @@ IndirectFitOutput::getResultLocation(IndirectFitData const *fitData, spectrum); } -MatrixWorkspace_sptr IndirectFitOutput::getLastResultWorkspace() const { +std::vector<std::string> IndirectFitOutput::getResultParameterNames() const { + if (auto resultWorkspace = getLastResultWorkspace()) { + if (auto workspace = getMatrixWorkspaceFromGroup(resultWorkspace, 0)) + return getAxisLabels(workspace, 1); + } + return std::vector<std::string>(); +} + +WorkspaceGroup_sptr IndirectFitOutput::getLastResultWorkspace() const { return m_resultWorkspace.lock(); } @@ -177,11 +308,12 @@ void IndirectFitOutput::mapParameterNames( void IndirectFitOutput::addOutput(WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) { updateParameters(parameterTable, fitDataBegin, fitDataEnd); updateFitResults(resultGroup, fitDataBegin, fitDataEnd); + renameResult(resultWorkspace, fitDataBegin, fitDataEnd); m_resultWorkspace = resultWorkspace; m_resultGroup = resultGroup; } @@ -189,11 +321,12 @@ void IndirectFitOutput::addOutput(WorkspaceGroup_sptr resultGroup, void IndirectFitOutput::addOutput( Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData const *fitData, std::size_t spectrum) { TableRowExtractor extractRowFromTable(parameterTable); m_parameters[fitData][spectrum] = extractRowFromTable(0); m_outputResultLocations[fitData][spectrum] = ResultLocation(resultGroup, 0); + renameResult(resultWorkspace, fitData); m_resultWorkspace = resultWorkspace; m_resultGroup = resultGroup; } @@ -203,6 +336,15 @@ void IndirectFitOutput::removeOutput(IndirectFitData const *fitData) { m_outputResultLocations.erase(fitData); } +void IndirectFitOutput::updateFitResults(WorkspaceGroup_sptr resultGroup, + const FitDataIterator &fitDataBegin, + const FitDataIterator &fitDataEnd) { + if (numberOfSpectraIn(fitDataBegin, fitDataEnd) <= resultGroup->size()) + updateFitResultsFromStructured(resultGroup, fitDataBegin, fitDataEnd); + else + updateFitResultsFromUnstructured(resultGroup, fitDataBegin, fitDataEnd); +} + void IndirectFitOutput::updateParameters(ITableWorkspace_sptr parameterTable, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) { @@ -210,13 +352,30 @@ void IndirectFitOutput::updateParameters(ITableWorkspace_sptr parameterTable, m_parameters); } -void IndirectFitOutput::updateFitResults( +void IndirectFitOutput::updateFitResultsFromUnstructured( + Mantid::API::WorkspaceGroup_sptr resultGroup, + const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) { + std::unordered_map<MatrixWorkspace *, + std::unordered_map<std::size_t, std::size_t>> + resultIndices; + std::size_t index = 0; + + auto update = [&](IndirectFitData const *inputData) { + auto &fitResults = extractOrAddDefault(m_outputResultLocations, inputData); + auto workspace = inputData->workspace().get(); + auto &indices = findOrCreateDefaultInMap(resultIndices, workspace); + return UnstructuredResultAdder(resultGroup, fitResults, indices, index); + }; + applyData(update, fitDataBegin, fitDataEnd); +} + +void IndirectFitOutput::updateFitResultsFromStructured( Mantid::API::WorkspaceGroup_sptr resultGroup, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) { auto update = [&](IndirectFitData const *inputData) { auto &fitResults = extractOrAddDefault(m_outputResultLocations, inputData); return [&](std::size_t index, std::size_t spectrum) { - fitResults[spectrum] = ResultLocation(resultGroup, index++); + fitResults[spectrum] = ResultLocation(resultGroup, index); }; }; applyEnumeratedData(update, fitDataBegin, fitDataEnd); diff --git a/qt/scientific_interfaces/Indirect/IndirectFitOutput.h b/qt/scientific_interfaces/Indirect/IndirectFitOutput.h index 540e11826249992db1ecc53e4022c27953222bdd..5bd553a3717ebdd62867f3c8b97db38d5872a138 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitOutput.h +++ b/qt/scientific_interfaces/Indirect/IndirectFitOutput.h @@ -24,8 +24,7 @@ struct ParameterValue { struct ResultLocation { ResultLocation() : result(), index(0) {} - ResultLocation(boost::weak_ptr<Mantid::API::WorkspaceGroup> group, - std::size_t i) + ResultLocation(Mantid::API::WorkspaceGroup_sptr group, std::size_t i) : result(group), index(i) {} boost::weak_ptr<Mantid::API::WorkspaceGroup> result; std::size_t index; @@ -69,13 +68,13 @@ class IndirectFitOutput { public: IndirectFitOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd); IndirectFitOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData const *fitData, std::size_t spectrum); bool isSpectrumFit(IndirectFitData const *fitData, @@ -86,7 +85,8 @@ public: boost::optional<ResultLocation> getResultLocation(IndirectFitData const *fitData, std::size_t spectrum) const; - Mantid::API::MatrixWorkspace_sptr getLastResultWorkspace() const; + std::vector<std::string> getResultParameterNames() const; + Mantid::API::WorkspaceGroup_sptr getLastResultWorkspace() const; Mantid::API::WorkspaceGroup_sptr getLastResultGroup() const; void mapParameterNames( @@ -101,26 +101,34 @@ public: void addOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd); void addOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData const *fitData, std::size_t spectrum); void removeOutput(IndirectFitData const *fitData); private: - void updateParameters(Mantid::API::ITableWorkspace_sptr parameterTable, + void updateFitResults(Mantid::API::WorkspaceGroup_sptr resultGroup, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd); - void updateFitResults(Mantid::API::WorkspaceGroup_sptr resultGroup, + void updateParameters(Mantid::API::ITableWorkspace_sptr parameterTable, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd); + void + updateFitResultsFromUnstructured(Mantid::API::WorkspaceGroup_sptr resultGroup, + const FitDataIterator &fitDataBegin, + const FitDataIterator &fitDataEnd); + void + updateFitResultsFromStructured(Mantid::API::WorkspaceGroup_sptr resultGroup, + const FitDataIterator &fitDataBegin, + const FitDataIterator &fitDataEnd); boost::weak_ptr<Mantid::API::WorkspaceGroup> m_resultGroup; - boost::weak_ptr<Mantid::API::MatrixWorkspace> m_resultWorkspace; + boost::weak_ptr<Mantid::API::WorkspaceGroup> m_resultWorkspace; std::unordered_map<IndirectFitData const *, ParameterValues> m_parameters; std::unordered_map<IndirectFitData const *, ResultLocations> m_outputResultLocations; diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.cpp b/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31ee5cae9e64c6787250adee42b4c40fd68c0976 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.cpp @@ -0,0 +1,382 @@ +#include "IndirectFitPlotModel.h" + +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/CompositeFunction.h" +#include "MantidAPI/FunctionDomain1D.h" +#include "MantidAPI/TextAxis.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidKernel/make_unique.h" + +namespace { +using namespace Mantid::API; + +// The name of the conjoined input and guess name -- required for +// creating an external guess plot. +const std::string INPUT_AND_GUESS_NAME = "__QENSInputAndGuess"; + +IFunction_sptr firstFunctionWithParameter(IFunction_sptr function, + const std::string &category, + const std::string ¶meterName); + +IFunction_sptr firstFunctionWithParameter(CompositeFunction_sptr composite, + const std::string &category, + const std::string ¶meterName) { + for (auto i = 0u; i < composite->nFunctions(); ++i) { + const auto value = firstFunctionWithParameter(composite->getFunction(i), + category, parameterName); + if (value) + return value; + } + return nullptr; +} + +IFunction_sptr firstFunctionWithParameter(IFunction_sptr function, + const std::string &category, + const std::string ¶meterName) { + if (function->category() == category && function->hasParameter(parameterName)) + return function; + + const auto composite = + boost::dynamic_pointer_cast<CompositeFunction>(function); + if (composite) + return firstFunctionWithParameter(composite, category, parameterName); + return nullptr; +} + +boost::optional<double> firstParameterValue(IFunction_sptr function, + const std::string &category, + const std::string ¶meterName) { + if (!function) + return boost::none; + + const auto functionWithParameter = + firstFunctionWithParameter(function, category, parameterName); + if (functionWithParameter) + return functionWithParameter->getParameter(parameterName); + return boost::none; +} + +boost::optional<double> findFirstPeakCentre(IFunction_sptr function) { + return firstParameterValue(function, "Peak", "PeakCentre"); +} + +boost::optional<double> findFirstFWHM(IFunction_sptr function) { + return firstParameterValue(function, "Peak", "FWHM"); +} + +boost::optional<double> findFirstBackgroundLevel(IFunction_sptr function) { + return firstParameterValue(function, "Background", "A0"); +} + +void setFunctionParameters(IFunction_sptr function, const std::string &category, + const std::string ¶meterName, double value); + +void setFunctionParameters(CompositeFunction_sptr composite, + const std::string &category, + const std::string ¶meterName, double value) { + for (auto i = 0u; i < composite->nFunctions(); ++i) + setFunctionParameters(composite->getFunction(i), category, parameterName, + value); +} + +void setFunctionParameters(IFunction_sptr function, const std::string &category, + const std::string ¶meterName, double value) { + if (function->category() == category && function->hasParameter(parameterName)) + function->setParameter(parameterName, value); + + auto composite = boost::dynamic_pointer_cast<CompositeFunction>(function); + if (composite) + setFunctionParameters(composite, category, parameterName, value); +} + +void setFirstBackground(IFunction_sptr function, double value) { + firstFunctionWithParameter(function, "Background", "A0") + ->setParameter("A0", value); +} +} // namespace + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +using namespace Mantid::API; + +IndirectFitPlotModel::IndirectFitPlotModel(IndirectFittingModel *fittingModel) + : m_fittingModel(fittingModel), m_activeIndex(0), m_activeSpectrum(0) {} + +IndirectFitPlotModel::~IndirectFitPlotModel() { + deleteExternalGuessWorkspace(); +} + +void IndirectFitPlotModel::setActiveIndex(std::size_t index) { + m_activeIndex = index; +} + +void IndirectFitPlotModel::setActiveSpectrum(std::size_t spectrum) { + m_activeSpectrum = spectrum; +} + +void IndirectFitPlotModel::setStartX(double startX) { + m_fittingModel->setStartX(startX, m_activeIndex, m_activeSpectrum); +} + +void IndirectFitPlotModel::setEndX(double endX) { + m_fittingModel->setEndX(endX, m_activeIndex, m_activeSpectrum); +} + +void IndirectFitPlotModel::setFWHM(double fwhm) { + m_fittingModel->setDefaultParameterValue("FWHM", fwhm, m_activeIndex); + setFunctionParameters(m_fittingModel->getFittingFunction(), "Peak", "FWHM", + fwhm); +} + +void IndirectFitPlotModel::setBackground(double background) { + m_fittingModel->setDefaultParameterValue("A0", background, m_activeIndex); + setFirstBackground(m_fittingModel->getFittingFunction(), background); +} + +void IndirectFitPlotModel::deleteExternalGuessWorkspace() { + if (AnalysisDataService::Instance().doesExist(INPUT_AND_GUESS_NAME)) + deleteWorkspace(INPUT_AND_GUESS_NAME); +} + +MatrixWorkspace_sptr IndirectFitPlotModel::getWorkspace() const { + return m_fittingModel->getWorkspace(m_activeIndex); +} + +Spectra IndirectFitPlotModel::getSpectra() const { + return m_fittingModel->getSpectra(m_activeIndex); +} + +std::pair<double, double> IndirectFitPlotModel::getRange() const { + return m_fittingModel->getFittingRange(m_activeIndex, m_activeSpectrum); +} + +std::pair<double, double> IndirectFitPlotModel::getWorkspaceRange() const { + const auto xValues = getWorkspace()->x(0); + return {xValues.front(), xValues.back()}; +} + +std::pair<double, double> IndirectFitPlotModel::getResultRange() const { + const auto xValues = getResultWorkspace()->x(0); + return {xValues.front(), xValues.back()}; +} + +std::size_t IndirectFitPlotModel::getActiveDataIndex() const { + return m_activeIndex; +} + +std::size_t IndirectFitPlotModel::getActiveSpectrum() const { + return m_activeSpectrum; +} + +std::size_t IndirectFitPlotModel::numberOfWorkspaces() const { + return m_fittingModel->numberOfWorkspaces(); +} + +std::string IndirectFitPlotModel::getFitDataName(std::size_t index) const { + return m_fittingModel->createDisplayName("%1% (%2%)", "-", index); +} + +std::string IndirectFitPlotModel::getFitDataName() const { + return getFitDataName(m_activeIndex); +} + +std::string IndirectFitPlotModel::getLastFitDataName() const { + return getFitDataName(m_fittingModel->numberOfWorkspaces() - 1); +} + +boost::optional<double> IndirectFitPlotModel::getFirstHWHM() const { + auto fwhm = findFirstFWHM(m_fittingModel->getFittingFunction()); + if (fwhm) + return *fwhm / 2.0; + return boost::none; +} + +boost::optional<double> IndirectFitPlotModel::getFirstPeakCentre() const { + return findFirstPeakCentre(m_fittingModel->getFittingFunction()); +} + +boost::optional<double> IndirectFitPlotModel::getFirstBackgroundLevel() const { + return findFirstBackgroundLevel(m_fittingModel->getFittingFunction()); +} + +double IndirectFitPlotModel::calculateHWHMMaximum(double minimum) const { + const auto peakCentre = getFirstPeakCentre().get_value_or(0.); + return peakCentre + (peakCentre - minimum); +} + +double IndirectFitPlotModel::calculateHWHMMinimum(double maximum) const { + const auto peakCentre = getFirstPeakCentre().get_value_or(0.); + return peakCentre - (maximum - peakCentre); +} + +bool IndirectFitPlotModel::canCalculateGuess() const { + const auto function = m_fittingModel->getFittingFunction(); + if (!function) + return false; + + const auto composite = + boost::dynamic_pointer_cast<CompositeFunction>(function); + const auto isEmptyModel = composite && composite->nFunctions() == 0; + return getWorkspace() && !isEmptyModel; +} + +MatrixWorkspace_sptr IndirectFitPlotModel::getResultWorkspace() const { + const auto location = + m_fittingModel->getResultLocation(m_activeIndex, m_activeSpectrum); + + if (location) { + const auto group = location->result.lock(); + return boost::dynamic_pointer_cast<MatrixWorkspace>( + group->getItem(location->index)); + } + return nullptr; +} + +MatrixWorkspace_sptr IndirectFitPlotModel::getGuessWorkspace() const { + const auto range = getRange(); + return createGuessWorkspace( + getWorkspace(), m_fittingModel->getFittingFunction(), + boost::numeric_cast<int>(m_activeSpectrum), range.first, range.second); +} + +MatrixWorkspace_sptr IndirectFitPlotModel::appendGuessToInput( + MatrixWorkspace_sptr guessWorkspace) const { + const auto range = getRange(); + return createInputAndGuessWorkspace( + getWorkspace(), guessWorkspace, + boost::numeric_cast<int>(m_activeSpectrum), range.first, range.second); +} + +MatrixWorkspace_sptr IndirectFitPlotModel::createInputAndGuessWorkspace( + MatrixWorkspace_sptr inputWS, MatrixWorkspace_sptr guessWorkspace, + int spectrum, double startX, double endX) const { + guessWorkspace->setInstrument(inputWS->getInstrument()); + guessWorkspace->replaceAxis(0, + inputWS->getAxis(0)->clone(guessWorkspace.get())); + guessWorkspace->setDistribution(inputWS->isDistribution()); + + auto extracted = extractSpectra(inputWS, spectrum, spectrum, startX, endX); + auto inputAndGuess = appendSpectra(extracted, guessWorkspace); + AnalysisDataService::Instance().addOrReplace(INPUT_AND_GUESS_NAME, + inputAndGuess); + + auto axis = Mantid::Kernel::make_unique<TextAxis>(2); + axis->setLabel(0, "Sample"); + axis->setLabel(1, "Guess"); + inputAndGuess->replaceAxis(1, axis.release()); + return inputAndGuess; +} + +MatrixWorkspace_sptr IndirectFitPlotModel::createGuessWorkspace( + MatrixWorkspace_sptr inputWorkspace, IFunction_const_sptr func, + int workspaceIndex, double startX, double endX) const { + auto croppedWS = cropWorkspace(inputWorkspace, startX, endX, workspaceIndex, + workspaceIndex); + const auto dataY = computeOutput(func, croppedWS->points(0).rawData()); + + if (dataY.empty()) + return WorkspaceFactory::Instance().create("Workspace2D", 1, 1, 1); + + auto createWs = createWorkspaceAlgorithm(1, croppedWS->dataX(0), dataY); + createWs->execute(); + return createWs->getProperty("OutputWorkspace"); +} + +std::vector<double> +IndirectFitPlotModel::computeOutput(IFunction_const_sptr func, + const std::vector<double> &dataX) const { + if (dataX.empty()) + return std::vector<double>(); + + FunctionDomain1DVector domain(dataX); + FunctionValues outputData(domain); + func->function(domain, outputData); + + std::vector<double> dataY(dataX.size()); + for (auto i = 0u; i < dataX.size(); ++i) + dataY[i] = outputData.getCalculated(i); + return dataY; +} + +IAlgorithm_sptr IndirectFitPlotModel::createWorkspaceAlgorithm( + std::size_t numberOfSpectra, const std::vector<double> &dataX, + const std::vector<double> &dataY) const { + IAlgorithm_sptr createWsAlg = + AlgorithmManager::Instance().create("CreateWorkspace"); + createWsAlg->initialize(); + createWsAlg->setChild(true); + createWsAlg->setLogging(false); + createWsAlg->setProperty("OutputWorkspace", "__QENSGuess"); + createWsAlg->setProperty("NSpec", boost::numeric_cast<int>(numberOfSpectra)); + createWsAlg->setProperty("DataX", dataX); + createWsAlg->setProperty("DataY", dataY); + return createWsAlg; +} + +MatrixWorkspace_sptr +IndirectFitPlotModel::extractSpectra(MatrixWorkspace_sptr inputWS, + int startIndex, int endIndex, + double startX, double endX) const { + auto extractSpectraAlg = + AlgorithmManager::Instance().create("ExtractSpectra"); + extractSpectraAlg->initialize(); + extractSpectraAlg->setChild(true); + extractSpectraAlg->setLogging(false); + extractSpectraAlg->setProperty("InputWorkspace", inputWS); + extractSpectraAlg->setProperty("StartWorkspaceIndex", startIndex); + extractSpectraAlg->setProperty("XMin", startX); + extractSpectraAlg->setProperty("XMax", endX); + extractSpectraAlg->setProperty("EndWorkspaceIndex", endIndex); + extractSpectraAlg->setProperty("OutputWorkspace", "__extracted"); + extractSpectraAlg->execute(); + return extractSpectraAlg->getProperty("OutputWorkspace"); +} + +MatrixWorkspace_sptr +IndirectFitPlotModel::appendSpectra(MatrixWorkspace_sptr inputWS, + MatrixWorkspace_sptr spectraWS) const { + auto appendSpectraAlg = AlgorithmManager::Instance().create("AppendSpectra"); + appendSpectraAlg->initialize(); + appendSpectraAlg->setChild(true); + appendSpectraAlg->setLogging(false); + appendSpectraAlg->setProperty("InputWorkspace1", inputWS); + appendSpectraAlg->setProperty("InputWorkspace2", spectraWS); + appendSpectraAlg->setProperty("OutputWorkspace", "__appended"); + appendSpectraAlg->execute(); + return appendSpectraAlg->getProperty("OutputWorkspace"); +} + +MatrixWorkspace_sptr +IndirectFitPlotModel::cropWorkspace(MatrixWorkspace_sptr inputWS, double startX, + double endX, int startIndex, + int endIndex) const { + const auto cropAlg = AlgorithmManager::Instance().create("CropWorkspace"); + cropAlg->initialize(); + cropAlg->setChild(true); + cropAlg->setLogging(false); + cropAlg->setProperty("InputWorkspace", inputWS); + cropAlg->setProperty("XMin", startX); + cropAlg->setProperty("XMax", endX); + cropAlg->setProperty("StartWorkspaceIndex", startIndex); + cropAlg->setProperty("EndWorkspaceIndex", endIndex); + cropAlg->setProperty("OutputWorkspace", "__cropped"); + cropAlg->execute(); + return cropAlg->getProperty("OutputWorkspace"); +} + +void IndirectFitPlotModel::deleteWorkspace(const std::string &name) const { + auto deleteWorkspaceAlg = + AlgorithmManager::Instance().create("DeleteWorkspace"); + deleteWorkspaceAlg->initialize(); + deleteWorkspaceAlg->setChild(true); + deleteWorkspaceAlg->setLogging(false); + deleteWorkspaceAlg->setProperty("Workspace", name); + deleteWorkspaceAlg->execute(); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.h b/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.h new file mode 100644 index 0000000000000000000000000000000000000000..032110435d6e6cf568cacf811f7789b7cd6555bd --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotModel.h @@ -0,0 +1,97 @@ +#ifndef MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITPLOTMODEL_H_ +#define MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITPLOTMODEL_H_ + +#include "IndirectFittingModel.h" + +#include "MantidAPI/IAlgorithm.h" +#include "MantidAPI/IFunction.h" +#include "MantidAPI/MatrixWorkspace.h" + +#include <boost/optional.hpp> +#include <boost/weak_ptr.hpp> + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class DLLExport IndirectFitPlotModel { +public: + IndirectFitPlotModel(IndirectFittingModel *fittingModel); + ~IndirectFitPlotModel(); + + Mantid::API::MatrixWorkspace_sptr getWorkspace() const; + Mantid::API::MatrixWorkspace_sptr getResultWorkspace() const; + Mantid::API::MatrixWorkspace_sptr getGuessWorkspace() const; + Spectra getSpectra() const; + + Mantid::API::MatrixWorkspace_sptr + appendGuessToInput(Mantid::API::MatrixWorkspace_sptr guessWorkspace) const; + + std::size_t getActiveDataIndex() const; + std::size_t getActiveSpectrum() const; + std::size_t numberOfWorkspaces() const; + std::string getFitDataName(std::size_t index) const; + std::string getFitDataName() const; + std::string getLastFitDataName() const; + std::pair<double, double> getRange() const; + std::pair<double, double> getWorkspaceRange() const; + std::pair<double, double> getResultRange() const; + boost::optional<double> getFirstHWHM() const; + boost::optional<double> getFirstPeakCentre() const; + boost::optional<double> getFirstBackgroundLevel() const; + double calculateHWHMMaximum(double minimum) const; + double calculateHWHMMinimum(double maximum) const; + bool canCalculateGuess() const; + + void setActiveIndex(std::size_t index); + void setActiveSpectrum(std::size_t spectrum); + void setStartX(double startX); + void setEndX(double endX); + void setFWHM(double fwhm); + void setBackground(double background); + + void deleteExternalGuessWorkspace(); + +private: + Mantid::API::MatrixWorkspace_sptr + createInputAndGuessWorkspace(Mantid::API::MatrixWorkspace_sptr inputWS, + Mantid::API::MatrixWorkspace_sptr guessWorkspace, + int spectrum, double startX, double endX) const; + + Mantid::API::MatrixWorkspace_sptr + createGuessWorkspace(Mantid::API::MatrixWorkspace_sptr inputWorkspace, + Mantid::API::IFunction_const_sptr func, + int workspaceIndex, double startX, double endX) const; + + std::vector<double> computeOutput(Mantid::API::IFunction_const_sptr func, + const std::vector<double> &dataX) const; + + Mantid::API::IAlgorithm_sptr + createWorkspaceAlgorithm(std::size_t numberOfSpectra, + const std::vector<double> &dataX, + const std::vector<double> &dataY) const; + + Mantid::API::MatrixWorkspace_sptr + extractSpectra(Mantid::API::MatrixWorkspace_sptr inputWS, int startIndex, + int endIndex, double startX, double endX) const; + + Mantid::API::MatrixWorkspace_sptr + appendSpectra(Mantid::API::MatrixWorkspace_sptr inputWS, + Mantid::API::MatrixWorkspace_sptr spectraWS) const; + + Mantid::API::MatrixWorkspace_sptr + cropWorkspace(Mantid::API::MatrixWorkspace_sptr inputWS, double startX, + double endX, int startIndex, int endIndex) const; + + void deleteWorkspace(const std::string &name) const; + + IndirectFittingModel *m_fittingModel; + std::size_t m_activeIndex; + std::size_t m_activeSpectrum; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITPLOTMODEL_H_ */ diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.cpp b/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d10413e35d212100dd440d623741e06bd09dede --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.cpp @@ -0,0 +1,406 @@ +#include "IndirectFitPlotPresenter.h" + +#include "MantidQtWidgets/Common/SignalBlocker.h" + +namespace { +using MantidQt::CustomInterfaces::IDA::IndirectFitPlotView; +using MantidQt::CustomInterfaces::IDA::DiscontinuousSpectra; + +std::string createPlotString(const std::string &workspaceName, + const std::string &spectra) { + std::string plotString = "from mantidplot import plotSpectrum\n"; + return plotString + "plotSpectrum(['" + workspaceName + "'], " + spectra + + ")\n"; +} + +std::string createPlotString(const std::string &workspaceName, + std::size_t spectrum) { + return createPlotString(workspaceName, std::to_string(spectrum)); +} + +struct UpdateAvailableSpectra : public boost::static_visitor<> { +public: + explicit UpdateAvailableSpectra(IndirectFitPlotView *view) : m_view(view) {} + + void operator()(const std::pair<std::size_t, std::size_t> &spectra) { + m_view->setAvailableSpectra(spectra.first, spectra.second); + } + + void operator()(const DiscontinuousSpectra<std::size_t> &spectra) { + m_view->setAvailableSpectra(spectra.begin(), spectra.end()); + } + +private: + IndirectFitPlotView *m_view; +}; +} // namespace + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +using namespace Mantid::API; + +IndirectFitPlotPresenter::IndirectFitPlotPresenter(IndirectFittingModel *model, + IndirectFitPlotView *view) + : m_model(new IndirectFitPlotModel(model)), m_view(view), + m_plotGuessInSeparateWindow(false) { + connect(m_view, SIGNAL(selectedFitDataChanged(std::size_t)), this, + SLOT(setActiveIndex(std::size_t))); + connect(m_view, SIGNAL(selectedFitDataChanged(std::size_t)), this, + SLOT(updateAvailableSpectra())); + connect(m_view, SIGNAL(selectedFitDataChanged(std::size_t)), this, + SLOT(updatePlots())); + connect(m_view, SIGNAL(selectedFitDataChanged(std::size_t)), this, + SLOT(updateFitRangeSelector())); + connect(m_view, SIGNAL(selectedFitDataChanged(std::size_t)), this, + SLOT(updateGuess())); + connect(m_view, SIGNAL(selectedFitDataChanged(std::size_t)), this, + SIGNAL(selectedFitDataChanged(std::size_t))); + + connect(m_view, SIGNAL(plotSpectrumChanged(std::size_t)), this, + SLOT(setActiveSpectrum(std::size_t))); + connect(m_view, SIGNAL(plotSpectrumChanged(std::size_t)), this, + SLOT(updatePlots())); + connect(m_view, SIGNAL(plotSpectrumChanged(std::size_t)), this, + SLOT(updateFitRangeSelector())); + connect(m_view, SIGNAL(plotSpectrumChanged(std::size_t)), this, + SIGNAL(plotSpectrumChanged(std::size_t))); + + connect(m_view, SIGNAL(plotCurrentPreview()), this, + SLOT(plotCurrentPreview())); + + connect(m_view, SIGNAL(fitSelectedSpectrum()), this, + SLOT(emitFitSingleSpectrum())); + + connect(m_view, SIGNAL(plotGuessChanged(bool)), this, + SLOT(updateGuess(bool))); + + connect(m_view, SIGNAL(startXChanged(double)), this, + SLOT(setModelStartX(double))); + connect(m_view, SIGNAL(endXChanged(double)), this, + SLOT(setModelEndX(double))); + + connect(m_view, SIGNAL(startXChanged(double)), this, + SIGNAL(startXChanged(double))); + connect(m_view, SIGNAL(endXChanged(double)), this, + SIGNAL(endXChanged(double))); + + connect(m_view, SIGNAL(hwhmMaximumChanged(double)), this, + SLOT(setHWHMMinimum(double))); + connect(m_view, SIGNAL(hwhmMinimumChanged(double)), this, + SLOT(setHWHMMaximum(double))); + connect(m_view, SIGNAL(hwhmChanged(double, double)), this, + SLOT(setModelHWHM(double, double))); + connect(m_view, SIGNAL(hwhmChanged(double, double)), this, + SLOT(emitFWHMChanged(double, double))); + + connect(m_view, SIGNAL(backgroundChanged(double)), this, + SLOT(setModelBackground(double))); + connect(m_view, SIGNAL(backgroundChanged(double)), this, + SIGNAL(backgroundChanged(double))); + + connect(&m_pythonRunner, SIGNAL(runAsPythonScript(const QString &, bool)), + this, SIGNAL(runAsPythonScript(const QString &, bool))); + + updateRangeSelectors(); + updateAvailableSpectra(); +} + +std::size_t IndirectFitPlotPresenter::getSelectedDataIndex() const { + return m_model->getActiveDataIndex(); +} + +std::size_t IndirectFitPlotPresenter::getSelectedSpectrum() const { + return m_model->getActiveSpectrum(); +} + +int IndirectFitPlotPresenter::getSelectedSpectrumIndex() const { + return m_view->getSelectedSpectrumIndex(); +} + +bool IndirectFitPlotPresenter::isCurrentlySelected(std::size_t dataIndex, + std::size_t spectrum) const { + return getSelectedDataIndex() == dataIndex && + getSelectedSpectrum() == spectrum; +} + +void IndirectFitPlotPresenter::setActiveIndex(std::size_t index) { + m_model->setActiveIndex(index); +} + +void IndirectFitPlotPresenter::setActiveSpectrum(std::size_t spectrum) { + m_model->setActiveSpectrum(spectrum); +} + +void IndirectFitPlotPresenter::setModelStartX(double startX) { + m_model->setStartX(startX); +} + +void IndirectFitPlotPresenter::setModelEndX(double endX) { + m_model->setEndX(endX); +} + +void IndirectFitPlotPresenter::setModelHWHM(double minimum, double maximum) { + m_model->setFWHM(maximum - minimum); +} + +void IndirectFitPlotPresenter::setModelBackground(double background) { + m_model->setBackground(background); +} + +void IndirectFitPlotPresenter::hideMultipleDataSelection() { + m_view->hideMultipleDataSelection(); +} + +void IndirectFitPlotPresenter::showMultipleDataSelection() { + m_view->showMultipleDataSelection(); +} + +void IndirectFitPlotPresenter::setStartX(double startX) { + m_view->setFitRangeMinimum(startX); +} + +void IndirectFitPlotPresenter::setEndX(double endX) { + m_view->setFitRangeMaximum(endX); +} + +void IndirectFitPlotPresenter::updateRangeSelectors() { + updateBackgroundSelector(); + updateHWHMSelector(); +} + +void IndirectFitPlotPresenter::setHWHMMaximum(double minimum) { + m_view->setHWHMMaximum(m_model->calculateHWHMMaximum(minimum)); +} + +void IndirectFitPlotPresenter::setHWHMMinimum(double maximum) { + m_view->setHWHMMinimum(m_model->calculateHWHMMinimum(maximum)); +} + +void IndirectFitPlotPresenter::enablePlotGuessInSeparateWindow() { + m_plotGuessInSeparateWindow = true; + const auto inputAndGuess = + m_model->appendGuessToInput(m_model->getGuessWorkspace()); + const auto plotString = createPlotString(inputAndGuess->getName(), "[0,1]"); + m_pythonRunner.runPythonCode(QString::fromStdString(plotString)); +} + +void IndirectFitPlotPresenter::disablePlotGuessInSeparateWindow() { + m_plotGuessInSeparateWindow = false; + m_model->deleteExternalGuessWorkspace(); +} + +void IndirectFitPlotPresenter::appendLastDataToSelection() { + const auto numberOfWorkspaces = m_model->numberOfWorkspaces(); + if (m_view->dataSelectionSize() == numberOfWorkspaces) + m_view->setNameInDataSelection(m_model->getLastFitDataName(), + numberOfWorkspaces - 1); + else + m_view->appendToDataSelection(m_model->getLastFitDataName()); +} + +void IndirectFitPlotPresenter::updateSelectedDataName() { + m_view->setNameInDataSelection(m_model->getFitDataName(), + m_model->getActiveDataIndex()); +} + +void IndirectFitPlotPresenter::updateDataSelection() { + MantidQt::API::SignalBlocker<QObject> blocker(m_view); + m_view->clearDataSelection(); + for (auto i = 0u; i < m_model->numberOfWorkspaces(); ++i) + m_view->appendToDataSelection(m_model->getFitDataName(i)); + setActiveIndex(0); + updateAvailableSpectra(); + emitSelectedFitDataChanged(); +} + +void IndirectFitPlotPresenter::updateAvailableSpectra() { + if (m_model->getWorkspace()) { + enableAllDataSelection(); + auto updateSpectra = UpdateAvailableSpectra(m_view); + m_model->getSpectra().apply_visitor(updateSpectra); + setActiveSpectrum(m_view->getSelectedSpectrum()); + } else + disableAllDataSelection(); +} + +void IndirectFitPlotPresenter::disableAllDataSelection() { + m_view->disableSpectrumSelection(); + m_view->disableFitRangeSelection(); +} + +void IndirectFitPlotPresenter::enableAllDataSelection() { + m_view->enableSpectrumSelection(); + m_view->enableFitRangeSelection(); +} + +void IndirectFitPlotPresenter::updatePlots() { + const auto result = m_model->getResultWorkspace(); + if (result) + plotResult(result); + else + plotInput(); + updateRangeSelectors(); + updateFitRangeSelector(); +} + +void IndirectFitPlotPresenter::plotInput() { + const auto workspace = m_model->getWorkspace(); + if (workspace) { + clearFit(); + clearDifference(); + plotInput(workspace, m_model->getActiveSpectrum()); + updatePlotRange(m_model->getWorkspaceRange()); + } else + m_view->clear(); +} + +void IndirectFitPlotPresenter::plotResult(MatrixWorkspace_sptr result) { + plotInput(result, 0); + plotFit(result, 1); + plotDifference(result, 2); + updatePlotRange(m_model->getResultRange()); +} + +void IndirectFitPlotPresenter::updatePlotRange( + const std::pair<double, double> &range) { + MantidQt::API::SignalBlocker<QObject> blocker(m_view); + m_view->setFitRange(range.first, range.second); + m_view->setHWHMRange(range.first, range.second); +} + +void IndirectFitPlotPresenter::plotInput(MatrixWorkspace_sptr workspace, + std::size_t spectrum) { + m_view->plotInTopPreview("Sample", workspace, spectrum, Qt::black); +} + +void IndirectFitPlotPresenter::plotFit(MatrixWorkspace_sptr workspace, + std::size_t spectrum) { + m_view->plotInTopPreview("Fit", workspace, spectrum, Qt::red); +} + +void IndirectFitPlotPresenter::plotDifference(MatrixWorkspace_sptr workspace, + std::size_t spectrum) { + m_view->plotInBottomPreview("Difference", workspace, spectrum, Qt::blue); +} + +void IndirectFitPlotPresenter::clearInput() { + m_view->removeFromTopPreview("Sample"); +} + +void IndirectFitPlotPresenter::clearFit() { + m_view->removeFromTopPreview("Fit"); +} + +void IndirectFitPlotPresenter::clearDifference() { + m_view->removeFromBottomPreview("Difference"); +} + +void IndirectFitPlotPresenter::updateFitRangeSelector() { + const auto range = m_model->getRange(); + m_view->setFitRangeMinimum(range.first); + m_view->setFitRangeMaximum(range.second); +} + +void IndirectFitPlotPresenter::plotCurrentPreview() { + const auto plotString = getPlotString(m_model->getActiveSpectrum()); + m_pythonRunner.runPythonCode(QString::fromStdString(plotString)); +} + +void IndirectFitPlotPresenter::updateGuess() { + if (m_model->canCalculateGuess()) { + m_view->enablePlotGuess(); + updateGuess(m_view->isPlotGuessChecked()); + } else { + m_view->disablePlotGuess(); + clearGuess(); + } +} + +void IndirectFitPlotPresenter::updateGuessAvailability() { + if (m_model->canCalculateGuess()) + m_view->enablePlotGuess(); + else + m_view->disablePlotGuess(); +} + +void IndirectFitPlotPresenter::updateGuess(bool doPlotGuess) { + if (doPlotGuess) { + const auto guessWorkspace = m_model->getGuessWorkspace(); + if (guessWorkspace->x(0).size() >= 2) { + plotGuess(guessWorkspace); + if (m_plotGuessInSeparateWindow) + plotGuessInSeparateWindow(guessWorkspace); + } + } else if (m_plotGuessInSeparateWindow) + plotGuessInSeparateWindow(m_model->getGuessWorkspace()); + else + clearGuess(); +} + +void IndirectFitPlotPresenter::plotGuess( + Mantid::API::MatrixWorkspace_sptr workspace) { + m_view->plotInTopPreview("Guess", workspace, 0, Qt::green); +} + +void IndirectFitPlotPresenter::plotGuessInSeparateWindow( + Mantid::API::MatrixWorkspace_sptr workspace) { + m_plotExternalGuessRunner.addCallback( + [this, workspace]() { m_model->appendGuessToInput(workspace); }); +} + +void IndirectFitPlotPresenter::clearGuess() { + m_view->removeFromTopPreview("Guess"); +} + +void IndirectFitPlotPresenter::updateHWHMSelector() { + const auto hwhm = m_model->getFirstHWHM(); + m_view->setHWHMRangeVisible(hwhm ? true : false); + + if (hwhm) + setHWHM(*hwhm); +} + +void IndirectFitPlotPresenter::setHWHM(double hwhm) { + const auto centre = m_model->getFirstPeakCentre().get_value_or(0.); + m_view->setHWHMMaximum(centre + hwhm); + m_view->setHWHMMinimum(centre - hwhm); +} + +void IndirectFitPlotPresenter::updateBackgroundSelector() { + const auto background = m_model->getFirstBackgroundLevel(); + m_view->setBackgroundRangeVisible(background ? true : false); + + if (background) + m_view->setBackgroundLevel(*background); +} + +std::string +IndirectFitPlotPresenter::getPlotString(std::size_t spectrum) const { + const auto result = m_model->getResultWorkspace(); + if (result) + return createPlotString(result->getName(), "[0,1,2]"); + return createPlotString(m_model->getWorkspace()->getName(), spectrum); +} + +void IndirectFitPlotPresenter::emitFitSingleSpectrum() { + emit fitSingleSpectrum(m_model->getActiveDataIndex(), + m_model->getActiveSpectrum()); +} + +void IndirectFitPlotPresenter::emitFWHMChanged(double minimum, double maximum) { + emit fwhmChanged(maximum - minimum); +} + +void IndirectFitPlotPresenter::emitSelectedFitDataChanged() { + const auto index = m_view->getSelectedDataIndex(); + if (index >= 0) + emit selectedFitDataChanged(static_cast<std::size_t>(index)); + else + emit noFitDataSelected(); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.h b/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.h new file mode 100644 index 0000000000000000000000000000000000000000..86f047ca5181a70ae0d49dee774dccf239a9f4d7 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotPresenter.h @@ -0,0 +1,103 @@ +#ifndef MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITPLOTPRESENTER_H_ +#define MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITPLOTPRESENTER_H_ + +#include "IndirectFitPlotModel.h" + +#include "IndirectFitPlotView.h" +#include "LazyAsyncRunner.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class DLLExport IndirectFitPlotPresenter : public QObject { + Q_OBJECT +public: + IndirectFitPlotPresenter(IndirectFittingModel *model, + IndirectFitPlotView *view); + + std::size_t getSelectedDataIndex() const; + std::size_t getSelectedSpectrum() const; + int getSelectedSpectrumIndex() const; + bool isCurrentlySelected(std::size_t dataIndex, std::size_t spectrum) const; + +public slots: + void setStartX(double); + void setEndX(double); + void hideMultipleDataSelection(); + void showMultipleDataSelection(); + void updateRangeSelectors(); + void appendLastDataToSelection(); + void updateSelectedDataName(); + void updateDataSelection(); + void updateAvailableSpectra(); + void updatePlots(); + void updateGuess(); + void updateGuessAvailability(); + void enablePlotGuessInSeparateWindow(); + void disablePlotGuessInSeparateWindow(); + +signals: + void selectedFitDataChanged(std::size_t); + void noFitDataSelected(); + void plotSpectrumChanged(std::size_t); + void fitSingleSpectrum(std::size_t, std::size_t); + void startXChanged(double); + void endXChanged(double); + void fwhmChanged(double); + void backgroundChanged(double); + void runAsPythonScript(const QString &code, bool noOutput = false); + +private slots: + void setModelStartX(double value); + void setModelEndX(double value); + void setModelHWHM(double minimum, double maximum); + void setModelBackground(double background); + void setActiveIndex(std::size_t index); + void setActiveSpectrum(std::size_t spectrum); + void setHWHMMaximum(double minimum); + void setHWHMMinimum(double maximum); + void updateGuess(bool doPlotGuess); + void updateFitRangeSelector(); + void plotCurrentPreview(); + void emitFitSingleSpectrum(); + void emitFWHMChanged(double minimum, double maximum); + +private: + void disableAllDataSelection(); + void enableAllDataSelection(); + void plotInput(Mantid::API::MatrixWorkspace_sptr workspace, + std::size_t spectrum); + void plotFit(Mantid::API::MatrixWorkspace_sptr workspace, + std::size_t spectrum); + void plotDifference(Mantid::API::MatrixWorkspace_sptr workspace, + std::size_t spectrum); + void clearInput(); + void clearFit(); + void clearDifference(); + void plotGuess(Mantid::API::MatrixWorkspace_sptr workspace); + void plotGuessInSeparateWindow(Mantid::API::MatrixWorkspace_sptr workspace); + void plotInput(); + void plotResult(Mantid::API::MatrixWorkspace_sptr workspace); + void updatePlotRange(const std::pair<double, double> &range); + void clearGuess(); + void updateHWHMSelector(); + void setHWHM(double value); + void updateBackgroundSelector(); + void emitSelectedFitDataChanged(); + + std::string getPlotString(std::size_t spectrum) const; + + std::unique_ptr<IndirectFitPlotModel> m_model; + IndirectFitPlotView *m_view; + + bool m_plotGuessInSeparateWindow; + MantidQt::API::PythonRunner m_pythonRunner; + QtLazyAsyncRunner<std::function<void()>> m_plotExternalGuessRunner; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotView.cpp b/qt/scientific_interfaces/Indirect/IndirectFitPlotView.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a48650acc698f985093d903f971df1241aa1a926 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotView.cpp @@ -0,0 +1,270 @@ +#include "IndirectFitPlotView.h" + +#include "MantidQtWidgets/Common/SignalBlocker.h" + +#include <boost/numeric/conversion/cast.hpp> + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +IndirectFitPlotView::IndirectFitPlotView(QWidget *parent) + : API::MantidWidget(parent), m_plotForm(new Ui::IndirectFitPreviewPlot) { + m_plotForm->setupUi(this); + + connect(m_plotForm->cbDataSelection, SIGNAL(currentIndexChanged(int)), this, + SLOT(emitSelectedFitDataChanged(int))); + connect(m_plotForm->spPlotSpectrum, SIGNAL(valueChanged(int)), this, + SLOT(emitPlotSpectrumChanged(int))); + connect(m_plotForm->cbPlotSpectrum, + SIGNAL(currentIndexChanged(const QString &)), this, + SLOT(emitPlotSpectrumChanged(const QString &))); + connect(m_plotForm->ckPlotGuess, SIGNAL(stateChanged(int)), this, + SLOT(emitPlotGuessChanged(int))); + connect(m_plotForm->pbPlotPreview, SIGNAL(clicked()), this, + SIGNAL(plotCurrentPreview())); + connect(m_plotForm->pbFitSingle, SIGNAL(clicked()), this, + SIGNAL(fitSelectedSpectrum())); + + m_plotForm->cbDataSelection->hide(); + addFitRangeSelector(); + addBackgroundRangeSelector(); + addHWHMRangeSelector(); +} + +IndirectFitPlotView::~IndirectFitPlotView() {} + +std::size_t IndirectFitPlotView::getSelectedSpectrum() const { + if (m_plotForm->swPlotSpectrum->currentIndex() == 0) + return m_plotForm->spPlotSpectrum->value(); + return m_plotForm->cbPlotSpectrum->currentIndex(); +} + +int IndirectFitPlotView::getSelectedSpectrumIndex() const { + if (m_plotForm->swPlotSpectrum->currentIndex() == 0) + return m_plotForm->spPlotSpectrum->value() - + m_plotForm->spPlotSpectrum->minimum(); + return m_plotForm->cbPlotSpectrum->currentIndex(); +} + +int IndirectFitPlotView::getSelectedDataIndex() const { + return m_plotForm->cbDataSelection->currentIndex(); +} + +std::size_t IndirectFitPlotView::dataSelectionSize() const { + return boost::numeric_cast<std::size_t>(m_plotForm->cbDataSelection->count()); +} + +bool IndirectFitPlotView::isPlotGuessChecked() const { + return m_plotForm->ckPlotGuess->isChecked(); +} + +void IndirectFitPlotView::hideMultipleDataSelection() { + m_plotForm->cbDataSelection->hide(); +} + +void IndirectFitPlotView::showMultipleDataSelection() { + m_plotForm->cbDataSelection->show(); +} + +void IndirectFitPlotView::setAvailableSpectra(std::size_t minimum, + std::size_t maximum) { + m_plotForm->swPlotSpectrum->setCurrentIndex(0); + m_plotForm->spPlotSpectrum->setMinimum(boost::numeric_cast<int>(minimum)); + m_plotForm->spPlotSpectrum->setMaximum(boost::numeric_cast<int>(maximum)); +} + +void IndirectFitPlotView::setAvailableSpectra( + const std::vector<std::size_t>::const_iterator &from, + const std::vector<std::size_t>::const_iterator &to) { + m_plotForm->swPlotSpectrum->setCurrentIndex(1); + m_plotForm->cbPlotSpectrum->clear(); + + for (auto spectrum = from; spectrum < to; ++spectrum) + m_plotForm->cbPlotSpectrum->addItem(QString::number(*spectrum)); +} + +void IndirectFitPlotView::setMinimumSpectrum(int minimum) { + m_plotForm->spPlotSpectrum->setMinimum(minimum); +} + +void IndirectFitPlotView::setMaximumSpectrum(int maximum) { + m_plotForm->spPlotSpectrum->setMaximum(maximum); +} + +void IndirectFitPlotView::setBackgroundLevel(double value) { + auto selector = m_plotForm->ppPlotTop->getRangeSelector("Background"); + MantidQt::API::SignalBlocker<QObject> blocker(selector); + selector->setMinimum(value); +} + +void IndirectFitPlotView::setFitRange(double minimum, double maximum) { + auto selector = m_plotForm->ppPlotTop->getRangeSelector("FitRange"); + MantidQt::API::SignalBlocker<QObject> blocker(selector); + selector->setRange(minimum, maximum); +} + +void IndirectFitPlotView::setFitRangeMinimum(double minimum) { + auto selector = m_plotForm->ppPlotTop->getRangeSelector("FitRange"); + MantidQt::API::SignalBlocker<QObject> blocker(selector); + selector->setMinimum(minimum); +} + +void IndirectFitPlotView::setFitRangeMaximum(double maximum) { + auto selector = m_plotForm->ppPlotTop->getRangeSelector("FitRange"); + MantidQt::API::SignalBlocker<QObject> blocker(selector); + selector->setMaximum(maximum); +} + +void IndirectFitPlotView::appendToDataSelection(const std::string &dataName) { + MantidQt::API::SignalBlocker<QObject> blocker(m_plotForm->cbDataSelection); + m_plotForm->cbDataSelection->addItem(QString::fromStdString(dataName)); +} + +void IndirectFitPlotView::setNameInDataSelection(const std::string &dataName, + std::size_t index) { + m_plotForm->cbDataSelection->setItemText(boost::numeric_cast<int>(index), + QString::fromStdString(dataName)); +} + +void IndirectFitPlotView::clearDataSelection() { + m_plotForm->cbDataSelection->clear(); +} + +void IndirectFitPlotView::plotInTopPreview( + const QString &name, Mantid::API::MatrixWorkspace_sptr workspace, + std::size_t spectrum, Qt::GlobalColor colour) { + m_plotForm->ppPlotTop->addSpectrum(name, workspace, spectrum, colour); +} + +void IndirectFitPlotView::plotInBottomPreview( + const QString &name, Mantid::API::MatrixWorkspace_sptr workspace, + std::size_t spectrum, Qt::GlobalColor colour) { + m_plotForm->ppPlotBottom->addSpectrum(name, workspace, spectrum, colour); +} + +void IndirectFitPlotView::removeFromTopPreview(const QString &name) { + m_plotForm->ppPlotTop->removeSpectrum(name); +} + +void IndirectFitPlotView::removeFromBottomPreview(const QString &name) { + m_plotForm->ppPlotBottom->removeSpectrum(name); +} + +void IndirectFitPlotView::disablePlotGuess() { + m_plotForm->ckPlotGuess->setDisabled(true); + m_plotForm->ckPlotGuess->setChecked(false); +} + +void IndirectFitPlotView::enablePlotGuess() { + m_plotForm->ckPlotGuess->setEnabled(true); +} + +void IndirectFitPlotView::disableSpectrumSelection() { + m_plotForm->spPlotSpectrum->setValue(0); + m_plotForm->spPlotSpectrum->setDisabled(true); +} + +void IndirectFitPlotView::enableSpectrumSelection() { + m_plotForm->spPlotSpectrum->setEnabled(true); +} + +void IndirectFitPlotView::disableFitRangeSelection() { + m_plotForm->ppPlotTop->getRangeSelector("FitRange")->setVisible(false); +} + +void IndirectFitPlotView::enableFitRangeSelection() { + m_plotForm->ppPlotTop->getRangeSelector("FitRange")->setVisible(true); +} + +void IndirectFitPlotView::clearTopPreview() { m_plotForm->ppPlotTop->clear(); } + +void IndirectFitPlotView::clearBottomPreview() { + m_plotForm->ppPlotBottom->clear(); +} + +void IndirectFitPlotView::clear() { + clearTopPreview(); + clearBottomPreview(); +} + +void IndirectFitPlotView::setHWHMRange(double minimum, double maximum) { + auto selector = m_plotForm->ppPlotTop->getRangeSelector("HWHM"); + MantidQt::API::SignalBlocker<QObject> blocker(selector); + selector->setRange(minimum, maximum); +} + +void IndirectFitPlotView::setHWHMMaximum(double minimum) { + auto selector = m_plotForm->ppPlotTop->getRangeSelector("HWHM"); + MantidQt::API::SignalBlocker<QObject> blocker(selector); + selector->setMaximum(minimum); +} + +void IndirectFitPlotView::setHWHMMinimum(double maximum) { + auto selector = m_plotForm->ppPlotTop->getRangeSelector("HWHM"); + MantidQt::API::SignalBlocker<QObject> blocker(selector); + selector->setMinimum(maximum); +} + +void IndirectFitPlotView::addFitRangeSelector() { + auto fitRangeSelector = m_plotForm->ppPlotTop->addRangeSelector("FitRange"); + + connect(fitRangeSelector, SIGNAL(minValueChanged(double)), this, + SIGNAL(startXChanged(double))); + connect(fitRangeSelector, SIGNAL(maxValueChanged(double)), this, + SIGNAL(endXChanged(double))); +} + +void IndirectFitPlotView::addBackgroundRangeSelector() { + auto backRangeSelector = m_plotForm->ppPlotTop->addRangeSelector( + "Background", MantidWidgets::RangeSelector::YSINGLE); + backRangeSelector->setVisible(false); + backRangeSelector->setColour(Qt::darkGreen); + backRangeSelector->setRange(0.0, 1.0); + + connect(backRangeSelector, SIGNAL(minValueChanged(double)), this, + SIGNAL(backgroundChanged(double))); +} + +void IndirectFitPlotView::addHWHMRangeSelector() { + auto hwhmRangeSelector = m_plotForm->ppPlotTop->addRangeSelector("HWHM"); + hwhmRangeSelector->setColour(Qt::red); + hwhmRangeSelector->setRange(0.0, 0.0); + hwhmRangeSelector->setVisible(false); + + connect(hwhmRangeSelector, SIGNAL(minValueChanged(double)), this, + SIGNAL(hwhmMinimumChanged(double))); + connect(hwhmRangeSelector, SIGNAL(maxValueChanged(double)), this, + SIGNAL(hwhmMaximumChanged(double))); + connect(hwhmRangeSelector, SIGNAL(selectionChanged(double, double)), this, + SIGNAL(hwhmChanged(double, double))); +} + +void IndirectFitPlotView::setBackgroundRangeVisible(bool visible) { + m_plotForm->ppPlotTop->getRangeSelector("Background")->setVisible(visible); +} + +void IndirectFitPlotView::setHWHMRangeVisible(bool visible) { + m_plotForm->ppPlotTop->getRangeSelector("HWHM")->setVisible(visible); +} + +void IndirectFitPlotView::emitSelectedFitDataChanged(int index) { + if (index >= 0) + emit selectedFitDataChanged(boost::numeric_cast<std::size_t>(index)); +} + +void IndirectFitPlotView::emitPlotSpectrumChanged(int spectrum) { + emit plotSpectrumChanged(boost::numeric_cast<std::size_t>(spectrum)); +} + +void IndirectFitPlotView::emitPlotSpectrumChanged(const QString &spectrum) { + emit plotSpectrumChanged(spectrum.toULongLong()); +} + +void IndirectFitPlotView::emitPlotGuessChanged(int doPlotGuess) { + emit plotGuessChanged(doPlotGuess == Qt::Checked); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPlotView.h b/qt/scientific_interfaces/Indirect/IndirectFitPlotView.h new file mode 100644 index 0000000000000000000000000000000000000000..78453f20cc5707b44c3f487f7db3be8852e2a15c --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitPlotView.h @@ -0,0 +1,106 @@ +#ifndef MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITPLOTVIEW_H_ +#define MANTIDQTCUSTOMINTERFACESIDA_INDIRECTFITPLOTVIEW_H_ + +#include "ui_IndirectFitPreviewPlot.h" + +#include "MantidAPI/MatrixWorkspace.h" + +#include "MantidQtWidgets/Common/MantidWidget.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class DLLExport IndirectFitPlotView : public API::MantidWidget { + Q_OBJECT +public: + IndirectFitPlotView(QWidget *parent); + ~IndirectFitPlotView() override; + + std::size_t getSelectedSpectrum() const; + int getSelectedSpectrumIndex() const; + int getSelectedDataIndex() const; + std::size_t dataSelectionSize() const; + bool isPlotGuessChecked() const; + + void hideMultipleDataSelection(); + void showMultipleDataSelection(); + + void setAvailableSpectra(std::size_t minimum, std::size_t maximum); + void setAvailableSpectra(const std::vector<std::size_t>::const_iterator &from, + const std::vector<std::size_t>::const_iterator &to); + + void setMinimumSpectrum(int minimum); + void setMaximumSpectrum(int maximum); + void appendToDataSelection(const std::string &dataName); + void setNameInDataSelection(const std::string &dataName, std::size_t index); + void clearDataSelection(); + + void plotInTopPreview(const QString &name, + Mantid::API::MatrixWorkspace_sptr workspace, + std::size_t spectrum, Qt::GlobalColor colour); + void plotInBottomPreview(const QString &name, + Mantid::API::MatrixWorkspace_sptr workspace, + std::size_t spectrum, Qt::GlobalColor colour); + + void removeFromTopPreview(const QString &name); + void removeFromBottomPreview(const QString &name); + + void disablePlotGuess(); + void enablePlotGuess(); + + void disableSpectrumSelection(); + void enableSpectrumSelection(); + + void disableFitRangeSelection(); + void enableFitRangeSelection(); + + void setBackgroundLevel(double value); + + void setFitRange(double minimum, double maximum); + void setFitRangeMinimum(double minimum); + void setFitRangeMaximum(double maximum); + + void setBackgroundRangeVisible(bool visible); + void setHWHMRangeVisible(bool visible); + +public slots: + void clearTopPreview(); + void clearBottomPreview(); + void clear(); + void setHWHMRange(double minimum, double maximum); + void setHWHMMaximum(double minimum); + void setHWHMMinimum(double maximum); + +signals: + void selectedFitDataChanged(std::size_t); + void plotCurrentPreview(); + void plotSpectrumChanged(std::size_t); + void plotGuessChanged(bool); + void fitSelectedSpectrum(); + void startXChanged(double); + void endXChanged(double); + void hwhmMinimumChanged(double); + void hwhmMaximumChanged(double); + void hwhmChanged(double, double); + void backgroundChanged(double); + +private slots: + void emitPlotSpectrumChanged(int); + void emitPlotSpectrumChanged(const QString &spectrum); + void emitSelectedFitDataChanged(int); + void emitPlotGuessChanged(int); + +private: + void addFitRangeSelector(); + void addBackgroundRangeSelector(); + void addHWHMRangeSelector(); + + std::unique_ptr<Ui::IndirectFitPreviewPlot> m_plotForm; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif diff --git a/qt/scientific_interfaces/Indirect/IndirectFitPreviewPlot.ui b/qt/scientific_interfaces/Indirect/IndirectFitPreviewPlot.ui new file mode 100644 index 0000000000000000000000000000000000000000..0c3e848047dd9dbf1a5a3188ab3c8ba176006f8f --- /dev/null +++ b/qt/scientific_interfaces/Indirect/IndirectFitPreviewPlot.ui @@ -0,0 +1,176 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>IndirectFitPreviewPlot</class> + <widget class="QWidget" name="IndirectFitPreviewPlot"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>595</width> + <height>663</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,5,3,0"> + <item> + <widget class="QComboBox" name="cbDataSelection"/> + </item> + <item> + <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotTop" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>125</height> + </size> + </property> + <property name="showLegend" stdset="0"> + <bool>true</bool> + </property> + <property name="canvasColour" stdset="0"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + </widget> + </item> + <item> + <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotBottom" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>75</height> + </size> + </property> + <property name="showLegend" stdset="0"> + <bool>true</bool> + </property> + <property name="canvasColour" stdset="0"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="loPlotOptions"> + <item> + <widget class="QLabel" name="lbPlotSpectrum"> + <property name="text"> + <string>Plot Spectrum:</string> + </property> + </widget> + </item> + <item> + <widget class="QStackedWidget" name="swPlotSpectrum"> + <widget class="QWidget" name="pgPlotSpinBox"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QSpinBox" name="spPlotSpectrum"/> + </item> + </layout> + </widget> + <widget class="QWidget" name="pgPlotCombo"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QComboBox" name="cbPlotSpectrum"/> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <spacer name="hspFitSingle"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pbFitSingle"> + <property name="text"> + <string>Fit Single Spectrum</string> + </property> + </widget> + </item> + <item> + <spacer name="hspPlotPreview"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pbPlotPreview"> + <property name="text"> + <string>Plot Current Preview</string> + </property> + </widget> + </item> + <item> + <spacer name="hspPlotGuess"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QCheckBox" name="ckPlotGuess"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Plot Guess</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>MantidQt::MantidWidgets::PreviewPlot</class> + <extends>QWidget</extends> + <header>MantidQtWidgets/LegacyQwt/PreviewPlot.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/qt/scientific_interfaces/Indirect/IndirectFittingModel.cpp b/qt/scientific_interfaces/Indirect/IndirectFittingModel.cpp index deabecb0049db86a008b1bcdd5c3be7c570fd670..f88cb744a2514cfeb45c64afc6f88ae408d51282 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFittingModel.cpp +++ b/qt/scientific_interfaces/Indirect/IndirectFittingModel.cpp @@ -8,6 +8,7 @@ #include "MantidAPI/MultiDomainFunction.h" #include "MantidAPI/TableRow.h" +#include <algorithm> #include <numeric> #include <set> @@ -20,7 +21,9 @@ using namespace MantidQt::CustomInterfaces::IDA; bool equivalentWorkspaces(MatrixWorkspace_const_sptr lhs, MatrixWorkspace_const_sptr rhs) { - if (lhs->getName() == "" && rhs->getName() == "") + if (!lhs || !rhs) + return false; + else if (lhs->getName() == "" && rhs->getName() == "") return lhs == rhs; return lhs->getName() == rhs->getName(); } @@ -139,23 +142,50 @@ void addInputDataToSimultaneousFit(IAlgorithm_sptr fitAlgorithm, void addInputDataToSimultaneousFit( IAlgorithm_sptr fitAlgorithm, - const std::unique_ptr<IndirectFitData> &fitData) { + const std::unique_ptr<IndirectFitData> &fitData, std::size_t &counter) { const auto workspace = fitData->workspace(); - - const auto addData = [&](std::size_t i, std::size_t spectrum) { - const auto suffix = i == 0 ? "" : "_" + std::to_string(i); + const auto addData = [&](std::size_t spectrum) { + const auto suffix = counter == 0 ? "" : "_" + std::to_string(counter); addInputDataToSimultaneousFit( fitAlgorithm, workspace, spectrum, fitData->getRange(spectrum), fitData->excludeRegionsVector(spectrum), suffix); + counter += 1; + }; + fitData->applySpectra(addData); +} + +void addInputDataToSimultaneousFit( + IAlgorithm_sptr fitAlgorithm, + const std::unique_ptr<IndirectFitData> &fitData, + const std::pair<double, double> &range, const std::vector<double> &exclude, + std::size_t &counter) { + const auto workspace = fitData->workspace(); + const auto addData = [&](std::size_t spectrum) { + const auto suffix = counter == 0 ? "" : "_" + std::to_string(counter); + addInputDataToSimultaneousFit(fitAlgorithm, workspace, spectrum, range, + exclude, suffix); + counter += 1; }; - fitData->applyEnumeratedSpectra(addData); + fitData->applySpectra(addData); } void addInputDataToSimultaneousFit( IAlgorithm_sptr fitAlgorithm, const std::vector<std::unique_ptr<IndirectFitData>> &fittingData) { + std::size_t counter = 0; for (auto i = 0u; i < fittingData.size(); ++i) - addInputDataToSimultaneousFit(fitAlgorithm, fittingData[i]); + addInputDataToSimultaneousFit(fitAlgorithm, fittingData[i], counter); +} + +void addInputDataToSimultaneousFit( + IAlgorithm_sptr fitAlgorithm, + const std::vector<std::unique_ptr<IndirectFitData>> &fittingData, + const std::pair<double, double> &range, + const std::vector<double> &exclude) { + std::size_t counter = 0; + for (auto i = 0u; i < fittingData.size(); ++i) + addInputDataToSimultaneousFit(fitAlgorithm, fittingData[i], range, exclude, + counter); } IAlgorithm_sptr saveNexusProcessedAlgorithm(Workspace_sptr workspace, @@ -232,6 +262,20 @@ CompositeFunction_sptr createMultiDomainFunction(IFunction_sptr function, return multiDomainFunction; } +IFunction_sptr extractFirstInnerFunction(IFunction_sptr function) { + if (const auto multiDomain = + boost::dynamic_pointer_cast<MultiDomainFunction>(function)) { + if (multiDomain->nFunctions() > 0) + return multiDomain->getFunction(0); + } + return function; +} + +IFunction_sptr extractFirstInnerFunction(const std::string &function) { + return extractFirstInnerFunction( + FunctionFactory::Instance().createInitialized(function)); +} + template <typename WorkspaceType> boost::shared_ptr<WorkspaceType> getWorkspaceOutput(IAlgorithm_sptr algorithm, const std::string &propertyName) { @@ -239,8 +283,8 @@ getWorkspaceOutput(IAlgorithm_sptr algorithm, const std::string &propertyName) { algorithm->getProperty(propertyName)); } -MatrixWorkspace_sptr getOutputResult(IAlgorithm_sptr algorithm) { - return getWorkspaceOutput<MatrixWorkspace>(algorithm, "OutputWorkspace"); +WorkspaceGroup_sptr getOutputResult(IAlgorithm_sptr algorithm) { + return getWorkspaceOutput<WorkspaceGroup>(algorithm, "OutputWorkspace"); } ITableWorkspace_sptr getOutputParameters(IAlgorithm_sptr algorithm) { @@ -257,6 +301,21 @@ namespace MantidQt { namespace CustomInterfaces { namespace IDA { +PrivateFittingData::PrivateFittingData() : m_data() {} + +PrivateFittingData::PrivateFittingData(PrivateFittingData &&privateData) + : m_data(std::move(privateData.m_data)) {} + +PrivateFittingData::PrivateFittingData( + std::vector<std::unique_ptr<IndirectFitData>> &&data) + : m_data(std::move(data)) {} + +PrivateFittingData &PrivateFittingData:: +operator=(PrivateFittingData &&fittingData) { + m_data = std::move(fittingData.m_data); + return *this; +} + IndirectFittingModel::IndirectFittingModel() : m_previousModelSelected(false), m_fittingMode(FittingMode::SEQUENTIAL) {} @@ -349,8 +408,8 @@ std::size_t IndirectFittingModel::getNumberOfSpectra(std::size_t index) const { } std::vector<std::string> IndirectFittingModel::getFitParameterNames() const { - if (m_fitFunction) - return m_fitFunction->getParameterNames(); + if (m_fitOutput) + return m_fitOutput->getResultParameterNames(); return std::vector<std::string>(); } @@ -358,6 +417,10 @@ Mantid::API::IFunction_sptr IndirectFittingModel::getFittingFunction() const { return m_activeFunction; } +void IndirectFittingModel::setFittingData(PrivateFittingData &&fittingData) { + m_fittingData = std::move(fittingData.m_data); +} + void IndirectFittingModel::setSpectra(const std::string &spectra, std::size_t dataIndex) { setSpectra(DiscontinuousSpectra<std::size_t>(spectra), dataIndex); @@ -407,6 +470,9 @@ void IndirectFittingModel::addWorkspace(const std::string &workspaceName) { void IndirectFittingModel::addWorkspace(const std::string &workspaceName, const std::string &spectra) { + if (spectra.empty()) + throw std::runtime_error( + "Fitting Data must consist of one or more spectra."); addWorkspace(workspaceName, DiscontinuousSpectra<std::size_t>(spectra)); } @@ -454,9 +520,9 @@ void IndirectFittingModel::removeFittingData(std::size_t index) { m_defaultParameters.erase(m_defaultParameters.begin() + index); } -void IndirectFittingModel::clearWorkspaces() { - m_fittingData.clear(); +PrivateFittingData IndirectFittingModel::clearWorkspaces() { m_fitOutput.reset(); + return std::move(m_fittingData); } void IndirectFittingModel::setFittingMode(FittingMode mode) { @@ -485,8 +551,8 @@ void IndirectFittingModel::addOutput(IAlgorithm_sptr fitAlgorithm, auto group = getOutputGroup(fitAlgorithm); auto parameters = getOutputParameters(fitAlgorithm); auto result = getOutputResult(fitAlgorithm); - m_fitFunction = FunctionFactory::Instance().createInitialized( - fitAlgorithm->getPropertyValue("Function")); + m_fitFunction = + extractFirstInnerFunction(fitAlgorithm->getPropertyValue("Function")); addOutput(group, parameters, result, fitDataBegin, fitDataEnd); } @@ -504,7 +570,7 @@ void IndirectFittingModel::addSingleFitOutput(IAlgorithm_sptr fitAlgorithm, void IndirectFittingModel::addOutput(WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) { if (m_previousModelSelected && m_fitOutput) @@ -519,7 +585,7 @@ void IndirectFittingModel::addOutput(WorkspaceGroup_sptr resultGroup, void IndirectFittingModel::addOutput(WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum) { if (m_previousModelSelected && m_fitOutput) @@ -534,7 +600,7 @@ void IndirectFittingModel::addOutput(WorkspaceGroup_sptr resultGroup, IndirectFitOutput IndirectFittingModel::createFitOutput( WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, const FitDataIterator &fitDataBegin, + WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) const { return IndirectFitOutput(resultGroup, parameterTable, resultWorkspace, fitDataBegin, fitDataEnd); @@ -543,7 +609,7 @@ IndirectFitOutput IndirectFittingModel::createFitOutput( IndirectFitOutput IndirectFittingModel::createFitOutput( Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, IndirectFitData *fitData, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum) const { return IndirectFitOutput(resultGroup, parameterTable, resultWorkspace, fitData, spectrum); @@ -552,7 +618,7 @@ IndirectFitOutput IndirectFittingModel::createFitOutput( void IndirectFittingModel::addOutput(IndirectFitOutput *fitOutput, WorkspaceGroup_sptr resultGroup, ITableWorkspace_sptr parameterTable, - MatrixWorkspace_sptr resultWorkspace, + WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) const { fitOutput->addOutput(resultGroup, parameterTable, resultWorkspace, @@ -562,7 +628,7 @@ void IndirectFittingModel::addOutput(IndirectFitOutput *fitOutput, void IndirectFittingModel::addOutput( IndirectFitOutput *fitOutput, Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, IndirectFitData *fitData, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum) const { fitOutput->addOutput(resultGroup, parameterTable, resultWorkspace, fitData, spectrum); @@ -616,7 +682,7 @@ std::unordered_map<std::string, ParameterValue> boost::optional<ResultLocation> IndirectFittingModel::getResultLocation(std::size_t index, std::size_t spectrum) const { - if (m_previousModelSelected && m_fitOutput) + if (m_previousModelSelected && m_fitOutput && m_fittingData.size() > index) return m_fitOutput->getResultLocation(m_fittingData[index].get(), spectrum); return boost::none; } @@ -632,7 +698,7 @@ void IndirectFittingModel::saveResult() const { } } -MatrixWorkspace_sptr IndirectFittingModel::getResultWorkspace() const { +WorkspaceGroup_sptr IndirectFittingModel::getResultWorkspace() const { return m_fitOutput->getLastResultWorkspace(); } @@ -650,7 +716,12 @@ CompositeFunction_sptr IndirectFittingModel::getMultiDomainFunction() const { } IAlgorithm_sptr IndirectFittingModel::getFittingAlgorithm() const { - if (m_fittingMode == FittingMode::SEQUENTIAL) + return getFittingAlgorithm(m_fittingMode); +} + +IAlgorithm_sptr +IndirectFittingModel::getFittingAlgorithm(FittingMode mode) const { + if (mode == FittingMode::SEQUENTIAL) return createSequentialFit(getFittingFunction()); else return createSimultaneousFit(getMultiDomainFunction()); @@ -713,7 +784,19 @@ IndirectFittingModel::createSimultaneousFit(IFunction_sptr function) const { auto fitAlgorithm = simultaneousFitAlgorithm(); fitAlgorithm->setProperty("Function", function); addInputDataToSimultaneousFit(fitAlgorithm, m_fittingData); - fitAlgorithm->setProperty("Output", simultaneousFitOutputName()); + fitAlgorithm->setProperty("OutputWorkspace", simultaneousFitOutputName()); + return fitAlgorithm; +} + +IAlgorithm_sptr IndirectFittingModel::createSimultaneousFitWithEqualRange( + IFunction_sptr function) const { + auto fitAlgorithm = simultaneousFitAlgorithm(); + fitAlgorithm->setProperty("Function", function); + + auto exclude = vectorFromString<double>(getExcludeRegion(0, 0)); + addInputDataToSimultaneousFit(fitAlgorithm, m_fittingData, + getFittingRange(0, 0), exclude); + fitAlgorithm->setProperty("OutputWorkspace", simultaneousFitOutputName()); return fitAlgorithm; } diff --git a/qt/scientific_interfaces/Indirect/IndirectFittingModel.h b/qt/scientific_interfaces/Indirect/IndirectFittingModel.h index dbe79136caaabf735ca31d6271a41fb280c44671..0468c835c9f55cceb5e497a4d6a1aecaeedc5cf5 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFittingModel.h +++ b/qt/scientific_interfaces/Indirect/IndirectFittingModel.h @@ -16,6 +16,21 @@ namespace IDA { enum class FittingMode { SEQUENTIAL, SIMULTANEOUS }; +class IndirectFittingModel; + +struct PrivateFittingData { + friend class IndirectFittingModel; + +public: + PrivateFittingData(); + PrivateFittingData &operator=(PrivateFittingData &&fittingData); + +private: + PrivateFittingData(PrivateFittingData &&privateData); + PrivateFittingData(std::vector<std::unique_ptr<IndirectFitData>> &&data); + std::vector<std::unique_ptr<IndirectFitData>> m_data; +}; + /* IndirectFittingModel - Provides methods for specifying and performing a QENS fit, as well as accessing the results of the fit. @@ -57,7 +72,7 @@ public: std::string createOutputName(const std::string &formatString, const std::string &rangeDelimiter, std::size_t dataIndex) const; - bool isMultiFit() const; + virtual bool isMultiFit() const; bool isPreviouslyFit(std::size_t dataIndex, std::size_t spectrum) const; bool hasZeroSpectra(std::size_t dataIndex) const; virtual boost::optional<std::string> isInvalidFunction() const; @@ -66,6 +81,7 @@ public: std::vector<std::string> getFitParameterNames() const; virtual Mantid::API::IFunction_sptr getFittingFunction() const; + void setFittingData(PrivateFittingData &&fittingData); void setSpectra(const std::string &spectra, std::size_t dataIndex); void setSpectra(Spectra &&spectra, std::size_t dataIndex); void setSpectra(const Spectra &spectra, std::size_t dataIndex); @@ -81,7 +97,7 @@ public: virtual void addWorkspace(Mantid::API::MatrixWorkspace_sptr workspace, const Spectra &spectra); virtual void removeWorkspace(std::size_t index); - void clearWorkspaces(); + PrivateFittingData clearWorkspaces(); void setFittingMode(FittingMode mode); virtual void setFitFunction(Mantid::API::IFunction_sptr function); void setDefaultParameterValue(const std::string &name, double value, @@ -90,7 +106,8 @@ public: std::size_t index); virtual void addOutput(Mantid::API::IAlgorithm_sptr fitAlgorithm); - template <typename F> void applySpectra(std::size_t index, const F &functor); + template <typename F> + void applySpectra(std::size_t index, const F &functor) const; FittingMode getFittingMode() const; std::unordered_map<std::string, ParameterValue> @@ -101,9 +118,9 @@ public: getDefaultParameters(std::size_t dataIndex) const; boost::optional<ResultLocation> getResultLocation(std::size_t dataIndex, std::size_t spectrum) const; - Mantid::API::MatrixWorkspace_sptr getResultWorkspace() const; + Mantid::API::WorkspaceGroup_sptr getResultWorkspace() const; Mantid::API::WorkspaceGroup_sptr getResultGroup() const; - Mantid::API::IAlgorithm_sptr getFittingAlgorithm() const; + virtual Mantid::API::IAlgorithm_sptr getFittingAlgorithm() const; Mantid::API::IAlgorithm_sptr getSingleFit(std::size_t dataIndex, std::size_t spectrum) const; @@ -113,10 +130,13 @@ public: std::size_t index); protected: + Mantid::API::IAlgorithm_sptr getFittingAlgorithm(FittingMode mode) const; Mantid::API::IAlgorithm_sptr createSequentialFit(Mantid::API::IFunction_sptr function) const; Mantid::API::IAlgorithm_sptr createSimultaneousFit(Mantid::API::IFunction_sptr function) const; + Mantid::API::IAlgorithm_sptr createSimultaneousFitWithEqualRange( + Mantid::API::IFunction_sptr function) const; virtual Mantid::API::CompositeFunction_sptr getMultiDomainFunction() const; virtual std::unordered_map<std::string, std::string> mapDefaultParameterNames() const; @@ -146,13 +166,13 @@ private: virtual IndirectFitOutput createFitOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) const; virtual IndirectFitOutput createFitOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum) const; void addOutput(Mantid::API::IAlgorithm_sptr fitAlgorithm, @@ -160,24 +180,24 @@ private: const FitDataIterator &fitDataEnd); void addOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd); void addOutput(Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum); virtual void addOutput(IndirectFitOutput *fitOutput, Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, const FitDataIterator &fitDataBegin, const FitDataIterator &fitDataEnd) const; virtual void addOutput(IndirectFitOutput *fitOutput, Mantid::API::WorkspaceGroup_sptr resultGroup, Mantid::API::ITableWorkspace_sptr parameterTable, - Mantid::API::MatrixWorkspace_sptr resultWorkspace, + Mantid::API::WorkspaceGroup_sptr resultWorkspace, IndirectFitData *fitData, std::size_t spectrum) const; std::unique_ptr<IndirectFitOutput> m_fitOutput; @@ -191,7 +211,8 @@ private: }; template <typename F> -void IndirectFittingModel::applySpectra(std::size_t index, const F &functor) { +void IndirectFittingModel::applySpectra(std::size_t index, + const F &functor) const { m_fittingData[index]->applySpectra(functor); } diff --git a/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionPresenter.cpp b/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionPresenter.cpp index ebcd9cea32fe65d9c8f49a29ae78e757a259784b..6c313a5617bc21a552f26b46495c033f76aa3781 100644 --- a/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionPresenter.cpp +++ b/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionPresenter.cpp @@ -29,12 +29,6 @@ private: IndirectSpectrumSelectionView *m_view; }; -} // namespace - -namespace MantidQt { -namespace CustomInterfaces { -namespace IDA { - std::string OR(const std::string &lhs, const std::string &rhs) { return "(" + lhs + "|" + rhs + ")"; } @@ -45,9 +39,9 @@ std::string NATURAL_NUMBER(std::size_t digits) { namespace Regexes { const std::string EMPTY = "^$"; -const std::string SPACE = "(\\s)*"; +const std::string SPACE = "[ ]*"; const std::string COMMA = SPACE + "," + SPACE; -const std::string MINUS = SPACE + "\\-" + SPACE; +const std::string MINUS = "\\-"; const std::string NUMBER = NATURAL_NUMBER(4); const std::string NATURAL_RANGE = "(" + NUMBER + MINUS + NUMBER + ")"; @@ -60,6 +54,11 @@ const std::string REAL_RANGE = "(" + REAL_NUMBER + COMMA + REAL_NUMBER + ")"; const std::string MASK_LIST = "(" + REAL_RANGE + "(" + COMMA + REAL_RANGE + ")*" + ")|" + EMPTY; } // namespace Regexes +} // namespace + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { IndirectSpectrumSelectionPresenter::IndirectSpectrumSelectionPresenter( IndirectFittingModel *model, IndirectSpectrumSelectionView *view) @@ -104,7 +103,6 @@ void IndirectSpectrumSelectionPresenter::setActiveIndexToZero() { void IndirectSpectrumSelectionPresenter::updateSpectra() { const auto workspace = m_model->getWorkspace(m_activeIndex); - MantidQt::API::SignalBlocker<QObject> blocker(m_view.get()); if (workspace) { setSpectraRange(0, workspace->getNumberHistograms() - 1); const auto spectra = m_model->getSpectra(m_activeIndex); @@ -126,10 +124,7 @@ void IndirectSpectrumSelectionPresenter::setSpectraRange(std::size_t minimum, std::size_t maximum) { int minimumInt = boost::numeric_cast<int>(minimum); int maximumInt = boost::numeric_cast<int>(maximum); - - MantidQt::API::SignalBlocker<QObject> blocker(m_view.get()); m_view->setSpectraRange(minimumInt, maximumInt); - m_view->setMaskSpectraRange(minimumInt, maximumInt); } void IndirectSpectrumSelectionPresenter::setModelSpectra( diff --git a/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionView.cpp b/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionView.cpp index 6ffdff768ba38ffb11a32851580f29663585a1ae..30ee7da8d1b4602887574410c0ecb6f52ad08215 100644 --- a/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionView.cpp +++ b/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionView.cpp @@ -17,6 +17,11 @@ IndirectSpectrumSelectionView::IndirectSpectrumSelectionView(QWidget *parent) connect(m_selector->cbMaskSpectrum, SIGNAL(currentIndexChanged(int)), this, SLOT(enableMaskLineEdit(int))); + connect(m_selector->spMaximumSpectrum, SIGNAL(valueChanged(int)), this, + SLOT(setSpectraRangeMaxiMin(int))); + connect(m_selector->spMinimumSpectrum, SIGNAL(valueChanged(int)), this, + SLOT(setSpectraRangeMiniMax(int))); + connect(m_selector->spMaximumSpectrum, SIGNAL(valueChanged(int)), this, SLOT(emitSpectraRangeChanged())); connect(m_selector->spMinimumSpectrum, SIGNAL(valueChanged(int)), this, @@ -32,11 +37,6 @@ IndirectSpectrumSelectionView::IndirectSpectrumSelectionView(QWidget *parent) connect(m_selector->leMaskBins, SIGNAL(editingFinished()), this, SLOT(emitMaskChanged())); - connect(m_selector->spMaximumSpectrum, SIGNAL(valueChanged(int)), this, - SLOT(setSpectraRangeMaxiMin(int))); - connect(m_selector->spMinimumSpectrum, SIGNAL(valueChanged(int)), this, - SLOT(setSpectraRangeMiniMax(int))); - connect(m_selector->cbSelectionMode, SIGNAL(currentIndexChanged(int)), this, SLOT(emitSpectraChanged(int))); connect(m_selector->cbSelectionMode, SIGNAL(currentIndexChanged(int)), this, @@ -83,14 +83,20 @@ void IndirectSpectrumSelectionView::displaySpectra(int minimum, int maximum) { } void IndirectSpectrumSelectionView::setSpectraRange(int minimum, int maximum) { + setSpectraRangeMinimum(minimum); + setSpectraRangeMaximum(maximum); +} + +void IndirectSpectrumSelectionView::setSpectraRangeMinimum(int minimum) { + MantidQt::API::SignalBlocker<QObject> blocker(m_selector->spMinimumSpectrum); m_selector->spMinimumSpectrum->setMinimum(minimum); - m_selector->spMaximumSpectrum->setMaximum(maximum); + setSpectraRangeMiniMax(minimum); } -void IndirectSpectrumSelectionView::setMaskSpectraRange(int minimum, - int maximum) { - m_selector->spMaskSpectrum->setMinimum(minimum); - m_selector->spMaskSpectrum->setMaximum(maximum); +void IndirectSpectrumSelectionView::setSpectraRangeMaximum(int maximum) { + MantidQt::API::SignalBlocker<QObject> blocker(m_selector->spMaximumSpectrum); + m_selector->spMaximumSpectrum->setMaximum(maximum); + setSpectraRangeMaxiMin(maximum); } void IndirectSpectrumSelectionView::setMaskSpectraList( @@ -151,13 +157,13 @@ void IndirectSpectrumSelectionView::setMaskString( } void IndirectSpectrumSelectionView::setSpectraRangeMaxiMin(int value) { - MantidQt::API::SignalBlocker<QObject> blocker(this); + MantidQt::API::SignalBlocker<QObject> blocker(m_selector->spMinimumSpectrum); m_selector->spMinimumSpectrum->setMaximum(value); m_selector->spMaskSpectrum->setMaximum(value); } void IndirectSpectrumSelectionView::setSpectraRangeMiniMax(int value) { - MantidQt::API::SignalBlocker<QObject> blocker(this); + MantidQt::API::SignalBlocker<QObject> blocker(m_selector->spMaximumSpectrum); m_selector->spMaximumSpectrum->setMinimum(value); m_selector->spMaskSpectrum->setMinimum(value); } diff --git a/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionView.h b/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionView.h index 1950e6dd0fa46b4cb56f535158abb9c4ff824a59..6c7ac237afc2954781beb467b6910d410823a2c9 100644 --- a/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionView.h +++ b/qt/scientific_interfaces/Indirect/IndirectSpectrumSelectionView.h @@ -57,7 +57,6 @@ public: void displaySpectra(int minimum, int maximum); void setSpectraRange(int minimum, int maximum); - void setMaskSpectraRange(int minimum, int maximum); void setSpectraRegex(const std::string ®ex); void setMaskBinsRegex(const std::string ®ex); @@ -106,6 +105,8 @@ private slots: void enableMaskLineEdit(int doEnable); private: + void setSpectraRangeMinimum(int minimum); + void setSpectraRangeMaximum(int maximum); QValidator *createValidator(const QString ®ex); std::unique_ptr<Ui::IndirectSpectrumSelector> m_selector; diff --git a/qt/scientific_interfaces/Indirect/IqtFit.cpp b/qt/scientific_interfaces/Indirect/IqtFit.cpp index e92dc9fb5c2364b36fa491d3560099ad94af1e73..b2aa51417d0de3cb1097fd383967533bb79b3d0b 100644 --- a/qt/scientific_interfaces/Indirect/IqtFit.cpp +++ b/qt/scientific_interfaces/Indirect/IqtFit.cpp @@ -31,22 +31,24 @@ IqtFit::IqtFit(QWidget *parent) : IndirectFitAnalysisTab(new IqtFitModel, parent), m_uiForm(new Ui::IqtFit) { m_uiForm->setupUi(parent); -} - -void IqtFit::setupFitTab() { m_iqtFittingModel = dynamic_cast<IqtFitModel *>(fittingModel()); - setFitPropertyBrowser(m_uiForm->fitPropertyBrowser); + + setFitDataPresenter(Mantid::Kernel::make_unique<IndirectFitDataPresenter>( + m_iqtFittingModel, m_uiForm->fitDataView)); + setPlotView(m_uiForm->pvFitPlotView); setSpectrumSelectionView(m_uiForm->svSpectrumView); + setFitPropertyBrowser(m_uiForm->fitPropertyBrowser); +} - m_uiForm->ckPlotGuess->setChecked(false); - disablePlotGuess(); - disablePlotPreview(); +void IqtFit::setupFitTab() { + setSampleWSSuffices({"_iqt"}); + setSampleFBSuffices({"_iqt.nxs"}); // Create custom function groups - const auto exponential = - FunctionFactory::Instance().createFunction("ExpDecay"); + auto &functionFactory = FunctionFactory::Instance(); + const auto exponential = functionFactory.createFunction("ExpDecay"); const auto stretchedExponential = - FunctionFactory::Instance().createFunction("StretchExp"); + functionFactory.createFunction("StretchExp"); addSpinnerFunctionGroup("Exponential", {exponential}, 0, 2); addCheckBoxFunctionGroup("Stretched Exponential", {stretchedExponential}); @@ -60,55 +62,15 @@ void IqtFit::setupFitTab() { // Set available background options setBackgroundOptions({"None", "FlatBackground"}); - auto fitRangeSelector = m_uiForm->ppPlotTop->addRangeSelector("IqtFitRange"); - connect(fitRangeSelector, SIGNAL(minValueChanged(double)), this, - SLOT(xMinSelected(double))); - connect(fitRangeSelector, SIGNAL(maxValueChanged(double)), this, - SLOT(xMaxSelected(double))); - - auto backRangeSelector = m_uiForm->ppPlotTop->addRangeSelector( - "IqtFitBackRange", MantidWidgets::RangeSelector::YSINGLE); - backRangeSelector->setVisible(false); - backRangeSelector->setColour(Qt::darkGreen); - backRangeSelector->setRange(0.0, 1.0); - connect(backRangeSelector, SIGNAL(minValueChanged(double)), this, - SLOT(backgroundSelectorChanged(double))); - - // Signal/slot ui connections - connect(m_uiForm->dsSampleInput, SIGNAL(dataReady(const QString &)), this, - SLOT(newDataLoaded(const QString &))); - connect(m_uiForm->pbSingle, SIGNAL(clicked()), this, SLOT(singleFit())); - - // Update plot when fit type changes - connect(m_uiForm->spPlotSpectrum, SIGNAL(valueChanged(int)), this, - SLOT(setSelectedSpectrum(int))); - connect(m_uiForm->spPlotSpectrum, SIGNAL(valueChanged(int)), this, - SLOT(updatePreviewPlots())); - - connect(m_uiForm->pbPlot, SIGNAL(clicked()), this, SLOT(plotWorkspace())); + connect(m_uiForm->pbPlot, SIGNAL(clicked()), this, SLOT(plotResult())); connect(m_uiForm->pbSave, SIGNAL(clicked()), this, SLOT(saveResult())); - connect(m_uiForm->pbPlotPreview, SIGNAL(clicked()), this, - SLOT(plotCurrentPreview())); - - connect(m_uiForm->ckPlotGuess, SIGNAL(stateChanged(int)), this, - SLOT(updatePlotGuess())); - connect(this, SIGNAL(parameterChanged(const Mantid::API::IFunction *)), this, - SLOT(parameterUpdated(const Mantid::API::IFunction *))); connect(this, SIGNAL(functionChanged()), this, SLOT(fitFunctionChanged())); connect(this, SIGNAL(customBoolChanged(const QString &, bool)), this, SLOT(customBoolUpdated(const QString &, bool))); } void IqtFit::fitFunctionChanged() { - auto backRangeSelector = - m_uiForm->ppPlotTop->getRangeSelector("IqtFitBackRange"); - - if (backgroundName() == "None") - backRangeSelector->setVisible(false); - else - backRangeSelector->setVisible(true); - if (numberOfCustomFunctions("StretchExp") > 0) { setCustomSettingEnabled("ConstrainBeta", true); } else { @@ -133,12 +95,8 @@ void IqtFit::customBoolUpdated(const QString &key, bool value) { if (key == "Constrain Intensities") { if (m_iqtFittingModel->setConstrainIntensities(value)) updateTies(); - } -} - -bool IqtFit::doPlotGuess() const { - return m_uiForm->ckPlotGuess->isEnabled() && - m_uiForm->ckPlotGuess->isChecked(); + } else if (key == "Make Beta Global") + m_iqtFittingModel->setBetaIsGlobal(value); } std::string IqtFit::fitTypeString() const { @@ -167,120 +125,16 @@ void IqtFit::setSaveResultEnabled(bool enabled) { m_uiForm->pbSave->setEnabled(enabled); } -void IqtFit::enablePlotPreview() { m_uiForm->pbPlotPreview->setEnabled(true); } - -void IqtFit::disablePlotPreview() { - m_uiForm->pbPlotPreview->setEnabled(false); -} - -/** - * Plot workspace based on user input - */ -void IqtFit::plotWorkspace() { - IndirectFitAnalysisTab::plotResult(m_uiForm->cbPlotType->currentText()); -} - -bool IqtFit::validate() { - UserInputValidator uiv; - - uiv.checkDataSelectorIsValid("Sample Input", m_uiForm->dsSampleInput); - - if (isEmptyModel()) - uiv.addErrorMessage("No fit function has been selected"); - - if (inputWorkspace()->getXMin() < 0) { - uiv.addErrorMessage("Error in input workspace: All X data must be " - "greater than or equal to 0."); - } - - auto error = uiv.generateErrorMessage(); - emit showMessageBox(error); - return error.isEmpty(); -} - -void IqtFit::loadSettings(const QSettings &settings) { - m_uiForm->dsSampleInput->readSettings(settings.group()); -} - -/** - * Called when new data has been loaded by the data selector. - * - * Configures ranges for spin boxes before raw plot is done. - * - * @param wsName Name of new workspace loaded - */ -void IqtFit::newDataLoaded(const QString wsName) { - IndirectFitAnalysisTab::newInputDataLoaded(wsName); - - const auto maxWsIndex = - static_cast<int>(inputWorkspace()->getNumberHistograms()) - 1; - - m_uiForm->spPlotSpectrum->setMaximum(maxWsIndex); - m_uiForm->spPlotSpectrum->setMinimum(0); - m_uiForm->spPlotSpectrum->setValue(0); -} - void IqtFit::setupFit(Mantid::API::IAlgorithm_sptr fitAlgorithm) { fitAlgorithm->setProperty("ExtractMembers", boolSettingValue("ExtractMembers")); IndirectFitAnalysisTab::setupFit(fitAlgorithm); } -void IqtFit::backgroundSelectorChanged(double val) { - m_iqtFittingModel->setDefaultParameterValue("A0", val, 0); - setParameterValue("LinearBackground", "A0", val); - setParameterValue("FlatBackground", "A0", val); -} - -void IqtFit::parameterUpdated(const Mantid::API::IFunction *function) { - if (function == nullptr) - return; - - if (background() && function->asString() == background()->asString()) { - auto rangeSelector = - m_uiForm->ppPlotTop->getRangeSelector("IqtFitBackRange"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMinimum(function->getParameter("A0")); - } -} - -void IqtFit::updatePreviewPlots() { - IndirectFitAnalysisTab::updatePlots(m_uiForm->ppPlotTop, - m_uiForm->ppPlotBottom); -} - -void IqtFit::updatePlotRange() { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("IqtFitRange"); - if (m_uiForm->ppPlotTop->hasCurve("Sample")) { - const auto range = m_uiForm->ppPlotTop->getCurveRange("Sample"); - rangeSelector->setRange(range.first, range.second); - } -} - -void IqtFit::startXChanged(double startX) { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("IqtFitRange"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMinimum(startX); -} - -void IqtFit::endXChanged(double endX) { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("IqtFitRange"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMaximum(endX); -} - -void IqtFit::disablePlotGuess() { m_uiForm->ckPlotGuess->setEnabled(false); } - -void IqtFit::enablePlotGuess() { m_uiForm->ckPlotGuess->setEnabled(true); } - -void IqtFit::addGuessPlot(MatrixWorkspace_sptr workspace) { - m_uiForm->ppPlotTop->addSpectrum("Guess", workspace, 0, Qt::green); +void IqtFit::plotResult() { + IndirectFitAnalysisTab::plotResult(m_uiForm->cbPlotType->currentText()); } -void IqtFit::removeGuessPlot() { - m_uiForm->ppPlotTop->removeSpectrum("Guess"); - m_uiForm->ckPlotGuess->setChecked(false); -} } // namespace IDA } // namespace CustomInterfaces } // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IqtFit.h b/qt/scientific_interfaces/Indirect/IqtFit.h index d7cc901a89983f6ec43989017e54c8558cc792fb..227f5394870d29e76b735d6983603cbd5aa38534 100644 --- a/qt/scientific_interfaces/Indirect/IqtFit.h +++ b/qt/scientific_interfaces/Indirect/IqtFit.h @@ -27,39 +27,21 @@ class DLLExport IqtFit : public IndirectFitAnalysisTab { public: IqtFit(QWidget *parent = nullptr); - bool doPlotGuess() const override; - private: void setupFitTab() override; - bool validate() override; - void loadSettings(const QSettings &settings) override; protected: void setPlotResultEnabled(bool enabled) override; void setSaveResultEnabled(bool enabled) override; - void enablePlotPreview() override; - void disablePlotPreview() override; - void addGuessPlot(Mantid::API::MatrixWorkspace_sptr workspace) override; - void removeGuessPlot() override; protected slots: - void newDataLoaded(const QString wsName); void setupFit(Mantid::API::IAlgorithm_sptr fitAlgorithm) override; - void updatePreviewPlots() override; - void updatePlotRange() override; - void startXChanged(double startX) override; - void endXChanged(double endX) override; - void backgroundSelectorChanged(double val); void updatePlotOptions() override; - void plotWorkspace(); void fitFunctionChanged(); - void parameterUpdated(const Mantid::API::IFunction *function); void customBoolUpdated(const QString &key, bool value); + void plotResult(); private: - void disablePlotGuess() override; - void enablePlotGuess() override; - void setConstrainIntensitiesEnabled(bool enabled); std::string fitTypeString() const; diff --git a/qt/scientific_interfaces/Indirect/IqtFit.ui b/qt/scientific_interfaces/Indirect/IqtFit.ui index 40da9a3e019648da39f50d9a754f2b89fa80e054..b34bba7a3cd5614b33c255274edc141f7deffaeb 100644 --- a/qt/scientific_interfaces/Indirect/IqtFit.ui +++ b/qt/scientific_interfaces/Indirect/IqtFit.ui @@ -6,316 +6,156 @@ <rect> <x>0</x> <y>0</y> - <width>801</width> + <width>698</width> <height>799</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> - <layout class="QVBoxLayout" name="layoutFuryFit"> + <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="QGroupBox" name="gbInput"> - <property name="title"> - <string>Input</string> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> - <layout class="QHBoxLayout" name="layoutInput"> - <item> - <widget class="MantidQt::MantidWidgets::DataSelector" name="dsSampleInput" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoLoad" stdset="0"> - <bool>true</bool> - </property> - <property name="workspaceSuffixes" stdset="0"> - <stringlist> - <string>_iqt</string> - </stringlist> - </property> - <property name="fileBrowserSuffixes" stdset="0"> - <stringlist> - <string>_iqt.nxs</string> - </stringlist> - </property> - <property name="showLoad" stdset="0"> - <bool>false</bool> - </property> - <property name="ShowGroups" stdset="0"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_25" stretch="1,1"> - <property name="leftMargin"> - <number>0</number> + <property name="childrenCollapsible"> + <bool>false</bool> </property> - <item> - <widget class="MantidQt::MantidWidgets::IndirectFitPropertyBrowser" name="fitPropertyBrowser"> - <widget class="QWidget" name="dockWidgetContents"/> - </widget> - </item> - <item> - <layout class="QVBoxLayout" name="verticalLayout_6"> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectFitDataView" name="fitDataView" native="true"/> + <widget class="QWidget" name="layoutWidget"> + <layout class="QVBoxLayout" name="verticalLayout"> <item> - <layout class="QVBoxLayout" name="loPlots" stretch="5,3"> + <layout class="QHBoxLayout" name="loPlotArea" stretch="1,1"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> <item> - <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotTop" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>125</height> - </size> - </property> - <property name="showLegend" stdset="0"> - <bool>true</bool> - </property> - <property name="canvasColour" stdset="0"> - <color> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </property> + <widget class="MantidQt::MantidWidgets::IndirectFitPropertyBrowser" name="fitPropertyBrowser"> + <widget class="QWidget" name="dockWidgetContents"/> </widget> </item> <item> - <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotBottom" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>75</height> - </size> - </property> - <property name="showLegend" stdset="0"> - <bool>true</bool> - </property> - <property name="canvasColour" stdset="0"> - <color> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </property> - </widget> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectFitPlotView" name="pvFitPlotView" native="true"/> </item> </layout> </item> <item> - <layout class="QHBoxLayout" name="gpPlotOptions"> - <item> - <widget class="QLabel" name="lbSpectra"> - <property name="text"> - <string>Plot Spectrum:</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spPlotSpectrum"> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>0</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbSingle"> - <property name="text"> - <string>Fit Single Spectrum</string> - </property> - </widget> - </item> - <item> - <spacer name="spLegendSpace"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbPlotPreview"> - <property name="text"> - <string>Plot Current Preview</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QCheckBox" name="ckPlotGuess"> - <property name="text"> - <string>Plot Guess</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView" name="svSpectrumView" native="true"/> </item> - </layout> - </item> - </layout> - </item> - <item> - <widget class="MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView" name="svSpectrumView" native="true"/> - </item> - <item> - <widget class="QGroupBox" name="gbOutput"> - <property name="title"> - <string>Output</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <item> - <widget class="QLabel" name="lbPlotType"> - <property name="text"> - <string>Plot Output: </string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="cbPlotType"> - <property name="enabled"> - <bool>false</bool> - </property> - <item> - <property name="text"> - <string>Background</string> - </property> - </item> - <item> - <property name="text"> - <string>Intensity</string> - </property> - </item> - <item> - <property name="text"> - <string>Tau</string> - </property> - </item> - <item> - <property name="text"> - <string>Beta</string> - </property> - </item> - <item> - <property name="text"> - <string>All</string> + <item> + <widget class="QGroupBox" name="gbOutput"> + <property name="title"> + <string>Output</string> </property> - </item> - </widget> - </item> - <item> - <widget class="QPushButton" name="pbPlot"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Plot Result</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_1"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbSave"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Save Result</string> - </property> - </widget> - </item> - </layout> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="lbPlotType"> + <property name="text"> + <string>Plot Output: </string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="cbPlotType"> + <property name="enabled"> + <bool>false</bool> + </property> + <item> + <property name="text"> + <string>Background</string> + </property> + </item> + <item> + <property name="text"> + <string>Intensity</string> + </property> + </item> + <item> + <property name="text"> + <string>Tau</string> + </property> + </item> + <item> + <property name="text"> + <string>Beta</string> + </property> + </item> + <item> + <property name="text"> + <string>All</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QPushButton" name="pbPlot"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Plot Result</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_1"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pbSave"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Save Result</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> </widget> </item> </layout> </widget> <customwidgets> <customwidget> - <class>MantidQt::MantidWidgets::DataSelector</class> + <class>MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView</class> <extends>QWidget</extends> - <header>MantidQtWidgets/Common/DataSelector.h</header> + <header>IndirectSpectrumSelectionView.h</header> + <container>1</container> </customwidget> <customwidget> - <class>MantidQt::MantidWidgets::PreviewPlot</class> - <extends>QWidget</extends> - <header>MantidQtWidgets/LegacyQwt/PreviewPlot.h</header> + <class>MantidQt::MantidWidgets::IndirectFitPropertyBrowser</class> + <extends>QDockWidget</extends> + <header>MantidQtWidgets/Common/IndirectFitPropertyBrowser.h</header> <container>1</container> </customwidget> <customwidget> - <class>MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView</class> + <class>MantidQt::CustomInterfaces::IDA::IndirectFitPlotView</class> <extends>QWidget</extends> - <header>IndirectSpectrumSelectionView.h</header> + <header>IndirectFitPlotView.h</header> <container>1</container> </customwidget> <customwidget> - <class>MantidQt::MantidWidgets::IndirectFitPropertyBrowser</class> - <extends>QDockWidget</extends> - <header>MantidQtWidgets/Common/IndirectFitPropertyBrowser.h</header> + <class>MantidQt::CustomInterfaces::IDA::IndirectFitDataView</class> + <extends>QWidget</extends> + <header>IndirectFitDataView.h</header> <container>1</container> </customwidget> </customwidgets> diff --git a/qt/scientific_interfaces/Indirect/IqtFitModel.cpp b/qt/scientific_interfaces/Indirect/IqtFitModel.cpp index d0ec4e9876de50cc7a7b192be7d336716a8717fa..ee8758fe739f42d842ede745a6f0e6d2605de388 100644 --- a/qt/scientific_interfaces/Indirect/IqtFitModel.cpp +++ b/qt/scientific_interfaces/Indirect/IqtFitModel.cpp @@ -2,6 +2,10 @@ #include "MantidAPI/AlgorithmManager.h" #include "MantidAPI/CompositeFunction.h" +#include "MantidAPI/FunctionFactory.h" +#include "MantidAPI/MultiDomainFunction.h" + +#include <boost/algorithm/string/predicate.hpp> using namespace Mantid::API; @@ -126,6 +130,47 @@ std::string getFitString(MatrixWorkspace_sptr workspace) { return "Fit"; return "_IqtFit"; } + +boost::optional<std::string> findFullParameterName(IFunction_sptr function, + const std::string &name) { + for (auto i = 0u; i < function->nParams(); ++i) { + const auto fullName = function->parameterName(i); + if (boost::algorithm::ends_with(fullName, name)) + return fullName; + } + return boost::none; +} + +std::vector<std::string> constructGlobalTies(const std::string ¶meter, + std::size_t nDomains) { + if (nDomains <= 1) + return std::vector<std::string>(); + + std::string firstParameter = "f0." + parameter; + std::vector<std::string> ties; + for (auto i = 1u; i < nDomains; ++i) + ties.emplace_back("f" + std::to_string(i) + "." + parameter + "=" + + firstParameter); + return ties; +} + +void addGlobalTie(CompositeFunction &composite, const std::string ¶meter) { + if (composite.nFunctions() == 0) + return; + + const auto fullName = + findFullParameterName(composite.getFunction(0), parameter); + + if (fullName) { + const auto ties = constructGlobalTies(*fullName, composite.nFunctions()); + composite.addTies(boost::algorithm::join(ties, ",")); + } +} + +IFunction_sptr createFunction(const std::string &functionString) { + return FunctionFactory::Instance().createInitialized(functionString); +} + } // namespace namespace MantidQt { @@ -133,7 +178,20 @@ namespace CustomInterfaces { namespace IDA { IqtFitModel::IqtFitModel() - : IndirectFittingModel(), m_constrainIntensities(false) {} + : IndirectFittingModel(), m_makeBetaGlobal(false), + m_constrainIntensities(false) {} + +CompositeFunction_sptr IqtFitModel::getMultiDomainFunction() const { + if (m_makeBetaGlobal) + return createFunctionWithGlobalBeta(getFittingFunction()); + return IndirectFittingModel::getMultiDomainFunction(); +} + +IAlgorithm_sptr IqtFitModel::getFittingAlgorithm() const { + if (m_makeBetaGlobal) + return createSimultaneousFitWithEqualRange(getMultiDomainFunction()); + return IndirectFittingModel::getFittingAlgorithm(); +} IAlgorithm_sptr IqtFitModel::sequentialFitAlgorithm() const { auto algorithm = AlgorithmManager::Instance().create("IqtFitSequential"); @@ -149,7 +207,7 @@ IAlgorithm_sptr IqtFitModel::simultaneousFitAlgorithm() const { std::string IqtFitModel::sequentialFitOutputName() const { if (isMultiFit()) - return "MultiIqtFit_" + m_fitType; + return "MultiIqtFit_" + m_fitType + "_Result"; auto fitString = getFitString(getWorkspace(0)); return createOutputName("%1%" + fitString + "_" + m_fitType + "_s%2%", "_to_", 0); @@ -157,7 +215,7 @@ std::string IqtFitModel::sequentialFitOutputName() const { std::string IqtFitModel::simultaneousFitOutputName() const { if (isMultiFit()) - return "MultiSimultaneousIqtFit_" + m_fitType; + return "MultiSimultaneousIqtFit_" + m_fitType + "_Result"; auto fitString = getFitString(getWorkspace(0)); return createOutputName("%1%" + fitString + "_mult" + m_fitType + "_s%2%", "_to_", 0); @@ -194,6 +252,8 @@ bool IqtFitModel::setConstrainIntensities(bool constrain) { return true; } +void IqtFitModel::setBetaIsGlobal(bool global) { m_makeBetaGlobal = global; } + std::unordered_map<std::string, ParameterValue> IqtFitModel::createDefaultParameters(std::size_t index) const { std::unordered_map<std::string, ParameterValue> parameters; @@ -209,6 +269,23 @@ IqtFitModel::createDefaultParameters(std::size_t index) const { return parameters; } +CompositeFunction_sptr +IqtFitModel::createFunctionWithGlobalBeta(IFunction_sptr function) const { + boost::shared_ptr<MultiDomainFunction> multiDomainFunction( + new MultiDomainFunction); + const auto functionString = function->asString(); + for (auto i = 0u; i < numberOfWorkspaces(); ++i) { + auto addDomains = [&](std::size_t) { + const auto index = multiDomainFunction->nFunctions(); + multiDomainFunction->addFunction(createFunction(functionString)); + multiDomainFunction->setDomainIndex(index, index); + }; + applySpectra(i, addDomains); + } + addGlobalTie(*multiDomainFunction, "Stretching"); + return multiDomainFunction; +} + } // namespace IDA } // namespace CustomInterfaces } // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/IqtFitModel.h b/qt/scientific_interfaces/Indirect/IqtFitModel.h index 2604ee8e3cf2d354cea6b4f21ecaf59a3bdf40e3..5f4b2469d975f46ad95241c1b739ae55fda0e32e 100644 --- a/qt/scientific_interfaces/Indirect/IqtFitModel.h +++ b/qt/scientific_interfaces/Indirect/IqtFitModel.h @@ -10,13 +10,18 @@ namespace IDA { class DLLExport IqtFitModel : public IndirectFittingModel { public: IqtFitModel(); + + Mantid::API::IAlgorithm_sptr getFittingAlgorithm() const override; + void setFitTypeString(const std::string &fitType); void setFitFunction(Mantid::API::IFunction_sptr function) override; bool canConstrainIntensities() const; bool setConstrainIntensities(bool constrain); + void setBetaIsGlobal(bool global); private: + Mantid::API::CompositeFunction_sptr getMultiDomainFunction() const override; Mantid::API::IAlgorithm_sptr sequentialFitAlgorithm() const override; Mantid::API::IAlgorithm_sptr simultaneousFitAlgorithm() const override; std::string sequentialFitOutputName() const override; @@ -25,7 +30,10 @@ private: std::size_t spectrum) const override; std::unordered_map<std::string, ParameterValue> createDefaultParameters(std::size_t index) const override; + Mantid::API::CompositeFunction_sptr + createFunctionWithGlobalBeta(Mantid::API::IFunction_sptr function) const; + bool m_makeBetaGlobal; bool m_constrainIntensities; std::string m_fitType; }; diff --git a/qt/scientific_interfaces/Indirect/JumpFit.cpp b/qt/scientific_interfaces/Indirect/JumpFit.cpp index a4f086fda803391af63466fe6c4ce6f8ddda856b..25293d4d61f7aeea42bb8936f3e3f3b8fcc6abe5 100644 --- a/qt/scientific_interfaces/Indirect/JumpFit.cpp +++ b/qt/scientific_interfaces/Indirect/JumpFit.cpp @@ -1,4 +1,6 @@ #include "JumpFit.h" +#include "JumpFitDataPresenter.h" + #include "../General/UserInputValidator.h" #include "MantidAPI/AlgorithmManager.h" #include "MantidAPI/FunctionFactory.h" @@ -24,54 +26,45 @@ JumpFit::JumpFit(QWidget *parent) : IndirectFitAnalysisTab(new JumpFitModel, parent), m_uiForm(new Ui::JumpFit) { m_uiForm->setupUi(parent); -} -void JumpFit::setupFitTab() { m_jumpFittingModel = dynamic_cast<JumpFitModel *>(fittingModel()); - setFitPropertyBrowser(m_uiForm->fitPropertyBrowser); + setFitDataPresenter(Mantid::Kernel::make_unique<JumpFitDataPresenter>( + m_jumpFittingModel, m_uiForm->fitDataView, m_uiForm->cbParameterType, + m_uiForm->cbParameter, m_uiForm->lbParameterType, m_uiForm->lbParameter)); + setPlotView(m_uiForm->pvFitPlotView); setSpectrumSelectionView(m_uiForm->svSpectrumView); + setFitPropertyBrowser(m_uiForm->fitPropertyBrowser); +} +void JumpFit::setupFitTab() { m_uiForm->svSpectrumView->hideSpectrumSelector(); m_uiForm->svSpectrumView->hideMaskSpectrumSelector(); - auto chudleyElliot = - FunctionFactory::Instance().createFunction("ChudleyElliot"); - auto hallRoss = FunctionFactory::Instance().createFunction("HallRoss"); - auto fickDiffusion = - FunctionFactory::Instance().createFunction("FickDiffusion"); - auto teixeiraWater = - FunctionFactory::Instance().createFunction("TeixeiraWater"); + setSampleWSSuffices({"_Result"}); + setSampleFBSuffices({"_Result.nxs"}); + + auto &functionFactory = FunctionFactory::Instance(); + auto chudleyElliot = functionFactory.createFunction("ChudleyElliot"); + auto hallRoss = functionFactory.createFunction("HallRoss"); + auto fickDiffusion = functionFactory.createFunction("FickDiffusion"); + auto teixeiraWater = functionFactory.createFunction("TeixeiraWater"); + auto eisfDiffCylinder = functionFactory.createFunction("EISFDiffCylinder"); + auto eisfDiffSphere = functionFactory.createFunction("EISFDiffSphere"); + auto eisfDiffSphereAklyl = + functionFactory.createFunction("EISFDiffSphereAlkyl"); addComboBoxFunctionGroup("ChudleyElliot", {chudleyElliot}); addComboBoxFunctionGroup("HallRoss", {hallRoss}); addComboBoxFunctionGroup("FickDiffusion", {fickDiffusion}); addComboBoxFunctionGroup("TeixeiraWater", {teixeiraWater}); + addComboBoxFunctionGroup("EISFDiffCylinder", {eisfDiffCylinder}); + addComboBoxFunctionGroup("EISFDiffSphere", {eisfDiffSphere}); + addComboBoxFunctionGroup("EISFDiffSphereAlkyl", {eisfDiffSphereAklyl}); - disablePlotGuess(); - disablePlotPreview(); - - // Create range selector - auto qRangeSelector = m_uiForm->ppPlotTop->addRangeSelector("JumpFitQ"); - connect(qRangeSelector, SIGNAL(minValueChanged(double)), this, - SLOT(xMinSelected(double))); - connect(qRangeSelector, SIGNAL(maxValueChanged(double)), this, - SLOT(xMaxSelected(double))); - - m_uiForm->cbWidth->setEnabled(false); - - // Connect data selector to handler method - connect(m_uiForm->dsSample, SIGNAL(dataReady(const QString &)), this, - SLOT(handleSampleInputReady(const QString &))); - // Connect width selector to handler method - connect(m_uiForm->cbWidth, SIGNAL(currentIndexChanged(int)), this, - SLOT(handleWidthChange(int))); + m_uiForm->cbParameter->setEnabled(false); // Handle plotting and saving connect(m_uiForm->pbSave, SIGNAL(clicked()), this, SLOT(saveResult())); - connect(m_uiForm->pbPlotPreview, SIGNAL(clicked()), this, - SLOT(plotCurrentPreview())); - - connect(m_uiForm->ckPlotGuess, SIGNAL(stateChanged(int)), this, - SLOT(updatePlotGuess())); + connect(m_uiForm->pbPlot, SIGNAL(clicked()), this, SLOT(plotClicked())); connect(this, SIGNAL(functionChanged()), this, SLOT(updateModelFitTypeString())); } @@ -80,147 +73,21 @@ void JumpFit::updateModelFitTypeString() { m_jumpFittingModel->setFitType(selectedFitType().toStdString()); } -bool JumpFit::doPlotGuess() const { - return m_uiForm->ckPlotGuess->isEnabled() && - m_uiForm->ckPlotGuess->isChecked(); -} - -/** - * Validate the form to check the program can be run - * - * @return :: Whether the form was valid - */ -bool JumpFit::validate() { - UserInputValidator uiv; - uiv.checkDataSelectorIsValid("Sample Input", m_uiForm->dsSample); - - // this workspace doesn't have any valid widths - if (m_jumpFittingModel->getWidths(0).empty()) - uiv.addErrorMessage( - "Sample Input: Workspace doesn't appear to contain any width data"); - - if (isEmptyModel()) - uiv.addErrorMessage("No fit function has been selected"); - - const auto errors = uiv.generateErrorMessage(); - emit showMessageBox(errors); - return errors.isEmpty(); -} - -/** - * Set the data selectors to use the default save directory - * when browsing for input files. - * - * @param settings :: The current settings - */ -void JumpFit::loadSettings(const QSettings &settings) { - m_uiForm->dsSample->readSettings(settings.group()); -} - -/** - * Plots the loaded file to the miniplot and sets the guides - * and the range - * - * @param filename :: The name of the workspace to plot - */ -void JumpFit::handleSampleInputReady(const QString &filename) { - IndirectFitAnalysisTab::newInputDataLoaded(filename); - setAvailableWidths(m_jumpFittingModel->getWidths(0)); - - QPair<double, double> res; - QPair<double, double> range = m_uiForm->ppPlotTop->getCurveRange("Sample"); - auto bounds = getResolutionRangeFromWs(filename, res) ? res : range; - auto qRangeSelector = m_uiForm->ppPlotTop->getRangeSelector("JumpFitQ"); - qRangeSelector->setMinimum(bounds.first); - qRangeSelector->setMaximum(bounds.second); - const auto width = m_jumpFittingModel->getWidthSpectrum(0, 0); - - if (width) { - m_uiForm->cbWidth->setEnabled(true); - setMinimumSpectrum(static_cast<int>(*width)); - setMaximumSpectrum(static_cast<int>(*width)); - setSelectedSpectrum(static_cast<int>(*width)); - } else { - m_uiForm->cbWidth->setEnabled(false); - emit showMessageBox("Workspace doesn't appear to contain any width data"); - } -} - -void JumpFit::setAvailableWidths(const std::vector<std::string> &widths) { - MantidQt::API::SignalBlocker<QObject> blocker(m_uiForm->cbWidth); - m_uiForm->cbWidth->clear(); - for (const auto &width : widths) - m_uiForm->cbWidth->addItem(QString::fromStdString(width)); -} - -/** - * Plots the loaded file to the miniplot when the selected spectrum changes - * - * @param text :: The name spectrum index to plot - */ -void JumpFit::handleWidthChange(int widthIndex) { - auto index = static_cast<std::size_t>(widthIndex); - auto spectrum = m_jumpFittingModel->getWidthSpectrum(index, 0); - - if (spectrum) { - m_jumpFittingModel->setActiveWidth(index, 0); - setSelectedSpectrum(static_cast<int>(*spectrum)); - } -} - -void JumpFit::startXChanged(double startX) { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("JumpFitQ"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMinimum(startX); -} - -void JumpFit::endXChanged(double endX) { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("JumpFitQ"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMaximum(endX); -} - -void JumpFit::disablePlotGuess() { m_uiForm->ckPlotGuess->setEnabled(false); } - -void JumpFit::enablePlotGuess() { m_uiForm->ckPlotGuess->setEnabled(true); } - -/** - * Updates the plot - */ -void JumpFit::updatePreviewPlots() { - IndirectFitAnalysisTab::updatePlots(m_uiForm->ppPlotTop, - m_uiForm->ppPlotBottom); +void JumpFit::updatePlotOptions() { + IndirectFitAnalysisTab::updatePlotOptions(m_uiForm->cbPlotType); } -void JumpFit::updatePlotRange() { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("JumpFitQ"); - if (m_uiForm->ppPlotTop->hasCurve("Sample")) { - const auto range = m_uiForm->ppPlotTop->getCurveRange("Sample"); - rangeSelector->setRange(range.first, range.second); - } +void JumpFit::setPlotResultEnabled(bool enabled) { + m_uiForm->pbPlot->setEnabled(enabled); + m_uiForm->cbPlotType->setEnabled(enabled); } -void JumpFit::updatePlotOptions() {} - -void JumpFit::setPlotResultEnabled(bool) {} - void JumpFit::setSaveResultEnabled(bool enabled) { m_uiForm->pbSave->setEnabled(enabled); } -void JumpFit::enablePlotPreview() { m_uiForm->pbPlotPreview->setEnabled(true); } - -void JumpFit::disablePlotPreview() { - m_uiForm->pbPlotPreview->setEnabled(false); -} - -void JumpFit::addGuessPlot(MatrixWorkspace_sptr workspace) { - m_uiForm->ppPlotTop->addSpectrum("Guess", workspace, 0, Qt::green); -} - -void JumpFit::removeGuessPlot() { - m_uiForm->ppPlotTop->removeSpectrum("Guess"); - m_uiForm->ckPlotGuess->setChecked(false); +void JumpFit::plotClicked() { + IndirectFitAnalysisTab::plotResult(m_uiForm->cbPlotType->currentText()); } } // namespace IDA diff --git a/qt/scientific_interfaces/Indirect/JumpFit.h b/qt/scientific_interfaces/Indirect/JumpFit.h index 174615dfb2bbea7369fb6def6a9cfb2d0096e674..b077582b27c49575debb9561ead3c2cb304c58b3 100644 --- a/qt/scientific_interfaces/Indirect/JumpFit.h +++ b/qt/scientific_interfaces/Indirect/JumpFit.h @@ -17,42 +17,18 @@ class DLLExport JumpFit : public IndirectFitAnalysisTab { public: JumpFit(QWidget *parent = nullptr); - // Inherited methods from IndirectDataAnalysisTab void setupFitTab() override; - bool validate() override; - /// Load default settings into the interface - void loadSettings(const QSettings &settings) override; - - bool doPlotGuess() const override; - protected slots: - /// Handle when the sample input is ready - void handleSampleInputReady(const QString &filename); - /// Slot to handle plotting a different spectrum of the workspace - void handleWidthChange(int); - /// Handles plotting and saving - void updatePreviewPlots() override; - void startXChanged(double startX) override; - void endXChanged(double endX) override; - void updatePlotRange() override; void updatePlotOptions() override; void updateModelFitTypeString(); + void plotClicked(); protected: void setPlotResultEnabled(bool enabled) override; void setSaveResultEnabled(bool enabled) override; - void enablePlotPreview() override; - void disablePlotPreview() override; - void addGuessPlot(Mantid::API::MatrixWorkspace_sptr workspace) override; - void removeGuessPlot() override; private: - void setAvailableWidths(const std::vector<std::string> &widths); - void disablePlotGuess() override; - void enablePlotGuess() override; - - // The UI form JumpFitModel *m_jumpFittingModel; std::unique_ptr<Ui::JumpFit> m_uiForm; }; diff --git a/qt/scientific_interfaces/Indirect/JumpFit.ui b/qt/scientific_interfaces/Indirect/JumpFit.ui index ca3cfc379aaef9d9b03b11926f25ac744e23e660..7a637d9169d4d833a36967e7b21dae1f516d253e 100644 --- a/qt/scientific_interfaces/Indirect/JumpFit.ui +++ b/qt/scientific_interfaces/Indirect/JumpFit.ui @@ -7,252 +7,223 @@ <x>0</x> <y>0</y> <width>805</width> - <height>800</height> + <height>798</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> - <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1,0"> + <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="QGroupBox" name="gbInput"> - <property name="title"> - <string>Input</string> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <widget class="QLabel" name="lblSample"> - <property name="text"> - <string>Sample </string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="MantidQt::MantidWidgets::DataSelector" name="dsSample" native="true"> - <property name="showLoad" stdset="0"> - <bool>false</bool> - </property> - <property name="workspaceSuffixes" stdset="0"> - <stringlist> - <string>_Result</string> - </stringlist> - </property> - <property name="fileBrowserSuffixes" stdset="0"> - <stringlist> - <string>_Result.nxs</string> - </stringlist> - </property> - <property name="ShowGroups" stdset="0"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="loWidth"> - <property name="topMargin"> - <number>0</number> + <property name="childrenCollapsible"> + <bool>false</bool> </property> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="lblWidth"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Width: </string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="cbWidth"/> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="loPlotArea" stretch="1,1"> - <item> - <widget class="MantidQt::MantidWidgets::IndirectFitPropertyBrowser" name="fitPropertyBrowser"> - <widget class="QWidget" name="dockWidgetContents"/> - </widget> - </item> - <item> - <layout class="QVBoxLayout" name="loPlots" stretch="5,3,0"> - <item> - <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotTop" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>125</height> - </size> - </property> - <property name="canvasColour" stdset="0"> - <color> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </property> - <property name="showLegend" stdset="0"> - <bool>true</bool> - </property> - <property name="curveErrorBars" stdset="0"> - <stringlist> - <string>Sample</string> - </stringlist> - </property> - </widget> - </item> - <item> - <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotBottom" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>75</height> - </size> - </property> - <property name="showLegend" stdset="0"> - <bool>true</bool> - </property> - <property name="canvasColour" stdset="0"> - <color> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </property> - </widget> - </item> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectFitDataView" name="fitDataView" native="true"/> + <widget class="QWidget" name="layoutWidget"> + <layout class="QVBoxLayout" name="verticalLayout"> <item> - <layout class="QHBoxLayout" name="horizontalLayout_4"> - <property name="topMargin"> - <number>0</number> - </property> + <layout class="QHBoxLayout" name="loPlotArea" stretch="1,1"> <item> - <widget class="MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView" name="svSpectrumView" native="true"/> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbPlotPreview"> - <property name="text"> - <string>Plot Current Preview</string> - </property> + <widget class="MantidQt::MantidWidgets::IndirectFitPropertyBrowser" name="fitPropertyBrowser"> + <widget class="QWidget" name="dockWidgetContents"/> </widget> </item> <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <layout class="QVBoxLayout" name="loPlotting" stretch="1,0"> + <property name="spacing"> + <number>6</number> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> + <property name="rightMargin"> + <number>0</number> </property> - </spacer> + <item> + <layout class="QVBoxLayout" name="vlParameterAndPlot" stretch="0,1"> + <property name="spacing"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="loParameterSelection"> + <property name="spacing"> + <number>6</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="lbParameterType"> + <property name="text"> + <string>Fit Parameter:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="cbParameterType"> + <property name="minimumSize"> + <size> + <width>80</width> + <height>0</height> + </size> + </property> + <item> + <property name="text"> + <string>Width</string> + </property> + </item> + <item> + <property name="text"> + <string>EISF</string> + </property> + </item> + </widget> + </item> + <item> + <spacer name="hspParameter"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="lbParameter"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Width: </string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="cbParameter"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>0</height> + </size> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectFitPlotView" name="pvFitPlotView" native="true"/> + </item> + </layout> + </item> + <item> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView" name="svSpectrumView" native="true"/> + </item> + </layout> </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="loOutput"> <item> - <widget class="QCheckBox" name="ckPlotGuess"> - <property name="text"> - <string>Plot Guess</string> + <widget class="QGroupBox" name="gbOutput"> + <property name="title"> + <string>Output Options</string> </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="lbPlotOutput"> + <property name="text"> + <string>Plot Output:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="cbPlotType"> + <item> + <property name="text"> + <string>All</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QPushButton" name="pbPlot"> + <property name="text"> + <string>Plot</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pbSave"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Save Result</string> + </property> + </widget> + </item> + </layout> </widget> </item> </layout> </item> </layout> - </item> - </layout> - </item> - <item> - <layout class="QHBoxLayout" name="loOutput"> - <item> - <widget class="QGroupBox" name="gbOutput"> - <property name="title"> - <string>Output Options</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QPushButton" name="pbSave"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Save Result</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> + </widget> + </widget> </item> </layout> </widget> <customwidgets> <customwidget> - <class>MantidQt::MantidWidgets::DataSelector</class> + <class>MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView</class> <extends>QWidget</extends> - <header>MantidQtWidgets/Common/DataSelector.h</header> + <header>IndirectSpectrumSelectionView.h</header> + <container>1</container> </customwidget> <customwidget> - <class>MantidQt::MantidWidgets::PreviewPlot</class> - <extends>QWidget</extends> - <header>MantidQtWidgets/LegacyQwt/PreviewPlot.h</header> + <class>MantidQt::MantidWidgets::IndirectFitPropertyBrowser</class> + <extends>QDockWidget</extends> + <header>MantidQtWidgets/Common/IndirectFitPropertyBrowser.h</header> <container>1</container> </customwidget> <customwidget> - <class>MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView</class> + <class>MantidQt::CustomInterfaces::IDA::IndirectFitPlotView</class> <extends>QWidget</extends> - <header>IndirectSpectrumSelectionView.h</header> + <header>IndirectFitPlotView.h</header> <container>1</container> </customwidget> <customwidget> - <class>MantidQt::MantidWidgets::IndirectFitPropertyBrowser</class> - <extends>QDockWidget</extends> - <header>MantidQtWidgets/Common/IndirectFitPropertyBrowser.h</header> + <class>MantidQt::CustomInterfaces::IDA::IndirectFitDataView</class> + <extends>QWidget</extends> + <header>IndirectFitDataView.h</header> <container>1</container> </customwidget> </customwidgets> diff --git a/qt/scientific_interfaces/Indirect/JumpFitAddWorkspaceDialog.cpp b/qt/scientific_interfaces/Indirect/JumpFitAddWorkspaceDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..924e716a38a942b011acc3e61943f65a2643c3c9 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/JumpFitAddWorkspaceDialog.cpp @@ -0,0 +1,72 @@ +#include "JumpFitAddWorkspaceDialog.h" + +#include <boost/optional.hpp> + +#include "MantidQtWidgets/Common/SignalBlocker.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +JumpFitAddWorkspaceDialog::JumpFitAddWorkspaceDialog(QWidget *parent) + : IAddWorkspaceDialog(parent) { + m_uiForm.setupUi(this); + + connect(m_uiForm.dsWorkspace, SIGNAL(dataReady(const QString &)), this, + SLOT(emitWorkspaceChanged(const QString &))); + connect(m_uiForm.cbParameterType, SIGNAL(currentIndexChanged(int)), this, + SLOT(emitParameterTypeChanged(int))); +} + +std::string JumpFitAddWorkspaceDialog::workspaceName() const { + return m_uiForm.dsWorkspace->getCurrentDataName().toStdString(); +} + +int JumpFitAddWorkspaceDialog::parameterNameIndex() const { + return m_uiForm.cbParameterName->currentIndex(); +} + +void JumpFitAddWorkspaceDialog::setParameterTypes( + const std::vector<std::string> &types) { + MantidQt::API::SignalBlocker<QObject> blocker(m_uiForm.cbParameterType); + m_uiForm.cbParameterType->clear(); + for (auto &&type : types) + m_uiForm.cbParameterType->addItem(QString::fromStdString(type)); +} + +void JumpFitAddWorkspaceDialog::setParameterNames( + const std::vector<std::string> &names) { + m_uiForm.cbParameterName->clear(); + for (auto &&name : names) + m_uiForm.cbParameterName->addItem(QString::fromStdString(name)); +} + +void JumpFitAddWorkspaceDialog::enableParameterSelection() { + m_uiForm.cbParameterName->setEnabled(true); + m_uiForm.cbParameterType->setEnabled(true); +} + +void JumpFitAddWorkspaceDialog::disableParameterSelection() { + m_uiForm.cbParameterName->setEnabled(false); + m_uiForm.cbParameterType->setEnabled(false); +} + +void JumpFitAddWorkspaceDialog::setWSSuffices(const QStringList &suffices) { + m_uiForm.dsWorkspace->setWSSuffixes(suffices); +} + +void JumpFitAddWorkspaceDialog::setFBSuffices(const QStringList &suffices) { + m_uiForm.dsWorkspace->setFBSuffixes(suffices); +} + +void JumpFitAddWorkspaceDialog::emitWorkspaceChanged(const QString &name) { + emit workspaceChanged(this, name.toStdString()); +} + +void JumpFitAddWorkspaceDialog::emitParameterTypeChanged(int type) { + emit parameterTypeChanged(this, type); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/JumpFitAddWorkspaceDialog.h b/qt/scientific_interfaces/Indirect/JumpFitAddWorkspaceDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..452789d9de713286d75ae16244971d49713d4310 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/JumpFitAddWorkspaceDialog.h @@ -0,0 +1,44 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_JUMPFITADDWORKSPACEDIALOG_H_ +#define MANTIDQTCUSTOMINTERFACES_JUMPFITADDWORKSPACEDIALOG_H_ + +#include "IAddWorkspaceDialog.h" +#include "ui_JumpFitAddWorkspaceDialog.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class JumpFitAddWorkspaceDialog : public IAddWorkspaceDialog { + Q_OBJECT +public: + explicit JumpFitAddWorkspaceDialog(QWidget *parent); + + std::string workspaceName() const override; + int parameterNameIndex() const; + + void setParameterTypes(const std::vector<std::string> &types); + void setParameterNames(const std::vector<std::string> &names); + void setWSSuffices(const QStringList &suffices) override; + void setFBSuffices(const QStringList &suffices) override; + + void enableParameterSelection(); + void disableParameterSelection(); + +public slots: + void emitWorkspaceChanged(const QString &name); + void emitParameterTypeChanged(int index); + +signals: + void workspaceChanged(JumpFitAddWorkspaceDialog *dialog, + const std::string &workspace); + void parameterTypeChanged(JumpFitAddWorkspaceDialog *dialog, int type); + +private: + Ui::JumpFitAddWorkspaceDialog m_uiForm; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACES_JUMPFITADDWORKSPACEDIALOG_H_ */ diff --git a/qt/scientific_interfaces/Indirect/JumpFitAddWorkspaceDialog.ui b/qt/scientific_interfaces/Indirect/JumpFitAddWorkspaceDialog.ui new file mode 100644 index 0000000000000000000000000000000000000000..b13089f01debef6c24f068d602b867cbf89f029d --- /dev/null +++ b/qt/scientific_interfaces/Indirect/JumpFitAddWorkspaceDialog.ui @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>JumpFitAddWorkspaceDialog</class> + <widget class="QDialog" name="JumpFitAddWorkspaceDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>410</width> + <height>183</height> + </rect> + </property> + <property name="windowTitle"> + <string>JumpFit Data Selection</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="gbSelectData"> + <property name="title"> + <string>Select data</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QGridLayout" name="loInput" rowstretch="0,0,0"> + <item row="0" column="1"> + <widget class="MantidQt::MantidWidgets::DataSelector" name="dsWorkspace" native="true"> + <property name="showLoad" stdset="0"> + <bool>false</bool> + </property> + <property name="ShowGroups" stdset="0"> + <bool>false</bool> + </property> + <property name="autoLoad" stdset="0"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="cbParameterName"/> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="cbParameterType"> + <item> + <property name="text"> + <string>Width</string> + </property> + </item> + <item> + <property name="text"> + <string>EISF</string> + </property> + </item> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="lbParameterType"> + <property name="text"> + <string>Parameter Type</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="lbParameterName"> + <property name="text"> + <string>Parameter Name</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="lbWorkspace"> + <property name="text"> + <string>Workspace</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>MantidQt::MantidWidgets::DataSelector</class> + <extends>QWidget</extends> + <header>MantidQtWidgets/Common/DataSelector.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>JumpFitAddWorkspaceDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>JumpFitAddWorkspaceDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/qt/scientific_interfaces/Indirect/JumpFitDataPresenter.cpp b/qt/scientific_interfaces/Indirect/JumpFitDataPresenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6cd0533ac9e6221160ec3f43701313d4ddc82a94 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/JumpFitDataPresenter.cpp @@ -0,0 +1,200 @@ +#include "JumpFitDataPresenter.h" +#include "JumpFitDataTablePresenter.h" + +#include "MantidKernel/make_unique.h" + +#include "MantidQtWidgets/Common/SignalBlocker.h" + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +JumpFitDataPresenter::JumpFitDataPresenter( + JumpFitModel *model, IndirectFitDataView *view, QComboBox *cbParameterType, + QComboBox *cbParameter, QLabel *lbParameterType, QLabel *lbParameter) + : IndirectFitDataPresenter( + model, view, Mantid::Kernel::make_unique<JumpFitDataTablePresenter>( + model, view->getDataTable())), + m_activeParameterType(0), m_dataIndex(0), + m_cbParameterType(cbParameterType), m_cbParameter(cbParameter), + m_lbParameterType(lbParameterType), m_lbParameter(lbParameter), + m_jumpModel(model) { + connect(view, SIGNAL(singleDataViewSelected()), this, + SLOT(showParameterComboBoxes())); + connect(view, SIGNAL(multipleDataViewSelected()), this, + SLOT(hideParameterComboBoxes())); + + connect(this, SIGNAL(requestedAddWorkspaceDialog()), this, + SLOT(updateActiveDataIndex())); + + connect(cbParameterType, SIGNAL(currentIndexChanged(const QString &)), this, + SLOT(setParameterLabel(const QString &))); + connect(cbParameterType, SIGNAL(currentIndexChanged(int)), this, + SLOT(updateAvailableParameters(int))); + connect(cbParameterType, SIGNAL(currentIndexChanged(int)), this, + SIGNAL(dataChanged())); + connect(cbParameter, SIGNAL(currentIndexChanged(int)), this, + SLOT(setSingleModelSpectrum(int))); + connect(cbParameter, SIGNAL(currentIndexChanged(int)), this, + SIGNAL(dataChanged())); + + connect(view, SIGNAL(sampleLoaded(const QString &)), this, + SLOT(updateAvailableParameters())); + connect(view, SIGNAL(sampleLoaded(const QString &)), this, + SLOT(updateParameterSelectionEnabled())); + + updateParameterSelectionEnabled(); +} + +void JumpFitDataPresenter::hideParameterComboBoxes() { + m_cbParameter->hide(); + m_cbParameterType->hide(); + m_lbParameter->hide(); + m_lbParameterType->hide(); +} + +void JumpFitDataPresenter::showParameterComboBoxes() { + m_cbParameter->show(); + m_cbParameterType->show(); + m_lbParameter->show(); + m_lbParameterType->show(); +} + +void JumpFitDataPresenter::setActiveParameterType(int type) { + m_activeParameterType = type; +} + +void JumpFitDataPresenter::updateActiveDataIndex() { + m_dataIndex = m_jumpModel->numberOfWorkspaces(); +} + +void JumpFitDataPresenter::updateAvailableParameters() { + updateAvailableParameters(m_cbParameterType->currentIndex()); +} + +void JumpFitDataPresenter::updateAvailableParameters(int typeIndex) { + if (typeIndex == 0) + setAvailableParameters(m_jumpModel->getWidths(0)); + else + setAvailableParameters(m_jumpModel->getEISF(0)); + setSingleModelSpectrum(m_cbParameter->currentIndex()); +} + +void JumpFitDataPresenter::updateParameterSelectionEnabled() { + const auto enabled = m_jumpModel->numberOfWorkspaces() > 0; + m_cbParameter->setEnabled(enabled); + m_cbParameterType->setEnabled(enabled); + m_lbParameter->setEnabled(enabled); +} + +void JumpFitDataPresenter::setAvailableParameters( + const std::vector<std::string> ¶meters) { + MantidQt::API::SignalBlocker<QObject> blocker(m_cbParameter); + m_cbParameter->clear(); + for (const auto ¶meter : parameters) + m_cbParameter->addItem(QString::fromStdString(parameter)); +} + +void JumpFitDataPresenter::setParameterLabel(const QString ¶meter) { + m_lbParameter->setText(parameter + ":"); +} + +void JumpFitDataPresenter::setDialogParameterNames( + JumpFitAddWorkspaceDialog *dialog, const std::string &workspace) { + try { + addWorkspace(m_jumpModel, workspace); + dialog->enableParameterSelection(); + } catch (const std::invalid_argument &) { + dialog->disableParameterSelection(); + } + updateParameterTypes(dialog); + updateParameterOptions(dialog); +} + +void JumpFitDataPresenter::setDialogParameterNames( + JumpFitAddWorkspaceDialog *dialog, int parameterType) { + setActiveParameterType(parameterType); + updateParameterOptions(dialog); +} + +void JumpFitDataPresenter::updateParameterOptions( + JumpFitAddWorkspaceDialog *dialog) { + if (m_activeParameterType == 0) + dialog->setParameterNames(m_jumpModel->getWidths(m_dataIndex)); + else + dialog->setParameterNames(m_jumpModel->getEISF(m_dataIndex)); +} + +void JumpFitDataPresenter::updateParameterTypes( + JumpFitAddWorkspaceDialog *dialog) { + dialog->setParameterTypes(getParameterTypes(m_dataIndex)); + setActiveParameterType(0); +} + +std::vector<std::string> +JumpFitDataPresenter::getParameterTypes(std::size_t dataIndex) const { + std::vector<std::string> types; + if (!m_jumpModel->zeroWidths(dataIndex)) + types.emplace_back("Width"); + if (!m_jumpModel->zeroEISF(dataIndex)) + types.emplace_back("EISF"); + return types; +} + +void JumpFitDataPresenter::addWorkspace(IndirectFittingModel *model, + const std::string &name) { + if (model->numberOfWorkspaces() > m_dataIndex) + model->removeWorkspace(m_dataIndex); + model->addWorkspace(name); +} + +void JumpFitDataPresenter::addDataToModel(IAddWorkspaceDialog const *dialog) { + if (const auto jumpDialog = + dynamic_cast<JumpFitAddWorkspaceDialog const *>(dialog)) { + setModelSpectrum(jumpDialog->parameterNameIndex()); + updateActiveDataIndex(); + } +} + +void JumpFitDataPresenter::setSingleModelSpectrum(int parameterIndex) { + auto index = static_cast<std::size_t>(parameterIndex); + if (m_cbParameterType->currentIndex() == 0) + m_jumpModel->setActiveWidth(index, 0); + else + m_jumpModel->setActiveEISF(index, 0); +} + +void JumpFitDataPresenter::setModelSpectrum(int index) { + if (index < 0) + throw std::runtime_error("No valid parameter was selected."); + else if (m_activeParameterType == 0) + m_jumpModel->setActiveWidth(static_cast<std::size_t>(index), m_dataIndex); + else + m_jumpModel->setActiveEISF(static_cast<std::size_t>(index), m_dataIndex); +} + +void JumpFitDataPresenter::dialogExecuted(IAddWorkspaceDialog const *dialog, + QDialog::DialogCode result) { + if (result == QDialog::Rejected && + m_jumpModel->numberOfWorkspaces() > m_dataIndex) + m_jumpModel->removeWorkspace(m_dataIndex); + else + IndirectFitDataPresenter::dialogExecuted(dialog, result); +} + +std::unique_ptr<IAddWorkspaceDialog> +JumpFitDataPresenter::getAddWorkspaceDialog(QWidget *parent) const { + auto dialog = Mantid::Kernel::make_unique<JumpFitAddWorkspaceDialog>(parent); + connect(dialog.get(), SIGNAL(workspaceChanged(JumpFitAddWorkspaceDialog *, + const std::string &)), + this, SLOT(setDialogParameterNames(JumpFitAddWorkspaceDialog *, + const std::string &))); + connect(dialog.get(), + SIGNAL(parameterTypeChanged(JumpFitAddWorkspaceDialog *, int)), this, + SLOT(setDialogParameterNames(JumpFitAddWorkspaceDialog *, int))); + return std::move(dialog); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/JumpFitDataPresenter.h b/qt/scientific_interfaces/Indirect/JumpFitDataPresenter.h new file mode 100644 index 0000000000000000000000000000000000000000..6a988a4dc1ea6b21d2943356b25017b8a9ba441d --- /dev/null +++ b/qt/scientific_interfaces/Indirect/JumpFitDataPresenter.h @@ -0,0 +1,82 @@ +#ifndef MANTIDQTCUSTOMINTERFACESIDA_JUMPFITDATAPRESENTER_H_ +#define MANTIDQTCUSTOMINTERFACESIDA_JUMPFITDATAPRESENTER_H_ + +#include "IndirectFitDataPresenter.h" +#include "JumpFitAddWorkspaceDialog.h" +#include "JumpFitModel.h" + +#include <QComboBox> +#include <QSpacerItem> + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +/** + A presenter. + Copyright © 2015-2016 ISIS Rutherford Appleton Laboratory, NScD + Oak Ridge National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport JumpFitDataPresenter : public IndirectFitDataPresenter { + Q_OBJECT +public: + JumpFitDataPresenter(JumpFitModel *model, IndirectFitDataView *view, + QComboBox *cbParameterType, QComboBox *cbParameter, + QLabel *lbParameterType, QLabel *lbParameter); + +private slots: + void hideParameterComboBoxes(); + void showParameterComboBoxes(); + void updateAvailableParameters(); + void updateAvailableParameters(int typeIndex); + void updateParameterSelectionEnabled(); + void setParameterLabel(const QString ¶meter); + void setDialogParameterNames(JumpFitAddWorkspaceDialog *dialog, + const std::string &workspace); + void setDialogParameterNames(JumpFitAddWorkspaceDialog *dialog, + int parameterType); + void setActiveParameterType(int type); + void updateActiveDataIndex(); + void setSingleModelSpectrum(int index); + +private: + void setAvailableParameters(const std::vector<std::string> ¶meters); + void addDataToModel(IAddWorkspaceDialog const *dialog) override; + void dialogExecuted(IAddWorkspaceDialog const *dialog, + QDialog::DialogCode result) override; + std::unique_ptr<IAddWorkspaceDialog> + getAddWorkspaceDialog(QWidget *parent) const override; + void updateParameterOptions(JumpFitAddWorkspaceDialog *dialog); + void updateParameterTypes(JumpFitAddWorkspaceDialog *dialog); + std::vector<std::string> getParameterTypes(std::size_t dataIndex) const; + void addWorkspace(IndirectFittingModel *model, const std::string &name); + void setModelSpectrum(int index); + + int m_activeParameterType; + std::size_t m_dataIndex; + + QComboBox *m_cbParameterType; + QComboBox *m_cbParameter; + QLabel *m_lbParameterType; + QLabel *m_lbParameter; + JumpFitModel *m_jumpModel; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACESIDA_JUMPFITDATAPRESENTER_H_ */ diff --git a/qt/scientific_interfaces/Indirect/JumpFitDataTablePresenter.cpp b/qt/scientific_interfaces/Indirect/JumpFitDataTablePresenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9257339d5f554ca1c7c075146b162fca7e4bfd1c --- /dev/null +++ b/qt/scientific_interfaces/Indirect/JumpFitDataTablePresenter.cpp @@ -0,0 +1,65 @@ +#include "JumpFitDataTablePresenter.h" + +#include <QComboBox> +#include <QHeaderView> + +namespace { +QStringList jumpFitHeaders() { + QStringList headers; + headers << "Workspace" + << "Parameter" + << "WS Index" + << "StartX" + << "EndX" + << "Exclude"; + return headers; +} +} // namespace + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +JumpFitDataTablePresenter::JumpFitDataTablePresenter(JumpFitModel *model, + QTableWidget *dataTable) + : IndirectDataTablePresenter(model, dataTable, jumpFitHeaders()), + m_jumpFitModel(model) { + auto header = dataTable->horizontalHeader(); + header->setResizeMode(1, QHeaderView::Stretch); +} + +int JumpFitDataTablePresenter::workspaceIndexColumn() const { return 2; } + +int JumpFitDataTablePresenter::startXColumn() const { return 3; } + +int JumpFitDataTablePresenter::endXColumn() const { return 4; } + +int JumpFitDataTablePresenter::excludeColumn() const { return 5; } + +void JumpFitDataTablePresenter::addTableEntry(std::size_t dataIndex, + std::size_t spectrum, int row) { + IndirectDataTablePresenter::addTableEntry(dataIndex, spectrum, row); + + const auto parameter = + m_jumpFitModel->getFitParameterName(dataIndex, spectrum); + auto cell = Mantid::Kernel::make_unique<QTableWidgetItem>( + QString::fromStdString(parameter)); + auto flags = cell->flags(); + flags ^= Qt::ItemIsEditable; + cell->setFlags(flags); + setCell(std::move(cell), row, 1); +} + +void JumpFitDataTablePresenter::updateTableEntry(std::size_t dataIndex, + std::size_t spectrum, + int row) { + IndirectDataTablePresenter::updateTableEntry(dataIndex, spectrum, row); + + const auto parameter = + m_jumpFitModel->getFitParameterName(dataIndex, spectrum); + setCellText(QString::fromStdString(parameter), row, 1); +} + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/JumpFitDataTablePresenter.h b/qt/scientific_interfaces/Indirect/JumpFitDataTablePresenter.h new file mode 100644 index 0000000000000000000000000000000000000000..2b743a0bdcb23449ce34bed3865c5da7bd5bb9dd --- /dev/null +++ b/qt/scientific_interfaces/Indirect/JumpFitDataTablePresenter.h @@ -0,0 +1,58 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_JUMPFITDATATABLEPRESENTER_H_ +#define MANTIDQTCUSTOMINTERFACES_JUMPFITDATATABLEPRESENTER_H_ + +#include "IndirectDataTablePresenter.h" +#include "JumpFitModel.h" + +#include <QTableWidget> + +#include <cstddef> +#include <unordered_map> + +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +/** + Presenter for a table of data containing Widths/EISF. + Copyright © 2015-2016 ISIS Rutherford Appleton Laboratory, NScD + Oak Ridge National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport JumpFitDataTablePresenter : public IndirectDataTablePresenter { + Q_OBJECT +public: + JumpFitDataTablePresenter(JumpFitModel *model, QTableWidget *dataTable); + +protected: + void addTableEntry(std::size_t dataIndex, std::size_t spectrum, + int row) override; + void updateTableEntry(std::size_t dataIndex, std::size_t spectrum, + int row) override; + +private: + int workspaceIndexColumn() const override; + int startXColumn() const override; + int endXColumn() const override; + int excludeColumn() const override; + + JumpFitModel *m_jumpFitModel; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACES_JUMPFITDATATABLEPRESENTER_H_ */ diff --git a/qt/scientific_interfaces/Indirect/JumpFitModel.cpp b/qt/scientific_interfaces/Indirect/JumpFitModel.cpp index 6028c0bd60aa48ef45e1b32304d68c1d0d7f196a..c3e5b8c30aab13b54e0bc2d91b1f24076129eb44 100644 --- a/qt/scientific_interfaces/Indirect/JumpFitModel.cpp +++ b/qt/scientific_interfaces/Indirect/JumpFitModel.cpp @@ -165,6 +165,9 @@ createHWHMWorkspace(MatrixWorkspace_sptr workspace, const std::string &hwhmName, const std::vector<std::size_t> &widthSpectra) { if (widthSpectra.empty()) return workspace; + if (AnalysisDataService::Instance().doesExist(hwhmName)) + return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>( + hwhmName); const auto subworkspaces = subdivideWidthWorkspace(workspace, widthSpectra); const auto hwhmWorkspace = appendAll(subworkspaces); @@ -239,8 +242,7 @@ void JumpFitModel::setActiveWidth(std::size_t widthIndex, const auto parametersIt = findJumpFitParameters(dataIndex); if (parametersIt != m_jumpParameters.end()) { const auto &widthSpectra = parametersIt->second.widthSpectra; - const auto spectra = createSpectra(widthSpectra[widthIndex]); - setSpectra(spectra, dataIndex); + setSpectra(createSpectra(widthSpectra[widthIndex]), dataIndex); } else throw std::runtime_error("Invalid width index specified."); } @@ -249,8 +251,7 @@ void JumpFitModel::setActiveEISF(std::size_t eisfIndex, std::size_t dataIndex) { const auto parametersIt = findJumpFitParameters(dataIndex); if (parametersIt != m_jumpParameters.end()) { const auto &eisfSpectra = parametersIt->second.eisfSpectra; - const auto spectra = createSpectra(eisfSpectra[eisfIndex]); - setSpectra(spectra, dataIndex); + setSpectra(createSpectra(eisfSpectra[eisfIndex]), dataIndex); } else throw std::runtime_error("Invalid EISF index specified."); } @@ -259,6 +260,26 @@ void JumpFitModel::setFitType(const std::string &fitType) { m_fitType = fitType; } +bool JumpFitModel::zeroWidths(std::size_t dataIndex) const { + const auto parameters = findJumpFitParameters(dataIndex); + if (parameters != m_jumpParameters.end()) + return parameters->second.widths.empty(); + return true; +} + +bool JumpFitModel::zeroEISF(std::size_t dataIndex) const { + const auto parameters = findJumpFitParameters(dataIndex); + if (parameters != m_jumpParameters.end()) + return parameters->second.eisf.empty(); + return true; +} + +bool JumpFitModel::isMultiFit() const { + if (numberOfWorkspaces() == 0) + return false; + return !allWorkspacesEqual(getWorkspace(0)); +} + std::vector<std::string> JumpFitModel::getWidths(std::size_t dataIndex) const { const auto parameters = findJumpFitParameters(dataIndex); if (parameters != m_jumpParameters.end()) @@ -292,11 +313,9 @@ JumpFitModel::getEISFSpectrum(std::size_t eisfIndex, } std::string JumpFitModel::sequentialFitOutputName() const { - auto name = createOutputName("%1%_JumpFit", "", 0); - auto position = name.find("_Result"); - if (position != std::string::npos) - return name.substr(0, position) + name.substr(position + 7, name.size()); - return name; + if (isMultiFit()) + return "MultiFofQFit_" + m_fitType + "_Result"; + return constructOutputName(); } std::string JumpFitModel::simultaneousFitOutputName() const { @@ -307,6 +326,23 @@ std::string JumpFitModel::singleFitOutputName(std::size_t, std::size_t) const { return sequentialFitOutputName(); } +std::string JumpFitModel::constructOutputName() const { + auto name = createOutputName("%1%_FofQFit_" + m_fitType, "", 0); + auto position = name.find("_Result"); + if (position != std::string::npos) + return name.substr(0, position) + name.substr(position + 7, name.size()); + return name; +} + +bool JumpFitModel::allWorkspacesEqual( + Mantid::API::MatrixWorkspace_sptr workspace) const { + for (auto i = 1u; i < numberOfWorkspaces(); ++i) { + if (getWorkspace(i) != workspace) + return false; + } + return true; +} + } // namespace IDA } // namespace CustomInterfaces } // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/JumpFitModel.h b/qt/scientific_interfaces/Indirect/JumpFitModel.h index d94eb6f03b95116b3747a8f074cf5f386720111a..41b2e7bdbfff064394da5910c871c78ef74d8d20 100644 --- a/qt/scientific_interfaces/Indirect/JumpFitModel.h +++ b/qt/scientific_interfaces/Indirect/JumpFitModel.h @@ -21,6 +21,11 @@ public: void removeWorkspace(std::size_t index) override; void setFitType(const std::string &fitType); + bool zeroWidths(std::size_t dataIndex) const; + bool zeroEISF(std::size_t dataIndex) const; + + bool isMultiFit() const override; + std::string getFitParameterName(std::size_t dataIndex, std::size_t spectrum) const; std::vector<std::string> getWidths(std::size_t dataIndex) const; @@ -38,6 +43,8 @@ public: std::size_t spectrum) const override; private: + std::string constructOutputName() const; + bool allWorkspacesEqual(Mantid::API::MatrixWorkspace_sptr workspace) const; JumpFitParameters & addJumpFitParameters(Mantid::API::MatrixWorkspace *workspace, const std::string &hwhmName); diff --git a/qt/scientific_interfaces/Indirect/LazyAsyncRunner.h b/qt/scientific_interfaces/Indirect/LazyAsyncRunner.h new file mode 100644 index 0000000000000000000000000000000000000000..ee7dba8a28bdbde35858905b80130b2f9c70ae03 --- /dev/null +++ b/qt/scientific_interfaces/Indirect/LazyAsyncRunner.h @@ -0,0 +1,91 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_LAZYASYNCRUNNER_H_ +#define MANTIDQTCUSTOMINTERFACES_LAZYASYNCRUNNER_H_ + +#include "DllConfig.h" + +#include <QtCore> + +#include <boost/optional.hpp> + +/** + A lazy asynchronous runner; forgets all but the most recent callback. + Copyright © 2015-2016 ISIS Rutherford Appleton Laboratory, NScD + Oak Ridge National Laboratory & European Spallation Source + This file is part of Mantid. + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +namespace MantidQt { +namespace CustomInterfaces { +namespace IDA { + +class DLLExport QtLazyAsyncRunnerBase : public QObject { + Q_OBJECT + +signals: + void finished(); + void finishedLazy(); + +protected slots: + void currentFinishedBase() { currentFinished(); } + +protected: + virtual void currentFinished() = 0; +}; + +template <typename Callback> +class DLLExport QtLazyAsyncRunner : public QtLazyAsyncRunnerBase { +public: + using ReturnType = typename std::result_of<Callback()>::type; + + explicit QtLazyAsyncRunner() + : m_current(), m_next(boost::none), m_initialized(false) { + connect(&m_current, SIGNAL(finished()), this, SLOT(currentFinishedBase())); + } + + void addCallback(Callback &&callback) { + if (m_next.is_initialized()) + m_next = boost::none; + + if (m_current.isFinished() || !m_initialized) + m_current.setFuture(QtConcurrent::run(callback)); + else + m_next = std::forward<Callback>(callback); + m_initialized = true; + } + + bool isFinished() const { return m_current.isFinished(); } + + ReturnType result() const { return m_current.result(); } + +protected: + void currentFinished() override { + if (m_next.is_initialized()) { + m_current.setFuture(QtConcurrent::run(*m_next)); + m_next = boost::none; + emit finished(); + } else + emit finishedLazy(); + } + +private: + QFutureWatcher<ReturnType> m_current; + boost::optional<Callback> m_next; + bool m_initialized; +}; + +} // namespace IDA +} // namespace CustomInterfaces +} // namespace MantidQt + +#endif /* MANTIDQTCUSTOMINTERFACES_INDIRECTFITDATAPRESENTER_H_ */ diff --git a/qt/scientific_interfaces/Indirect/MSDFit.cpp b/qt/scientific_interfaces/Indirect/MSDFit.cpp index ea02a9f8db51fc7ed060ee6c7f847b772802be1d..39f4b31e3b66bcf95b0c55debb100825986d0529 100644 --- a/qt/scientific_interfaces/Indirect/MSDFit.cpp +++ b/qt/scientific_interfaces/Indirect/MSDFit.cpp @@ -26,45 +26,29 @@ MSDFit::MSDFit(QWidget *parent) : IndirectFitAnalysisTab(new MSDFitModel, parent), m_uiForm(new Ui::MSDFit) { m_uiForm->setupUi(parent); + m_msdFittingModel = dynamic_cast<MSDFitModel *>(fittingModel()); - setFitPropertyBrowser(m_uiForm->fitPropertyBrowser); + setFitDataPresenter(Mantid::Kernel::make_unique<IndirectFitDataPresenter>( + m_msdFittingModel, m_uiForm->fitDataView)); + setPlotView(m_uiForm->pvFitPlotView); setSpectrumSelectionView(m_uiForm->svSpectrumView); + setFitPropertyBrowser(m_uiForm->fitPropertyBrowser); } void MSDFit::setupFitTab() { - auto fitRangeSelector = m_uiForm->ppPlotTop->addRangeSelector("MSDRange"); - connect(fitRangeSelector, SIGNAL(minValueChanged(double)), this, - SLOT(xMinSelected(double))); - connect(fitRangeSelector, SIGNAL(maxValueChanged(double)), this, - SLOT(xMaxSelected(double))); - - auto gaussian = FunctionFactory::Instance().createFunction("MSDGauss"); - auto peters = FunctionFactory::Instance().createFunction("MSDPeters"); - auto yi = FunctionFactory::Instance().createFunction("MSDYi"); + auto &functionFactory = FunctionFactory::Instance(); + auto gaussian = functionFactory.createFunction("MSDGauss"); + auto peters = functionFactory.createFunction("MSDPeters"); + auto yi = functionFactory.createFunction("MSDYi"); addComboBoxFunctionGroup("Gaussian", {gaussian}); addComboBoxFunctionGroup("Peters", {peters}); addComboBoxFunctionGroup("Yi", {yi}); - disablePlotGuess(); - disablePlotPreview(); - - connect(m_uiForm->dsSampleInput, SIGNAL(dataReady(const QString &)), this, - SLOT(newDataLoaded(const QString &))); - connect(m_uiForm->pbSingleFit, SIGNAL(clicked()), this, SLOT(singleFit())); - - // Update plot when fit type changes - connect(m_uiForm->spPlotSpectrum, SIGNAL(valueChanged(int)), this, - SLOT(setSelectedSpectrum(int))); - connect(m_uiForm->spPlotSpectrum, SIGNAL(valueChanged(int)), this, - SLOT(updatePreviewPlots())); + setSampleWSSuffices({"_eq"}); + setSampleFBSuffices({"_eq.nxs"}); connect(m_uiForm->pbPlot, SIGNAL(clicked()), this, SLOT(plotClicked())); connect(m_uiForm->pbSave, SIGNAL(clicked()), this, SLOT(saveResult())); - connect(m_uiForm->pbPlotPreview, SIGNAL(clicked()), this, - SLOT(plotCurrentPreview())); - - connect(m_uiForm->ckPlotGuess, SIGNAL(stateChanged(int)), this, - SLOT(updatePlotGuess())); connect(this, SIGNAL(functionChanged()), this, SLOT(updateModelFitTypeString())); } @@ -73,52 +57,10 @@ void MSDFit::updateModelFitTypeString() { m_msdFittingModel->setFitType(selectedFitType().toStdString()); } -bool MSDFit::doPlotGuess() const { - return m_uiForm->ckPlotGuess->isEnabled() && - m_uiForm->ckPlotGuess->isChecked(); -} - -bool MSDFit::validate() { - UserInputValidator uiv; - - uiv.checkDataSelectorIsValid("Sample Input", m_uiForm->dsSampleInput); - - auto range = std::make_pair(startX(), endX()); - uiv.checkValidRange("Fitting Range", range); - - // In the future the MSDFit algorithm should be modified to allow this - if (selectedFitType() == "None") - uiv.addErrorMessage("No fit type has been selected"); - - QString errors = uiv.generateErrorMessage(); - showMessageBox(errors); - - return errors.isEmpty(); -} - -void MSDFit::loadSettings(const QSettings &settings) { - m_uiForm->dsSampleInput->readSettings(settings.group()); -} - -void MSDFit::updatePreviewPlots() { - IndirectFitAnalysisTab::updatePlots(m_uiForm->ppPlotTop, - m_uiForm->ppPlotBottom); -} - -void MSDFit::updatePlotRange() { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("MSDRange"); - if (m_uiForm->ppPlotTop->hasCurve("Sample")) { - const auto range = m_uiForm->ppPlotTop->getCurveRange("Sample"); - rangeSelector->setRange(range.first, range.second); - } -} - -void MSDFit::disablePlotGuess() { m_uiForm->ckPlotGuess->setEnabled(false); } - -void MSDFit::enablePlotGuess() { m_uiForm->ckPlotGuess->setEnabled(true); } - void MSDFit::updatePlotOptions() {} +void MSDFit::plotClicked() { IndirectFitAnalysisTab::plotResult("All"); } + void MSDFit::setPlotResultEnabled(bool enabled) { m_uiForm->pbPlot->setEnabled(enabled); } @@ -127,58 +69,6 @@ void MSDFit::setSaveResultEnabled(bool enabled) { m_uiForm->pbSave->setEnabled(enabled); } -void MSDFit::enablePlotPreview() { m_uiForm->pbPlotPreview->setEnabled(true); } - -void MSDFit::disablePlotPreview() { - m_uiForm->pbPlotPreview->setEnabled(false); -} - -void MSDFit::addGuessPlot(Mantid::API::MatrixWorkspace_sptr workspace) { - m_uiForm->ppPlotTop->addSpectrum("Guess", workspace, 0, Qt::green); -} - -void MSDFit::removeGuessPlot() { - m_uiForm->ppPlotTop->removeSpectrum("Guess"); - m_uiForm->ckPlotGuess->setChecked(false); -} - -/** - * Called when new data has been loaded by the data selector. - * - * Configures ranges for spin boxes before raw plot is done. - * - * @param wsName Name of new workspace loaded - */ -void MSDFit::newDataLoaded(const QString wsName) { - IndirectFitAnalysisTab::newInputDataLoaded(wsName); - auto const &workspace = inputWorkspace(); - int maxWsIndex = 0; - - if (workspace) - maxWsIndex = static_cast<int>(workspace->getNumberHistograms()) - 1; - - m_uiForm->spPlotSpectrum->setMaximum(maxWsIndex); - m_uiForm->spPlotSpectrum->setMinimum(0); - m_uiForm->spPlotSpectrum->setValue(0); -} - -void MSDFit::startXChanged(double startX) { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("MSDRange"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMinimum(startX); -} - -void MSDFit::endXChanged(double endX) { - auto rangeSelector = m_uiForm->ppPlotTop->getRangeSelector("MSDRange"); - MantidQt::API::SignalBlocker<QObject> blocker(rangeSelector); - rangeSelector->setMaximum(endX); -} - -/** - * Handles mantid plotting - */ -void MSDFit::plotClicked() { IndirectFitAnalysisTab::plotResult("All"); } - } // namespace IDA } // namespace CustomInterfaces } // namespace MantidQt diff --git a/qt/scientific_interfaces/Indirect/MSDFit.h b/qt/scientific_interfaces/Indirect/MSDFit.h index ab8af7ef450e20922dd233442eb6b697770428e8..c9e64b5f788bf0b3331d61c4447861a010df18c9 100644 --- a/qt/scientific_interfaces/Indirect/MSDFit.h +++ b/qt/scientific_interfaces/Indirect/MSDFit.h @@ -16,35 +16,19 @@ class DLLExport MSDFit : public IndirectFitAnalysisTab { public: MSDFit(QWidget *parent = nullptr); - bool doPlotGuess() const override; - private: void setupFitTab() override; - bool validate() override; - void loadSettings(const QSettings &settings) override; protected slots: - void newDataLoaded(const QString wsName); - void startXChanged(double startX) override; - void endXChanged(double endX) override; void plotClicked(); - void updatePreviewPlots() override; - void updatePlotRange() override; void updatePlotOptions() override; void updateModelFitTypeString(); protected: void setPlotResultEnabled(bool enabled) override; void setSaveResultEnabled(bool enabled) override; - void enablePlotPreview() override; - void disablePlotPreview() override; - void addGuessPlot(Mantid::API::MatrixWorkspace_sptr workspace) override; - void removeGuessPlot() override; private: - void disablePlotGuess() override; - void enablePlotGuess() override; - MSDFitModel *m_msdFittingModel; std::unique_ptr<Ui::MSDFit> m_uiForm; }; diff --git a/qt/scientific_interfaces/Indirect/MSDFit.ui b/qt/scientific_interfaces/Indirect/MSDFit.ui index 36cf8294e3610cdae78924f331451f039695903d..764d6a883172df358be5052f78059faf8111c682 100644 --- a/qt/scientific_interfaces/Indirect/MSDFit.ui +++ b/qt/scientific_interfaces/Indirect/MSDFit.ui @@ -6,276 +6,114 @@ <rect> <x>0</x> <y>0</y> - <width>804</width> + <width>775</width> <height>799</height> </rect> </property> <property name="windowTitle"> <string>Form</string> </property> - <layout class="QVBoxLayout" name="verticalLayout_5" stretch="0,1,0,0"> + <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="QGroupBox" name="gbInput"> - <property name="title"> - <string>Input</string> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <widget class="MantidQt::MantidWidgets::DataSelector" name="dsSampleInput" native="true"/> - </item> - </layout> - </widget> - </item> - <item> - <layout class="QHBoxLayout" name="loMSDFit"> - <item> - <layout class="QVBoxLayout" name="loMSDPlot" stretch="0"> + <property name="childrenCollapsible"> + <bool>false</bool> + </property> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectFitDataView" name="fitDataView" native="true"/> + <widget class="QWidget" name=""> + <layout class="QVBoxLayout" name="verticalLayout"> <item> - <layout class="QHBoxLayout" name="loPlotProperties" stretch="1,1"> + <layout class="QHBoxLayout" name="loPlotArea" stretch="1,1"> <item> <widget class="MantidQt::MantidWidgets::IndirectFitPropertyBrowser" name="fitPropertyBrowser"> <widget class="QWidget" name="dockWidgetContents"/> </widget> </item> <item> - <layout class="QVBoxLayout" name="loPlotsLayout" stretch="5,3,0"> - <property name="sizeConstraint"> - <enum>QLayout::SetNoConstraint</enum> - </property> - <item> - <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotTop" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>1</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>125</height> - </size> - </property> - <property name="showLegend" stdset="0"> - <bool>true</bool> - </property> - <property name="curveErrorBars" stdset="0"> - <stringlist> - <string>Sample</string> - </stringlist> - </property> - <property name="canvasColour" stdset="0"> - <color> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </property> - </widget> - </item> - <item> - <widget class="MantidQt::MantidWidgets::PreviewPlot" name="ppPlotBottom" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> - <horstretch>1</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>0</width> - <height>75</height> - </size> - </property> - <property name="showLegend" stdset="0"> - <bool>true</bool> - </property> - <property name="canvasColour" stdset="0"> - <color> - <red>255</red> - <green>255</green> - <blue>255</blue> - </color> - </property> - </widget> - </item> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="topMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <property name="topMargin"> - <number>0</number> - </property> - <item> - <widget class="QLabel" name="lbPlotSpectrum"> - <property name="text"> - <string>Plot Spectrum:</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="spPlotSpectrum"> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>0</number> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbSingleFit"> - <property name="text"> - <string>Fit Single Spectrum</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbPlotPreview"> - <property name="text"> - <string>Plot Current Preview</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QCheckBox" name="ckPlotGuess"> - <property name="text"> - <string>Plot Guess</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </item> - </layout> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectFitPlotView" name="pvFitPlotView" native="true"/> </item> </layout> </item> + <item> + <widget class="MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView" name="svSpectrumView" native="true"/> + </item> + <item> + <widget class="QGroupBox" name="gbOutput"> + <property name="title"> + <string>Output</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <item> + <widget class="QPushButton" name="pbPlot"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Plot Result</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_1"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pbSave"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Save Result</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> </layout> - </item> - </layout> - </item> - <item> - <widget class="MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView" name="svSpectrumView" native="true"/> - </item> - <item> - <widget class="QGroupBox" name="gbOutput"> - <property name="title"> - <string>Output</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout_8"> - <item> - <widget class="QPushButton" name="pbPlot"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Plot Result</string> - </property> - <property name="checked"> - <bool>false</bool> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer_1"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QPushButton" name="pbSave"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Save Result</string> - </property> - </widget> - </item> - </layout> + </widget> </widget> </item> </layout> </widget> <customwidgets> <customwidget> - <class>MantidQt::MantidWidgets::DataSelector</class> + <class>MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView</class> <extends>QWidget</extends> - <header>MantidQtWidgets/Common/DataSelector.h</header> + <header>IndirectSpectrumSelectionView.h</header> + <container>1</container> </customwidget> <customwidget> - <class>MantidQt::MantidWidgets::PreviewPlot</class> - <extends>QWidget</extends> - <header>MantidQtWidgets/LegacyQwt/PreviewPlot.h</header> + <class>MantidQt::MantidWidgets::IndirectFitPropertyBrowser</class> + <extends>QDockWidget</extends> + <header>MantidQtWidgets/Common/IndirectFitPropertyBrowser.h</header> <container>1</container> </customwidget> <customwidget> - <class>MantidQt::CustomInterfaces::IDA::IndirectSpectrumSelectionView</class> + <class>MantidQt::CustomInterfaces::IDA::IndirectFitPlotView</class> <extends>QWidget</extends> - <header>IndirectSpectrumSelectionView.h</header> + <header>IndirectFitPlotView.h</header> <container>1</container> </customwidget> <customwidget> - <class>MantidQt::MantidWidgets::IndirectFitPropertyBrowser</class> - <extends>QDockWidget</extends> - <header>MantidQtWidgets/Common/IndirectFitPropertyBrowser.h</header> + <class>MantidQt::CustomInterfaces::IDA::IndirectFitDataView</class> + <extends>QWidget</extends> + <header>IndirectFitDataView.h</header> <container>1</container> </customwidget> </customwidgets> diff --git a/qt/scientific_interfaces/Indirect/MSDFitModel.cpp b/qt/scientific_interfaces/Indirect/MSDFitModel.cpp index 649e7a44485dfec34fb390b699c56dabc55739d7..2c2dcda6807f6cdf3677847abda4106b2f8b9933 100644 --- a/qt/scientific_interfaces/Indirect/MSDFitModel.cpp +++ b/qt/scientific_interfaces/Indirect/MSDFitModel.cpp @@ -12,7 +12,7 @@ void MSDFitModel::setFitType(const std::string &fitType) { std::string MSDFitModel::sequentialFitOutputName() const { if (isMultiFit()) - return "MultiMSDFit_" + m_fitType; + return "MultiMSDFit_" + m_fitType + "_Result"; return createOutputName("%1%_MSDFit_" + m_fitType + "_s%2%", "_to_", 0); } diff --git a/qt/scientific_interfaces/Muon/MuonAnalysis.cpp b/qt/scientific_interfaces/Muon/MuonAnalysis.cpp index acec36bd327b76ab6ce209aee95f0169d7d0b182..b0c1740c18fa9966ba8a3107417c71862d5ffa9b 100644 --- a/qt/scientific_interfaces/Muon/MuonAnalysis.cpp +++ b/qt/scientific_interfaces/Muon/MuonAnalysis.cpp @@ -591,7 +591,7 @@ Workspace_sptr MuonAnalysis::createAnalysisWorkspace(ItemType itemType, options.subtractedPeriods = getSubtractedPeriods(); options.timeZero = timeZero(); // user input options.loadedTimeZero = m_dataTimeZero; // from file - options.timeLimits.first = startTime(); + options.timeLimits.first = firstGoodBin(); options.timeLimits.second = finishTime(); options.rebinArgs = isRaw ? "" : rebinParams(loadedWS); options.plotType = plotType; @@ -3098,7 +3098,8 @@ MuonAnalysis::groupWorkspace(const std::string &wsName, m_dataTimeZero); // won't be used, but property is mandatory groupAlg->setPropertyValue("DetectorGroupingTable", groupingName); groupAlg->setPropertyValue("OutputWorkspace", outputEntry.name()); - groupAlg->setProperty("xmin", m_dataSelector->getStartTime()); + // want to remove data before first good data + groupAlg->setProperty("xmin", firstGoodBin()); groupAlg->setProperty("xmax", m_dataSelector->getEndTime()); groupAlg->execute(); diff --git a/qt/scientific_interfaces/test/MuonAnalysisDataLoaderTest.h b/qt/scientific_interfaces/test/MuonAnalysisDataLoaderTest.h index 660f497ccb823ff78ad9f2095ca83bc5f766c779..bab99a392028dcbd7f96b5ca1c4ce8dd63d7b53f 100644 --- a/qt/scientific_interfaces/test/MuonAnalysisDataLoaderTest.h +++ b/qt/scientific_interfaces/test/MuonAnalysisDataLoaderTest.h @@ -258,13 +258,13 @@ public: analysed = loader.createAnalysisWorkspace(corrected, options)); // test the output Mantid::MantidVec expectedOutput = { - -0.000584754, -0.037308, -0.0183329, 0.0250825, -0.0154756, - 0.018308, 0.0116216, -0.019053, 0.0100087, -0.0393029}; + -0.037308, -0.0183329, 0.0250825, -0.0154756, 0.018308, + 0.0116216, -0.019053, 0.0100087, -0.0393029, -0.001696}; const auto outputWS = boost::dynamic_pointer_cast<Mantid::API::MatrixWorkspace>(analysed); TS_ASSERT(outputWS); const auto &data = outputWS->y(0); - TS_ASSERT_EQUALS(data.size(), 2000); + TS_ASSERT_EQUALS(data.size(), 1958); auto xData = outputWS->x(0); auto offset = std::distance(xData.begin(), std::lower_bound(xData.begin(), xData.end(), diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/IndirectFitPropertyBrowser.h b/qt/widgets/common/inc/MantidQtWidgets/Common/IndirectFitPropertyBrowser.h index 748d5270422bda26819772b143307cb6b29d2942..cfbaf4fb0a39e01cd1d8b1585b4e39a2af53f206 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/IndirectFitPropertyBrowser.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/IndirectFitPropertyBrowser.h @@ -124,9 +124,10 @@ public: void removeFunction(PropertyHandler *handler) override; + void setWorkspaceIndex(int i) override; + public slots: void fit() override; - void sequentialFit() override; protected slots: diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/MantidTreeWidget.h b/qt/widgets/common/inc/MantidQtWidgets/Common/MantidTreeWidget.h index 64a7cf8d480fd711f5f29905d5ee3099e2bfde3b..ea20dd9af28ffb7d066f92b5ad688a9920dd14d9 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/MantidTreeWidget.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/MantidTreeWidget.h @@ -14,7 +14,7 @@ namespace MantidWidgets { class MantidDisplayBase; class WorkspaceTreeWidget; -enum class MantidItemSortScheme { ByName, ByLastModified }; +enum class MantidItemSortScheme { ByName, ByLastModified, ByMemorySize }; class EXPORT_OPT_MANTIDQT_COMMON MantidTreeWidget : public QTreeWidget { Q_OBJECT diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/MantidTreeWidgetItem.h b/qt/widgets/common/inc/MantidQtWidgets/Common/MantidTreeWidgetItem.h index 2357956d422aaa87da8a7c4b47f8495976e1ea6a..d62797469691cbf88fb831d708d70c59d1578340 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/MantidTreeWidgetItem.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/MantidTreeWidgetItem.h @@ -2,6 +2,7 @@ #define MANTIDTREEWIDGETITEM_H #include "MantidQtWidgets/Common/DllOption.h" +#include "MantidAPI/WorkspaceGroup.h" #include <MantidKernel/DateAndTime.h> #include <MantidQtWidgets/Common/WorkspaceObserver.h> #include <QTreeWidgetItem> @@ -26,6 +27,8 @@ private: MantidTreeWidget *m_parent; static Mantid::Types::Core::DateAndTime getLastModified(const QTreeWidgetItem *); + std::size_t getMemorySize() const; + std::size_t getGroupMemorySize(Mantid::API::WorkspaceGroup_sptr group) const; int m_sortPos; }; } diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/IWorkspaceDockView.h b/qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/IWorkspaceDockView.h index 093e7de112138ec1e8b3ad0bd434c2135330f138..42634010cbe8d424cfeaaee2f7108715382eb77e 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/IWorkspaceDockView.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/IWorkspaceDockView.h @@ -46,7 +46,7 @@ File change history is stored at: <https://github.com/mantidproject/mantid> class IWorkspaceDockView { public: enum class SortDirection { Ascending, Descending }; - enum class SortCriteria { ByName, ByLastModified }; + enum class SortCriteria { ByName, ByLastModified, ByMemorySize }; enum class SaveFileType { Nexus, ASCII, ASCIIv1 }; virtual ~IWorkspaceDockView(){}; diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidget.h b/qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidget.h index 4474389aea02859ad20c98022fb3ff76ca2f75a6..c547598edcdd8e85c2e60ef644c1543f4ef3f861 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidget.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidget.h @@ -2,6 +2,7 @@ #define MANTIDQT_MANTIDWIDGETS_WORKSPACETREEWIDGET_H #include "MantidQtWidgets/Common/DllOption.h" +#include "MantidQtWidgets/Common/MantidTreeWidget.h" #include <MantidAPI/ExperimentInfo.h> #include <MantidAPI/IAlgorithm_fwd.h> @@ -179,6 +180,9 @@ private: void setupLoadButtonMenu(); void setupConnections(); + MantidQt::MantidWidgets::MantidItemSortScheme + whichCriteria(SortCriteria criteria); + public slots: void clickedWorkspace(QTreeWidgetItem *, int); void saveWorkspaceCollection(); @@ -190,6 +194,7 @@ public slots: void sortDescending(); void chooseByName(); void chooseByLastModified(); + void chooseByMemorySize(); void keyPressEvent(QKeyEvent *) override; protected slots: diff --git a/qt/widgets/common/src/IndirectFitPropertyBrowser.cpp b/qt/widgets/common/src/IndirectFitPropertyBrowser.cpp index 4956a42050d9dfe127b98cd7ce2ae4ba45943398..f00b315025435262b604439678bb411f09fb6578 100644 --- a/qt/widgets/common/src/IndirectFitPropertyBrowser.cpp +++ b/qt/widgets/common/src/IndirectFitPropertyBrowser.cpp @@ -933,6 +933,10 @@ void IndirectFitPropertyBrowser::customFunctionRemoved(QtProperty *prop) { } } +void IndirectFitPropertyBrowser::setWorkspaceIndex(int i) { + FitPropertyBrowser::setWorkspaceIndex(i); +} + /** * Schedules a fit. */ diff --git a/qt/widgets/common/src/MantidTreeWidgetItem.cpp b/qt/widgets/common/src/MantidTreeWidgetItem.cpp index b15a1202e1352a239405e7eeaad51c0ef0e02ac8..e7c4683ea5a25749b64930ad8df69e559bc08542 100644 --- a/qt/widgets/common/src/MantidTreeWidgetItem.cpp +++ b/qt/widgets/common/src/MantidTreeWidgetItem.cpp @@ -61,9 +61,40 @@ bool MantidTreeWidgetItem::operator<(const QTreeWidgetItem &other) const { // If both should be sorted, and the scheme is set to ByName ... if (m_parent->getSortScheme() == MantidItemSortScheme::ByName) { - if (QString::compare(text(0), other.text(0), Qt::CaseInsensitive) < 0) - return true; - return false; + return QString::compare(text(0), other.text(0), Qt::CaseInsensitive) < 0; + } + // If both should be sorted and the scheme is set to ByMemorySize ... + else if (m_parent->getSortScheme() == MantidItemSortScheme::ByMemorySize) { + auto thisGroupSize = std::size_t(0); + auto otherGroupSize = std::size_t(0); + // If workspace parent is a group since groups return 0: + if (this->getMemorySize() == 0) { + auto theWorkspace = this->data(0, Qt::UserRole).value<Workspace_sptr>(); + thisGroupSize = getGroupMemorySize( + boost::dynamic_pointer_cast<WorkspaceGroup>(theWorkspace)); + } + // If workspace other is a group since groups return 0: + if (mantidOther->getMemorySize() == 0) { + auto theWorkspace = + mantidOther->data(0, Qt::UserRole).value<Workspace_sptr>(); + otherGroupSize = getGroupMemorySize( + boost::dynamic_pointer_cast<WorkspaceGroup>(theWorkspace)); + } + // If either are groups: + if (thisGroupSize > 0 || otherGroupSize > 0) { + // If this is group and other is not + if (thisGroupSize > 0 && otherGroupSize == 0) { + return thisGroupSize < mantidOther->getMemorySize(); + } + // If other is group and this iss not + if (otherGroupSize > 0 && thisGroupSize == 0) { + return this->getMemorySize() < otherGroupSize; + } + // Both are groups + return thisGroupSize < otherGroupSize; + } + // else return normally: + return this->getMemorySize() < mantidOther->getMemorySize(); } // ... else both should be sorted and the scheme is set to ByLastModified. else { @@ -103,5 +134,25 @@ DateAndTime MantidTreeWidgetItem::getLastModified(const QTreeWidgetItem *item) { const auto lastAlgHist = wsHist.getAlgorithmHistory(indexOfLast); return lastAlgHist->executionDate(); } +std::size_t MantidTreeWidgetItem::getMemorySize() const { + return this->data(0, Qt::UserRole).value<Workspace_sptr>()->getMemorySize(); +} +std::size_t +MantidTreeWidgetItem::getGroupMemorySize(WorkspaceGroup_sptr group) const { + std::vector<Workspace_sptr> workspaces = group->getAllItems(); + auto total = std::size_t(0); + // Go through each workspace + for (auto workspace : workspaces) { + // If the workspace is a group + if (workspace->getMemorySize() == 0) { + total = + total + getGroupMemorySize( + boost::dynamic_pointer_cast<WorkspaceGroup>(workspace)); + continue; + } + total = total + workspace->getMemorySize(); + } + return total; +} } } diff --git a/qt/widgets/common/src/MuonFitDataSelector.cpp b/qt/widgets/common/src/MuonFitDataSelector.cpp index e65920d8130972544da6ebc337a6d4a0113ea1e9..40be2008d5dd84b4aee7d0da8ebc4328670d2452 100644 --- a/qt/widgets/common/src/MuonFitDataSelector.cpp +++ b/qt/widgets/common/src/MuonFitDataSelector.cpp @@ -3,6 +3,7 @@ #include "MantidQtWidgets/Common/QtPropertyBrowser/qttreepropertybrowser.h" #include "MantidQtWidgets/Common/QtPropertyBrowser/qtpropertymanager.h" +#include <QFileInfo> namespace { Mantid::Kernel::Logger g_log("MuonFitDataSelector"); @@ -356,7 +357,18 @@ QString MuonFitDataSelector::getSimultaneousFitLabel() const { * @param label :: [input] Text to set as label */ void MuonFitDataSelector::setSimultaneousFitLabel(const QString &label) { - m_ui.txtSimFitLabel->setText(label); + // do some checks that it is valid + auto safeLabel = label; + if (label.indexOf(".") >= 0) { + QFileInfo file(label); + safeLabel = file.baseName(); + // trim instrument name + auto index = safeLabel.indexOf("0"); + safeLabel = safeLabel.mid(index); + // trim leading zeros + safeLabel = safeLabel.remove(QRegExp("^[0]*")); + } + m_ui.txtSimFitLabel->setText(safeLabel); } /** diff --git a/qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidget.cpp b/qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidget.cpp index 5b785c9b3f2f3292f0477eb08293c11f91f2f26a..9e08a44e462f97b068dab68b1b3c437ea8753aa5 100644 --- a/qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidget.cpp +++ b/qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidget.cpp @@ -335,6 +335,11 @@ void WorkspaceTreeWidget::chooseByLastModified() { m_presenter->notifyFromView(ViewNotifiable::Flag::SortWorkspaces); } +void WorkspaceTreeWidget::chooseByMemorySize() { + m_sortCriteria = SortCriteria::ByMemorySize; + m_presenter->notifyFromView(ViewNotifiable::Flag::SortWorkspaces); +} + void WorkspaceTreeWidget::excludeItemFromSort(MantidTreeWidgetItem *item) { static int counter = 1; @@ -356,15 +361,29 @@ void WorkspaceTreeWidget::sortWorkspaces(SortCriteria criteria, SortDirection direction) { if (isTreeUpdating()) return; - m_tree->setSortScheme(criteria == SortCriteria::ByName - ? MantidItemSortScheme::ByName - : MantidItemSortScheme::ByLastModified); + m_tree->setSortScheme(whichCriteria(criteria)); m_tree->setSortOrder(direction == SortDirection::Ascending ? Qt::AscendingOrder : Qt::DescendingOrder); m_tree->sort(); } +MantidQt::MantidWidgets::MantidItemSortScheme +WorkspaceTreeWidget::whichCriteria(SortCriteria criteria) { + switch (criteria) { + case SortCriteria::ByName: + return MantidItemSortScheme::ByName; + case SortCriteria::ByLastModified: + return MantidItemSortScheme::ByLastModified; + case SortCriteria::ByMemorySize: + return MantidItemSortScheme::ByMemorySize; + default: + // Handle if someone adds a new Enum and it falls through by defaulting to + // name + return MantidItemSortScheme::ByName; + } +} + void WorkspaceTreeWidget::saveWorkspaceCollection() { m_presenter->notifyFromView(ViewNotifiable::Flag::SaveWorkspaceCollection); } @@ -676,6 +695,7 @@ void WorkspaceTreeWidget::createSortMenuActions() { QAction *m_descendingSortAction = new QAction("Descending", this); QAction *m_byNameChoice = new QAction("Name", this); QAction *m_byLastModifiedChoice = new QAction("Last Modified", this); + QAction *m_byMemorySize = new QAction("Size", this); m_ascendingSortAction->setCheckable(true); m_ascendingSortAction->setEnabled(true); @@ -695,9 +715,13 @@ void WorkspaceTreeWidget::createSortMenuActions() { m_byLastModifiedChoice->setCheckable(true); m_byLastModifiedChoice->setEnabled(true); + m_byMemorySize->setCheckable(true); + m_byMemorySize->setEnabled(true); + m_sortChoiceGroup = new QActionGroup(m_sortMenu); m_sortChoiceGroup->addAction(m_byNameChoice); m_sortChoiceGroup->addAction(m_byLastModifiedChoice); + m_sortChoiceGroup->addAction(m_byMemorySize); m_sortChoiceGroup->setExclusive(true); m_byNameChoice->setChecked(true); @@ -708,6 +732,8 @@ void WorkspaceTreeWidget::createSortMenuActions() { connect(m_byNameChoice, SIGNAL(triggered()), this, SLOT(chooseByName())); connect(m_byLastModifiedChoice, SIGNAL(triggered()), this, SLOT(chooseByLastModified())); + connect(m_byMemorySize, SIGNAL(triggered()), this, + SLOT(chooseByMemorySize())); m_sortMenu->addActions(sortDirectionGroup->actions()); m_sortMenu->addSeparator(); diff --git a/scripts/FilterEvents/eventFilterGUI.py b/scripts/FilterEvents/eventFilterGUI.py index 386fce5c2b7843c02dfaaed9c7707590800ba686..fa31ac99450110b946662140e5cc5bbd796c067b 100644 --- a/scripts/FilterEvents/eventFilterGUI.py +++ b/scripts/FilterEvents/eventFilterGUI.py @@ -806,11 +806,13 @@ class MainWindow(QtGui.QMainWindow): run = dataws.getRun() plist = run.getProperties() for p in plist: - pv = p.value - if isinstance(pv, numpy.ndarray): + try: times = p.times - if len(times) > 1: + if len(times) > 1 and numpy.isreal(p.value[0]): self._sampleLogNames.append(p.name) + # This is here for FloatArrayProperty. If a log value is of this type it does not have times + except AttributeError: + pass # ENDFOR(p) # Set up sample log