diff --git a/CMakeLists.txt b/CMakeLists.txt index c16eafbe0983ce66c9e5ec970403a09e99f44d29..19cf07243f8388421b4a8860649850122f827375 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,10 @@ if (POLICY CMP0072) cmake_policy (SET CMP0072 OLD) endif () +if (POLICY CMP0074) + cmake_policy (SET CMP0074 NEW) +endif () + # System package target is important for the windows builds as it allows us to package only the dlls and exes and exclude libs. Defaults to empty for other platforms. set ( SYSTEM_PACKAGE_TARGET "" ) @@ -367,12 +371,18 @@ if ( ENABLE_CPACK ) "libqscintilla2-12v5," "ipython-notebook," "libhdf5-cpp-11" ) + if (ENABLE_WORKBENCH) + set ( CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5scintilla2-12v5" ) + endif() else() # bionic and newer set ( CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libgsl23," "liboce-foundation11,liboce-modeling11," "libqscintilla2-qt4-13," "jupyter-notebook," "libhdf5-cpp-100" ) + if (ENABLE_WORKBENCH) + set ( CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqscintilla2-qt5-13" ) + endif() endif() if ( PYTHON_VERSION_MAJOR EQUAL 3 ) diff --git a/Framework/API/CMakeLists.txt b/Framework/API/CMakeLists.txt index b66bd694e7850b4771d1e197e0439999fbaf0941..05529388b2621945dceac437546e5087658091a4 100644 --- a/Framework/API/CMakeLists.txt +++ b/Framework/API/CMakeLists.txt @@ -491,9 +491,10 @@ if(UNITY_BUILD) enable_unity_build(API SRC_FILES SRC_UNITY_IGNORE_FILES 10) endif(UNITY_BUILD) -# Have to link to winsock library on Windows +# Have to link to winsock and bcrypt library on Windows if ( WIN32 ) set ( WINSOCK ws2_32 ) + set ( BCRYPT bcrypt ) endif () # Add a precompiled header where they are supported @@ -513,7 +514,7 @@ endif () # Add to the 'Framework' group in VS set_property ( TARGET API PROPERTY FOLDER "MantidFramework" ) -target_link_libraries ( API LINK_PRIVATE ${TCMALLOC_LIBRARIES_LINKTIME} ${JSONCPP_LIBRARIES} ${MANTIDLIBS} ${GSL_LIBRARIES} ${NEXUS_LIBRARIES} ${WINSOCK} ) +target_link_libraries ( API LINK_PRIVATE ${TCMALLOC_LIBRARIES_LINKTIME} ${JSONCPP_LIBRARIES} ${MANTIDLIBS} ${GSL_LIBRARIES} ${NEXUS_LIBRARIES} ${WINSOCK} ${BCRYPT}) # Add the unit tests directory add_subdirectory ( test ) diff --git a/Framework/API/inc/MantidAPI/AlgorithmHistory.h b/Framework/API/inc/MantidAPI/AlgorithmHistory.h index 621c11d630a50c0dbb005850e5352b1af7253a64..78b04c21c1de57618a493758e1ad7931035ea2b3 100644 --- a/Framework/API/inc/MantidAPI/AlgorithmHistory.h +++ b/Framework/API/inc/MantidAPI/AlgorithmHistory.h @@ -27,23 +27,10 @@ class IAlgorithm; class Algorithm; class AlgorithmHistory; -namespace Detail { -// Written as a template in order to get around circular issue of CompareHistory -// needing to know the implementation of AlgorithmHistory and AlgorithmHistory -// needing to know the implementation of CompareHistory. -template <class T> struct CompareHistory { - bool operator()(const boost::shared_ptr<T> &lhs, - const boost::shared_ptr<T> &rhs) const { - return (*lhs) < (*rhs); - } -}; -} // namespace Detail - // typedefs for algorithm history pointers using AlgorithmHistory_sptr = boost::shared_ptr<AlgorithmHistory>; using AlgorithmHistory_const_sptr = boost::shared_ptr<const AlgorithmHistory>; -using AlgorithmHistories = - std::set<AlgorithmHistory_sptr, Detail::CompareHistory<AlgorithmHistory>>; +using AlgorithmHistories = std::vector<AlgorithmHistory_sptr>; /** @class AlgorithmHistory AlgorithmHistory.h API/MAntidAPI/AlgorithmHistory.h @@ -69,7 +56,7 @@ public: ~AlgorithmHistory(); AlgorithmHistory &operator=(const AlgorithmHistory &); AlgorithmHistory(const AlgorithmHistory &); - AlgorithmHistory(const std::string &name, int vers, + AlgorithmHistory(const std::string &name, int vers, std::string uuid, const Types::Core::DateAndTime &start = Types::Core::DateAndTime::getCurrentTime(), const double &duration = -1.0, std::size_t uexeccount = 0); @@ -93,6 +80,8 @@ public: } /// get the execution count const std::size_t &execCount() const { return m_execCount; } + /// get the uuid + const std::string &uuid() const { return m_uuid; } /// get parameter list of algorithm in history const const Mantid::Kernel::PropertyHistories &getProperties() const { return m_properties; @@ -114,7 +103,7 @@ public: const size_t maxPropertyLength = 0) const; /// Less than operator inline bool operator<(const AlgorithmHistory &other) const { - return (execCount() < other.execCount()); + return execCount() < other.execCount(); } /// Equality operator inline bool operator==(const AlgorithmHistory &other) const { @@ -138,7 +127,7 @@ public: private: // private constructor - AlgorithmHistory() = default; + AlgorithmHistory(); // Set properties of algorithm void setProperties(const Algorithm *const alg); /// The name of the Algorithm @@ -155,6 +144,8 @@ private: std::size_t m_execCount{0}; /// set of child algorithm histories for this history record AlgorithmHistories m_childHistories; + /// UUID for this algorithm history + std::string m_uuid; }; MANTID_API_DLL std::ostream &operator<<(std::ostream &, diff --git a/Framework/API/inc/MantidAPI/SpectrumInfo.h b/Framework/API/inc/MantidAPI/SpectrumInfo.h index 5acea67bcbdc2d42c50df22a0162a427326e4269..510cac4a0180c050e314302988e2b1a9b5a7f156 100644 --- a/Framework/API/inc/MantidAPI/SpectrumInfo.h +++ b/Framework/API/inc/MantidAPI/SpectrumInfo.h @@ -8,6 +8,7 @@ #define MANTID_API_SPECTRUMINFO_H_ #include "MantidAPI/DllConfig.h" +#include "MantidAPI/SpectrumInfoIterator.h" #include "MantidKernel/V3D.h" #include "MantidKernel/cow_ptr.h" @@ -28,7 +29,6 @@ class Instrument; class ParameterMap; } // namespace Geometry namespace API { -class SpectrumInfoIterator; class ExperimentInfo; /** API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is @@ -84,8 +84,10 @@ public: Kernel::V3D samplePosition() const; double l1() const; - SpectrumInfoIterator begin() const; - SpectrumInfoIterator end() const; + SpectrumInfoIterator<SpectrumInfo> begin(); + SpectrumInfoIterator<SpectrumInfo> end(); + const SpectrumInfoIterator<const SpectrumInfo> cbegin() const; + const SpectrumInfoIterator<const SpectrumInfo> cend() const; friend class ExperimentInfo; @@ -102,6 +104,9 @@ private: mutable std::vector<size_t> m_lastIndex; }; +using SpectrumInfoIt = SpectrumInfoIterator<SpectrumInfo>; +using SpectrumInfoConstIt = SpectrumInfoIterator<const SpectrumInfo>; + } // namespace API } // namespace Mantid diff --git a/Framework/API/inc/MantidAPI/SpectrumInfoItem.h b/Framework/API/inc/MantidAPI/SpectrumInfoItem.h index 57ba067d2830614aacaee4fec2e10bcce334a599..489cf2c59748d9e76b6849c8ee9eba20348cb81f 100644 --- a/Framework/API/inc/MantidAPI/SpectrumInfoItem.h +++ b/Framework/API/inc/MantidAPI/SpectrumInfoItem.h @@ -7,11 +7,11 @@ #ifndef MANTID_API_SPECTRUMINFOITEM_H_ #define MANTID_API_SPECTRUMINFOITEM_H_ -#include "MantidAPI/SpectrumInfo.h" +#include "MantidAPI/DllConfig.h" #include "MantidKernel/V3D.h" #include "MantidTypes/SpectrumDefinition.h" +#include <type_traits> -using Mantid::API::SpectrumInfo; using Mantid::Kernel::V3D; using Mantid::SpectrumDefinition; @@ -35,8 +35,7 @@ methods include: @author Bhuvan Bezawada, STFC @date 2018 */ - -class MANTID_API_DLL SpectrumInfoItem { +template <typename T> class SpectrumInfoItem { public: // Methods that can be called via the iterator @@ -44,6 +43,11 @@ public: bool isMasked() const { return m_spectrumInfo->isMasked(m_index); } + void setMasked(bool masked) { + static_assert(!std::is_const<T>::value, "Operation disabled on const T"); + return m_spectrumInfo->setMasked(m_index, masked); + } + double twoTheta() const { return m_spectrumInfo->twoTheta(m_index); } double signedTwoTheta() const { @@ -64,17 +68,12 @@ public: return m_spectrumInfo->position(m_index); } -private: - // Allow SpectrumInfoIterator access - friend class SpectrumInfoIterator; - - // Private constructor, can only be created by SpectrumInfoIterator - SpectrumInfoItem(const SpectrumInfo &spectrumInfo, const size_t index) + SpectrumInfoItem(T &spectrumInfo, const size_t index) : m_spectrumInfo(&spectrumInfo), m_index(index) {} // Non-owning pointer. A reference makes the class unable to define an // assignment operator that we need. - const SpectrumInfo *m_spectrumInfo; + T *m_spectrumInfo; size_t m_index; }; diff --git a/Framework/API/inc/MantidAPI/SpectrumInfoIterator.h b/Framework/API/inc/MantidAPI/SpectrumInfoIterator.h index 0c1e30e154e9245bfe11aa24b1e83f0ad18f883a..6cb8b133be0359861e5f16610ea90c18d21e1295 100644 --- a/Framework/API/inc/MantidAPI/SpectrumInfoIterator.h +++ b/Framework/API/inc/MantidAPI/SpectrumInfoIterator.h @@ -8,7 +8,6 @@ #define MANTID_API_SPECTRUMINFOITERATOR_H_ #include "MantidAPI/SpectrumInfoItem.h" - #include <boost/iterator/iterator_facade.hpp> using Mantid::API::SpectrumInfoItem; @@ -27,13 +26,14 @@ iterator. @date 2018 */ -class MANTID_API_DLL SpectrumInfoIterator - : public boost::iterator_facade<SpectrumInfoIterator, - const SpectrumInfoItem &, +template <typename T> +class SpectrumInfoIterator + : public boost::iterator_facade<SpectrumInfoIterator<T>, + SpectrumInfoItem<T> &, boost::random_access_traversal_tag> { public: - SpectrumInfoIterator(const SpectrumInfo &spectrumInfo, const size_t index) + SpectrumInfoIterator(T &spectrumInfo, const size_t index) : m_item(spectrumInfo, index) {} private: @@ -69,18 +69,18 @@ private: void setIndex(const size_t index) { m_item.m_index = index; } - uint64_t distance_to(const SpectrumInfoIterator &other) const { + uint64_t distance_to(const SpectrumInfoIterator<T> &other) const { return static_cast<uint64_t>(other.getIndex()) - static_cast<uint64_t>(getIndex()); } - bool equal(const SpectrumInfoIterator &other) const { + bool equal(const SpectrumInfoIterator<T> &other) const { return getIndex() == other.getIndex(); } - const SpectrumInfoItem &dereference() const { return m_item; } + SpectrumInfoItem<T> &dereference() const { return m_item; } - SpectrumInfoItem m_item; + mutable SpectrumInfoItem<T> m_item; }; } // namespace API diff --git a/Framework/API/src/AlgorithmHistory.cpp b/Framework/API/src/AlgorithmHistory.cpp index 519810cbe45a846d4e17e82751f9b2f0020a11f6..d5ae137a744bf94fa4d1540020fc23c7931eed9e 100644 --- a/Framework/API/src/AlgorithmHistory.cpp +++ b/Framework/API/src/AlgorithmHistory.cpp @@ -9,6 +9,9 @@ //---------------------------------------------------------------------- #include "MantidAPI/AlgorithmHistory.h" #include "MantidAPI/Algorithm.h" +#include <boost/uuid/uuid.hpp> +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> #include <sstream> namespace Mantid { @@ -21,6 +24,11 @@ using Kernel::PropertyHistory_const_sptr; using Kernel::PropertyHistory_sptr; using Types::Core::DateAndTime; +namespace { +/// The generator for algorithm history UUIDs +static boost::uuids::random_generator uuidGen; +} // namespace + /** Constructor * @param alg :: A pointer to the algorithm for which the history should * be constructed @@ -39,6 +47,12 @@ AlgorithmHistory::AlgorithmHistory(const Algorithm *const alg, // Now go through the algorithm's properties and create the PropertyHistory // objects. setProperties(alg); + m_uuid = boost::uuids::to_string(uuidGen()); +} + +/// Default constructor +AlgorithmHistory::AlgorithmHistory() { + m_uuid = boost::uuids::to_string(uuidGen()); } /// Destructor @@ -49,18 +63,21 @@ AlgorithmHistory::~AlgorithmHistory() = default; from saved records. @param name :: The algorithm name. @param vers :: The algorithm version. + @param uuid :: The universally unique id assigned to this alghistory on + creation during assignment to history. @param start :: The start time of the algorithm execution (optional). @param duration :: The time (in seconds) that it took to run this algorithm (optional). @param uexeccount :: an unsigned int for algorithm execution order */ AlgorithmHistory::AlgorithmHistory(const std::string &name, int vers, + std::string uuid, const Types::Core::DateAndTime &start, const double &duration, std::size_t uexeccount) : m_name(name), m_version(vers), m_executionDate(start), m_executionDuration(duration), m_execCount(uexeccount), - m_childHistories() {} + m_childHistories(), m_uuid(uuid) {} /** * Set the history properties for an algorithm pointer @@ -107,7 +124,7 @@ AlgorithmHistory::AlgorithmHistory(const AlgorithmHistory &A) : m_name(A.m_name), m_version(A.m_version), m_executionDate(A.m_executionDate), m_executionDuration(A.m_executionDuration), m_properties(A.m_properties), - m_execCount(A.m_execCount) { + m_execCount(A.m_execCount), m_uuid(A.m_uuid) { m_childHistories = A.m_childHistories; } @@ -143,7 +160,7 @@ void AlgorithmHistory::addChildHistory(AlgorithmHistory_sptr childHist) { return; } - m_childHistories.insert(childHist); + m_childHistories.emplace_back(childHist); } /* @@ -214,14 +231,15 @@ AlgorithmHistory::getChildAlgorithm(const size_t index) const { */ void AlgorithmHistory::printSelf(std::ostream &os, const int indent, const size_t maxPropertyLength) const { + auto execDate = m_executionDate.toISO8601String(); + execDate.replace(execDate.find("T"), 1, " "); os << std::string(indent, ' ') << "Algorithm: " << m_name; os << std::string(indent, ' ') << " v" << m_version << '\n'; - os << std::string(indent, ' ') - << "Execution Date: " << m_executionDate.toFormattedString() << '\n'; + os << std::string(indent, ' ') << "Execution Date: " << execDate << '\n'; os << std::string(indent, ' ') << "Execution Duration: " << m_executionDuration << " seconds\n"; - + os << std::string(indent, ' ') << "UUID: " << m_uuid << '\n'; os << std::string(indent, ' ') << "Parameters:\n"; for (const auto &property : m_properties) { @@ -252,6 +270,7 @@ AlgorithmHistory &AlgorithmHistory::operator=(const AlgorithmHistory &A) { // to an ancestor auto temp = A.m_childHistories; m_childHistories = temp; + m_uuid = A.m_uuid; } return *this; } @@ -290,6 +309,5 @@ void AlgorithmHistory::saveNexus(::NeXus::File *file, int &algCount) const { } file->closeGroup(); } - } // namespace API } // namespace Mantid diff --git a/Framework/API/src/ScriptBuilder.cpp b/Framework/API/src/ScriptBuilder.cpp index 78b1b98d2278c7986dfe109be0ee4f019c3550b9..d574b27246956b925e85832a1b038873da970ae7 100644 --- a/Framework/API/src/ScriptBuilder.cpp +++ b/Framework/API/src/ScriptBuilder.cpp @@ -264,8 +264,11 @@ const std::string ScriptBuilder::buildPropertyString( 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'; + // If algs are to be ignored (Common use case is project recovery) ignore + if (m_algsToIgnore.size() == 0) { + 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(); diff --git a/Framework/API/src/SpectrumInfo.cpp b/Framework/API/src/SpectrumInfo.cpp index 6e135bb5b3609635b6626c9df3e9d16d28284969..3aab438e6157554b389c6b878ee64d7823423ace 100644 --- a/Framework/API/src/SpectrumInfo.cpp +++ b/Framework/API/src/SpectrumInfo.cpp @@ -190,13 +190,18 @@ SpectrumInfo::checkAndGetSpectrumDefinition(const size_t index) const { } // Begin method for iterator -SpectrumInfoIterator SpectrumInfo::begin() const { - return SpectrumInfoIterator(*this, 0); +SpectrumInfoIt SpectrumInfo::begin() { return SpectrumInfoIt(*this, 0); } + +// End method for iterator +SpectrumInfoIt SpectrumInfo::end() { return SpectrumInfoIt(*this, size()); } + +const SpectrumInfoConstIt SpectrumInfo::cbegin() const { + return SpectrumInfoConstIt(*this, 0); } // End method for iterator -SpectrumInfoIterator SpectrumInfo::end() const { - return SpectrumInfoIterator(*this, size()); +const SpectrumInfoConstIt SpectrumInfo::cend() const { + return SpectrumInfoConstIt(*this, size()); } } // namespace API diff --git a/Framework/API/src/WorkspaceHistory.cpp b/Framework/API/src/WorkspaceHistory.cpp index 0b9d4a99ac4361a6af4380bc23950ca3d3db30c4..46bda66cdc66ca7b7831040eb0dbb1bb343be723 100644 --- a/Framework/API/src/WorkspaceHistory.cpp +++ b/Framework/API/src/WorkspaceHistory.cpp @@ -11,9 +11,14 @@ #include "MantidKernel/EnvironmentHistory.h" #include "MantidKernel/StringTokenizer.h" #include "MantidKernel/Strings.h" +#include "MantidTypes/Core/DateAndTime.h" #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> +#include <boost/functional/hash.hpp> +#include <boost/uuid/uuid.hpp> +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> #include "Poco/DateTime.h" #include <Poco/DateTimeParser.h> @@ -26,6 +31,25 @@ namespace API { namespace { /// static logger object Kernel::Logger g_log("WorkspaceHistory"); +struct AlgorithmHistorySearch { + bool operator()(const AlgorithmHistory_sptr &lhs, + const AlgorithmHistory_sptr &rhs) { + return (*lhs) < (*rhs); + } +}; +struct AlgorithmHistoryHasher { + size_t operator()(const AlgorithmHistory_sptr &x) const { + std::size_t nameAsSeed = std::hash<std::string>{}(x->name()); + boost::hash_combine(nameAsSeed, x->executionDate().totalNanoseconds()); + return nameAsSeed; + } +}; +struct AlgorithmHistoryComparator { + bool operator()(const AlgorithmHistory_sptr &a, + const AlgorithmHistory_sptr &b) const { + return a->uuid() == b->uuid(); + } +}; } // namespace /// Default Constructor @@ -64,12 +88,25 @@ void WorkspaceHistory::addHistory(const WorkspaceHistory &otherHistory) { // Merge the histories const AlgorithmHistories &otherAlgorithms = otherHistory.getAlgorithmHistories(); - m_algorithms.insert(otherAlgorithms.begin(), otherAlgorithms.end()); + + for (const auto &algHistory : otherAlgorithms) { + this->addHistory(algHistory); + } + + std::unordered_set<AlgorithmHistory_sptr, AlgorithmHistoryHasher, + AlgorithmHistoryComparator> + set; + for (auto i : m_algorithms) + set.insert(i); + m_algorithms.assign(set.begin(), set.end()); + std::sort(m_algorithms.begin(), m_algorithms.end(), AlgorithmHistorySearch()); } /// Append an AlgorithmHistory to this WorkspaceHistory void WorkspaceHistory::addHistory(AlgorithmHistory_sptr algHistory) { - m_algorithms.insert(std::move(algHistory)); + // Assume it is always sorted as algorithm history should only be inserted in + // the correct order + m_algorithms.emplace_back(std::move(algHistory)); } /* @@ -336,14 +373,16 @@ WorkspaceHistory::parseAlgorithmHistory(const std::string &rawData) { NAME = 0, //< algorithms name EXEC_TIME = 1, //< when the algorithm was run EXEC_DUR = 2, //< execution time for the algorithm - PARAMS = 3 //< the algorithm's parameters + UUID = 3, //< the universal unique id of the algorithm + PARAMS = 4 //< the algorithm's parameters }; std::vector<std::string> info; boost::split(info, rawData, boost::is_any_of("\n")); const size_t nlines = info.size(); - if (nlines < 4) { // ignore badly formed history entries + if (nlines < 4) { // ignore badly formed history entries still at 4 so that + // legacy files can be loaded, 5 is ideal for newer files throw std::runtime_error( "Malformed history record: Incorrect record size."); } @@ -362,16 +401,24 @@ WorkspaceHistory::parseAlgorithmHistory(const std::string &rawData) { // Get the execution date/time std::string date, time; getWordsInString(info[EXEC_TIME], dummy, dummy, date, time); - Poco::DateTime start_timedate; - // This is needed by the Poco parsing function - int tzdiff(-1); Mantid::Types::Core::DateAndTime utc_start; - if (!Poco::DateTimeParser::tryParse("%Y-%b-%d %H:%M:%S", date + " " + time, - start_timedate, tzdiff)) { - g_log.warning() << "Error parsing start time in algorithm history entry." - << "\n"; - utc_start = Types::Core::DateAndTime::defaultTime(); + // If not legacy version construct normally else Parse in the legacy data + if (std::isdigit(date[6])) { + Mantid::Types::Core::DateAndTime timeConstruction(date + "T" + time); + utc_start = timeConstruction; + } else { + Poco::DateTime start_timedate; + // This is needed by the Poco parsing function + int tzdiff(-1); + if (!Poco::DateTimeParser::tryParse("%Y-%b-%d %H:%M:%S", date + " " + time, + start_timedate, tzdiff)) { + g_log.warning() << "Error parsing start time in algorithm history entry." + << "\n"; + utc_start = Types::Core::DateAndTime::defaultTime(); + } + utc_start.set_from_time_t(start_timedate.timestamp().epochTime()); } + // Get the duration getWordsInString(info[EXEC_DUR], dummy, dummy, temp, dummy); double dur = boost::lexical_cast<double>(temp); @@ -380,16 +427,28 @@ WorkspaceHistory::parseAlgorithmHistory(const std::string &rawData) { << "\n"; dur = -1.0; } - // Convert the timestamp to time_t to DateAndTime - utc_start.set_from_time_t(start_timedate.timestamp().epochTime()); + + /// To allow legacy files we must check if it is parameters and set the + /// variables accordingly. If legacy generate a new UUID for it. + std::string uuid; + size_t paramNum; + if (info[3] != "Parameters:") { + uuid = info[UUID]; + uuid.erase(uuid.find("UUID: "), 6); + paramNum = PARAMS; + } else { + uuid = boost::uuids::to_string(boost::uuids::random_generator()()); + paramNum = 3; + } + // Create the algorithm history - API::AlgorithmHistory alg_hist(algName, version, utc_start, dur, + API::AlgorithmHistory alg_hist(algName, version, uuid, utc_start, dur, Algorithm::g_execCount); // Simulate running an algorithm ++Algorithm::g_execCount; // Add property information - for (size_t index = static_cast<size_t>(PARAMS) + 1; index < nlines; + for (size_t index = static_cast<size_t>(paramNum) + 1; index < nlines; ++index) { const std::string line = info[index]; std::string::size_type colon = line.find(':'); @@ -431,6 +490,5 @@ std::ostream &operator<<(std::ostream &os, const WorkspaceHistory &WH) { WH.printSelf(os); return os; } - } // namespace API } // namespace Mantid diff --git a/Framework/API/test/AlgorithmHistoryTest.h b/Framework/API/test/AlgorithmHistoryTest.h index 08d762d8e15bcb72f6543a23567e137b84085034..69d391a3554615b6f73460a0251b45015fbb2d19 100644 --- a/Framework/API/test/AlgorithmHistoryTest.h +++ b/Framework/API/test/AlgorithmHistoryTest.h @@ -49,7 +49,13 @@ public: std::ostringstream output; output.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT_THROWS_NOTHING(output << AH); - TS_ASSERT_EQUALS(output.str(), m_correctOutput); + + // Remove UUID line from output + std::string outputStr = output.str(); + // 44 is the expected length of the UUID string + outputStr.erase(outputStr.find("UUID: "), 43); + + TS_ASSERT_EQUALS(outputStr, m_correctOutput); // Does it equal itself TS_ASSERT_EQUALS(AH, AH); } @@ -114,7 +120,13 @@ public: std::ostringstream output; output.exceptions(std::ios::failbit | std::ios::badbit); TS_ASSERT_THROWS_NOTHING(output << algHist); - TS_ASSERT_EQUALS(output.str(), m_correctOutput); + + // Remove UUID line from output + std::string outputStr = output.str(); + // 44 is the expected length of the UUID string + outputStr.erase(outputStr.find("UUID: "), 43); + + TS_ASSERT_EQUALS(outputStr, m_correctOutput); auto children = algHist.getChildHistories(); TS_ASSERT_EQUALS(children.size(), 3); @@ -183,8 +195,10 @@ private: AlgorithmHistory createTestHistory() { m_correctOutput = "Algorithm: testalg "; m_correctOutput += "v1\n"; - m_correctOutput += "Execution Date: 2008-Feb-29 09:54:49\n"; + m_correctOutput += "Execution Date: 2008-02-29 09:54:49\n"; m_correctOutput += "Execution Duration: 14 seconds\n"; + // This line is newer than the rest and cannot be predicted + // m_correctOutput += "UUID: 207ca8f8-fee0-49ce-86c8-7842a7313c2e\n"; m_correctOutput += "Parameters:\n"; m_correctOutput += " Name: arg1_param, "; m_correctOutput += "Value: y, "; diff --git a/Framework/API/test/CMakeLists.txt b/Framework/API/test/CMakeLists.txt index 5bfdb4dd813cccfdb6304fd945092e7e8ed85ac0..180ace293300b445be06c9de8d349522e5e3b7be 100644 --- a/Framework/API/test/CMakeLists.txt +++ b/Framework/API/test/CMakeLists.txt @@ -13,7 +13,10 @@ if ( CXXTEST_FOUND ) ../../TestHelpers/src/ParallelRunner.cpp ) cxxtest_add_test ( APITest ${TEST_FILES} ${GMOCK_TEST_FILES}) - target_link_libraries( APITest LINK_PRIVATE ${TCMALLOC_LIBRARIES_LINKTIME} ${MANTIDLIBS} + if ( WIN32 ) + set ( BCRYPT bcrypt ) + endif () + target_link_libraries( APITest LINK_PRIVATE ${TCMALLOC_LIBRARIES_LINKTIME} ${MANTIDLIBS} ${BCRYPT} Types API Nexus diff --git a/Framework/API/test/HistoryItemTest.h b/Framework/API/test/HistoryItemTest.h index 0fff1f92b7ea55013589482a1022ad3c7007fa31..1d740a141e25f040774b1439fbe8833b769df1ba 100644 --- a/Framework/API/test/HistoryItemTest.h +++ b/Framework/API/test/HistoryItemTest.h @@ -9,6 +9,9 @@ #include "MantidAPI/Algorithm.h" #include "MantidAPI/HistoryView.h" +#include <boost/uuid/uuid.hpp> +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> #include <cxxtest/TestSuite.h> using namespace Mantid::API; @@ -19,7 +22,9 @@ class HistoryItemTest : public CxxTest::TestSuite { public: void test_Minimum() { // not really much to test - AlgorithmHistory algHist("AnAlg", 1); + AlgorithmHistory algHist( + "AnAlg", 1, + boost::uuids::to_string(boost::uuids::random_generator()())); HistoryItem item(boost::make_shared<AlgorithmHistory>(algHist)); item.unrolled(true); diff --git a/Framework/API/test/SpectrumInfoTest.h b/Framework/API/test/SpectrumInfoTest.h index bd965373c41888847ebc2098b071efdf88c29577..a78911299f3852592c39eec1f5c3ccabc95e5a77 100644 --- a/Framework/API/test/SpectrumInfoTest.h +++ b/Framework/API/test/SpectrumInfoTest.h @@ -497,28 +497,28 @@ public: void test_iterator_begin() { // Get the SpectrumInfo object const auto &spectrumInfo = m_workspace.spectrumInfo(); - auto iter = spectrumInfo.begin(); + auto iter = spectrumInfo.cbegin(); // Check we start at the correct place - TS_ASSERT(iter != spectrumInfo.end()); + TS_ASSERT(iter != spectrumInfo.cend()); } void test_iterator_end() { // Get the SpectrumInfo object const auto &spectrumInfo = m_workspace.spectrumInfo(); - auto iter = spectrumInfo.end(); + auto iter = spectrumInfo.cend(); // Check we start at the correct place - TS_ASSERT(iter != spectrumInfo.begin()); + TS_ASSERT(iter != spectrumInfo.cbegin()); } void test_iterator_increment_and_hasUniqueDetector() { // Get the SpectrumInfo object const auto &spectrumInfo = m_workspace.spectrumInfo(); - auto iter = spectrumInfo.begin(); + auto iter = spectrumInfo.cbegin(); // Check that we start at the beginning - TS_ASSERT(iter == spectrumInfo.begin()); + TS_ASSERT(iter == spectrumInfo.cbegin()); // Increment iterator and check hasUniqueDetector for (size_t i = 0; i < m_workspace.spectrumInfo().size(); ++i) { @@ -527,16 +527,16 @@ public: } // Check we've reached the end - TS_ASSERT(iter == spectrumInfo.end()); + TS_ASSERT(iter == spectrumInfo.cend()); } void test_iterator_decrement_and_hasUniqueDetector() { // Get the SpectrumInfo object const auto &spectrumInfo = m_workspace.spectrumInfo(); - auto iter = spectrumInfo.end(); + auto iter = spectrumInfo.cend(); // Check that we start at the end - TS_ASSERT(iter == spectrumInfo.end()); + TS_ASSERT(iter == spectrumInfo.cend()); // Decrement iterator and check hasUniqueDetector for (size_t i = m_workspace.spectrumInfo().size(); i > 0; --i) { @@ -545,13 +545,13 @@ public: } // Check we've reached the beginning - TS_ASSERT(iter == spectrumInfo.begin()); + TS_ASSERT(iter == spectrumInfo.cbegin()); } void test_iterator_advance_and_hasUniqueDetector() { // Get the SpectrumInfo object const auto &spectrumInfo = m_workspace.spectrumInfo(); - auto iter = spectrumInfo.begin(); + auto iter = spectrumInfo.cbegin(); // Advance 3 places std::advance(iter, 3); @@ -563,16 +563,16 @@ public: // Go to the start std::advance(iter, -1); - TS_ASSERT(iter == spectrumInfo.begin()); + TS_ASSERT(iter == spectrumInfo.cbegin()); } void test_copy_iterator_and_hasUniqueDetector() { // Get the SpectrumInfo object const auto &spectrumInfo = m_workspace.spectrumInfo(); - auto iter = spectrumInfo.begin(); + auto iter = spectrumInfo.cbegin(); // Create a copy - auto iterCopy = SpectrumInfoIterator(iter); + auto iterCopy = SpectrumInfoConstIt(iter); // Check TS_ASSERT_EQUALS(iter->hasUniqueDetector(), true); @@ -587,6 +587,14 @@ public: TS_ASSERT_EQUALS(iterCopy->hasUniqueDetector(), true); } + void test_mutating_via_writable_iterator() { + auto &spectrumInfo = m_workspace.mutableSpectrumInfo(); + auto it = spectrumInfo.begin(); + + it->setMasked(true); + TS_ASSERT(spectrumInfo.cbegin()->isMasked() == true); + } + private: WorkspaceTester m_workspace; WorkspaceTester m_workspaceNoInstrument; diff --git a/Framework/API/test/WorkspaceHistoryIOTest.h b/Framework/API/test/WorkspaceHistoryIOTest.h index 44a3d491695b898b0f8446626ab62c143e3f96e3..ba913301de704e83d39cf215158074a1bbca44c6 100644 --- a/Framework/API/test/WorkspaceHistoryIOTest.h +++ b/Framework/API/test/WorkspaceHistoryIOTest.h @@ -15,6 +15,9 @@ #include "MantidKernel/Property.h" #include "MantidTestHelpers/NexusTestHelper.h" #include "Poco/File.h" +#include <boost/uuid/uuid.hpp> +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> #include <cxxtest/TestSuite.h> using namespace Mantid::API; @@ -211,8 +214,10 @@ public: void test_SaveNexus() { WorkspaceHistory testHistory; for (int i = 1; i < 5; i++) { - AlgorithmHistory algHist("History" + boost::lexical_cast<std::string>(i), - 1, DateAndTime::defaultTime(), -1.0, i); + AlgorithmHistory algHist( + "History" + boost::lexical_cast<std::string>(i), 1, + boost::uuids::to_string(boost::uuids::random_generator()()), + DateAndTime::defaultTime(), -1.0, i); testHistory.addHistory(boost::make_shared<AlgorithmHistory>(algHist)); } @@ -248,10 +253,14 @@ public: void test_SaveNexus_NestedHistory() { WorkspaceHistory testHistory; - AlgorithmHistory algHist("ParentHistory", 1, DateAndTime::defaultTime(), - -1.0, 0); - AlgorithmHistory childHist("ChildHistory", 1, DateAndTime::defaultTime(), - -1.0, 1); + AlgorithmHistory algHist( + "ParentHistory", 1, + boost::uuids::to_string(boost::uuids::random_generator()()), + DateAndTime::defaultTime(), -1.0, 0); + AlgorithmHistory childHist( + "ChildHistory", 1, + boost::uuids::to_string(boost::uuids::random_generator()()), + DateAndTime::defaultTime(), -1.0, 1); algHist.addChildHistory(boost::make_shared<AlgorithmHistory>(childHist)); testHistory.addHistory(boost::make_shared<AlgorithmHistory>(algHist)); diff --git a/Framework/API/test/WorkspaceHistoryTest.h b/Framework/API/test/WorkspaceHistoryTest.h index 89944bd693d915901506605db7d5bcb28aef1703..f56b2a1d37605554870510aef46e330c4f13dba9 100644 --- a/Framework/API/test/WorkspaceHistoryTest.h +++ b/Framework/API/test/WorkspaceHistoryTest.h @@ -76,7 +76,8 @@ public: TS_ASSERT_EQUALS(history.size(), 0); TS_ASSERT_EQUALS(history.empty(), true); - AlgorithmHistory alg1("FirstAlgorithm", 2); + AlgorithmHistory alg1("FirstAlgorithm", 2, + "207ca8f8-fee0-49ce-86c8-7842a7313c2e"); alg1.addProperty("FirstAlgProperty", "1", false, Mantid::Kernel::Direction::Input); @@ -139,10 +140,18 @@ public: class WorkspaceHistoryTestPerformance : public CxxTest::TestSuite { public: + WorkspaceHistoryTestPerformance() { + constructAlgHistories1(); + constructAlgHistories2(); + } + + void setUp() override { m_wsHist.clearHistory(); } + void test_Wide_History() { int depth = 3; int width = 50; - auto algHist = boost::make_shared<AlgorithmHistory>("AnAlgorithm", 1); + auto algHist = boost::make_shared<AlgorithmHistory>( + "AnAlgorithm", 1, "207ca8f8-fee0-49ce-86c8-7842a7313c2e"); build_Algorithm_History(*algHist, width, depth); m_wsHist.addHistory(std::move(algHist)); } @@ -151,24 +160,73 @@ public: int depth = 10; int width = 3; - auto algHist = boost::make_shared<AlgorithmHistory>("AnAlgorithm", 1); + auto algHist = boost::make_shared<AlgorithmHistory>( + "AnAlgorithm", 1, "207ca8f8-fee0-49ce-86c8-7842a7313c2e"); build_Algorithm_History(*algHist, width, depth); m_wsHist.addHistory(std::move(algHist)); } + void test_standard_insertion_500000_times() { + for (auto i = 0u; i < 500000; ++i) { + m_wsHist.addHistory(m_1000000Histories1[i]); + } + } + + void test_standard_insertion_1000000_times() { + for (auto i = 0u; i < 1000000; ++i) { + m_wsHist.addHistory(m_1000000Histories1[i]); + } + } + + void test_adding_1000000_to_500000_workspace_histories() { + // It's hard to test this without doing this bit + for (auto i = 0u; i < 500000; ++i) { + m_wsHist.addHistory(m_1000000Histories1[i]); + } + // The actual test + m_wsHist.addHistory(m_1000000Histories2); + } + + void test_adding_1000000_to_1000000_workspace_histories() { + // It's hard to test this without doing this bit + for (auto i = 0u; i < 1000000; ++i) { + m_wsHist.addHistory(m_1000000Histories1[i]); + } + // The actual test + m_wsHist.addHistory(m_1000000Histories2); + } + +private: void build_Algorithm_History(AlgorithmHistory &parent, int width, int depth = 0) { if (depth > 0) { for (int i = 0; i < width; ++i) { - auto algHist = boost::make_shared<AlgorithmHistory>("AnAlgorithm", 1); + auto algHist = boost::make_shared<AlgorithmHistory>( + "AnAlgorithm", 1, "207ca8f8-fee0-49ce-86c8-7842a7313c2e"); build_Algorithm_History(*algHist, width, depth - 1); parent.addChildHistory(std::move(algHist)); } } } -private: + void constructAlgHistories1() { + for (auto i = 1u; i < 1000001; ++i) { + auto algHist = boost::make_shared<AlgorithmHistory>( + "AnAlgorithm", i, "207ca8f8-fee0-49ce-86c8-7842a7313c2e"); + m_1000000Histories1.emplace_back(std::move(algHist)); + } + } + void constructAlgHistories2() { + for (auto i = 1000001u; i < 1000001; ++i) { + auto algHist = boost::make_shared<AlgorithmHistory>( + "AnAlgorithm", i, "207ca8f8-fee0-49ce-86c8-7842a7313c2e"); + m_1000000Histories2.addHistory(std::move(algHist)); + } + } + Mantid::API::WorkspaceHistory m_wsHist; + std::vector<AlgorithmHistory_sptr> m_1000000Histories1; + WorkspaceHistory m_1000000Histories2; }; #endif diff --git a/Framework/Algorithms/test/GeneratePythonScriptTest.h b/Framework/Algorithms/test/GeneratePythonScriptTest.h index 43f7cf86824e933955a27cbad2daf2572551bd82..72f0212221a5d47c6ac932c1653a3ed6ae03e2c6 100644 --- a/Framework/Algorithms/test/GeneratePythonScriptTest.h +++ b/Framework/Algorithms/test/GeneratePythonScriptTest.h @@ -84,7 +84,6 @@ public: "#Python Script Generated by GeneratePythonScript Algorithm", "#####################################################################" "#", - "NonExistingAlgorithm()", "CreateWorkspace(OutputWorkspace='" "testGeneratePython', DataX='1,2,3,5,6', " "DataY='7,9,16,4,3', DataE='2,3,4,2,1', " @@ -93,6 +92,7 @@ public: "OutputWorkspace='testGeneratePython', XMin=2, XMax=5)", "Power(InputWorkspace='testGeneratePython', " "OutputWorkspace='testGeneratePython', Exponent=1.5)", + "NonExistingAlgorithm()", ""}; // Set up and execute the algorithm. @@ -121,8 +121,8 @@ public: // Verify that if we set the content of ScriptText that it is set correctly. alg.setPropertyValue("ScriptText", result[5]); TS_ASSERT_EQUALS(alg.getPropertyValue("ScriptText"), - "CropWorkspace(InputWorkspace='testGeneratePython', " - "OutputWorkspace='testGeneratePython', XMin=2, XMax=5)"); + "Power(InputWorkspace='testGeneratePython', " + "OutputWorkspace='testGeneratePython', Exponent=1.5)"); file.close(); if (Poco::File(filename).exists()) @@ -177,21 +177,6 @@ public: pAlg.reset(nullptr); } - - MatrixWorkspace_sptr createSimpleWorkspace(const std::vector<double> xData, - const std::vector<double> yData, - const std::vector<double> eData, - const int nSpec = 1) { - Workspace2D_sptr outputWorkspace = create<DataObjects::Workspace2D>( - nSpec, Mantid::HistogramData::Histogram( - Mantid::HistogramData::Points(xData.size()))); - for (int i = 0; i < nSpec; ++i) { - outputWorkspace->mutableY(i) = yData; - outputWorkspace->mutableE(i) = eData; - outputWorkspace->mutableX(i) = xData; - } - return outputWorkspace; - } }; #endif /* MANTID_ALGORITHMS_GENERATEPYTHONSCRIPTTEST_H_ */ diff --git a/Framework/Algorithms/test/LineProfileTest.h b/Framework/Algorithms/test/LineProfileTest.h index 2fc6d8c54b07991622dd9a6bae51d980ada6ac12..740da3e737f85b47cb6563b78c1a6a39a4fac6b4 100644 --- a/Framework/Algorithms/test/LineProfileTest.h +++ b/Framework/Algorithms/test/LineProfileTest.h @@ -18,6 +18,10 @@ #include "MantidDataObjects/WorkspaceCreation.h" #include "MantidTestHelpers/WorkspaceCreationHelper.h" +#include <boost/uuid/uuid.hpp> +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> + using Mantid::Algorithms::CompareWorkspaces; using Mantid::Algorithms::LineProfile; using namespace Mantid::API; @@ -409,7 +413,8 @@ public: MatrixWorkspace_sptr inputWS = create2DWorkspace154(nHist, nBins); auto &oldHistory = inputWS->history(); auto historyEntry = boost::make_shared<AlgorithmHistory>( - "LineProfileTestDummyAlgorithmName", 1); + "LineProfileTestDummyAlgorithmName", 1, + boost::uuids::to_string(boost::uuids::random_generator()())); oldHistory.addHistory(historyEntry); const int start = 2; const int end = nBins - 2; diff --git a/Framework/Catalog/inc/MantidCatalog/OAuth.h b/Framework/Catalog/inc/MantidCatalog/OAuth.h index 9e4f91852942faf5279f69d0e21242ca4ab38d61..15d2b84a1144f1e72acbd8588f258ba73b57155d 100644 --- a/Framework/Catalog/inc/MantidCatalog/OAuth.h +++ b/Framework/Catalog/inc/MantidCatalog/OAuth.h @@ -66,6 +66,7 @@ class MANTID_CATALOG_DLL IOAuthTokenStore { public: virtual void setToken(const boost::optional<OAuthToken> &token) = 0; virtual boost::optional<OAuthToken> getToken() = 0; + virtual ~IOAuthTokenStore() = default; }; class MANTID_CATALOG_DLL ConfigServiceTokenStore : public IOAuthTokenStore { @@ -73,7 +74,7 @@ public: ConfigServiceTokenStore() = default; ConfigServiceTokenStore & operator=(const ConfigServiceTokenStore &other) = default; - ~ConfigServiceTokenStore(); + ~ConfigServiceTokenStore() override; void setToken(const boost::optional<OAuthToken> &token) override; boost::optional<OAuthToken> getToken() override; diff --git a/Framework/DataHandling/src/LoadEMU.cpp b/Framework/DataHandling/src/LoadEMU.cpp index fee19807dd7071199199c5eb0cf713deef2698ba..3fe201297385a994454244fee3b9a6a3f9c3599f 100644 --- a/Framework/DataHandling/src/LoadEMU.cpp +++ b/Framework/DataHandling/src/LoadEMU.cpp @@ -206,8 +206,8 @@ class ConvertTOF { public: ConvertTOF(double Amp, double freq, double phase, double L1, double v2, std::vector<double> &L2) - : m_w(2 * M_PI * freq), m_phi(phase), m_L0(L1), m_v2(v2), m_A(Amp), - m_L2(L2) {} + : m_w(2 * M_PI * freq), m_phi(M_PI * phase / 180.0), m_L0(L1), m_v2(v2), + m_A(Amp), m_L2(L2) {} double directTOF(size_t detID, double tobs) const { diff --git a/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfo.h b/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfo.h index c5819b840c4e671a1e2df2a15d0e049d6242fccf..eefcf9fc90b5ee8a3896380a42201a3f1b6f3ae7 100644 --- a/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfo.h +++ b/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfo.h @@ -13,6 +13,7 @@ #include <vector> #include "MantidGeometry/DllConfig.h" +#include "MantidGeometry/Instrument/DetectorInfoIterator.h" #include "MantidKernel/DateAndTime.h" #include "MantidKernel/Quat.h" #include "MantidKernel/V3D.h" @@ -28,7 +29,6 @@ class SpectrumInfo; namespace Geometry { class IDetector; class Instrument; -class DetectorInfoIterator; /** Geometry::DetectorInfo is an intermediate step towards a DetectorInfo that is part of Instrument-2.0. The aim is to provide a nearly identical interface @@ -111,8 +111,10 @@ public: friend class API::SpectrumInfo; friend class Instrument; - DetectorInfoIterator begin() const; - DetectorInfoIterator end() const; + DetectorInfoIterator<DetectorInfo> begin(); + DetectorInfoIterator<DetectorInfo> end(); + const DetectorInfoIterator<const DetectorInfo> cbegin() const; + const DetectorInfoIterator<const DetectorInfo> cend() const; private: const Geometry::IDetector &getDetector(const size_t index) const; @@ -131,6 +133,9 @@ private: mutable std::vector<size_t> m_lastIndex; }; +using DetectorInfoIt = DetectorInfoIterator<DetectorInfo>; +using DetectorInfoConstIt = DetectorInfoIterator<const DetectorInfo>; + } // namespace Geometry } // namespace Mantid diff --git a/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfoItem.h b/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfoItem.h index 4b9b2a5212c755c907d0c3025cc833f193b64126..3699f43492230bf8f19cd3c5a948a755f55ce83e 100644 --- a/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfoItem.h +++ b/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfoItem.h @@ -7,11 +7,9 @@ #ifndef MANTID_GEOMETRY_DETECTORINFOITEM_H_ #define MANTID_GEOMETRY_DETECTORINFOITEM_H_ -#include "MantidGeometry/Instrument/DetectorInfo.h" #include "MantidKernel/Quat.h" #include "MantidKernel/V3D.h" -using Mantid::Geometry::DetectorInfo; using Mantid::Kernel::Quat; using Mantid::Kernel::V3D; @@ -32,8 +30,7 @@ methods include: @author Bhuvan Bezawada, STFC @date 2018 */ - -class MANTID_GEOMETRY_DLL DetectorInfoItem { +template <typename T> class DetectorInfoItem { public: // Methods that can be accessed via the iterator @@ -41,6 +38,11 @@ public: bool isMasked() const { return m_detectorInfo->isMasked(m_index); } + void setMasked(bool masked) { + static_assert(!std::is_const<T>::value, "Operation disabled on const T"); + return m_detectorInfo->setMasked(m_index, masked); + } + double twoTheta() const { return m_detectorInfo->twoTheta(m_index); } Mantid::Kernel::V3D position() const { @@ -51,17 +53,12 @@ public: return m_detectorInfo->rotation(m_index); } -private: - // Allow DetectorInfoIterator access - friend class DetectorInfoIterator; - - // Private constructor, can only be created by DetectorInfoIterator - DetectorInfoItem(const DetectorInfo &detectorInfo, const size_t index) + DetectorInfoItem(T &detectorInfo, const size_t index) : m_detectorInfo(&detectorInfo), m_index(index) {} // Non-owning pointer. A reference makes the class unable to define an // assignment operator that we need. - const DetectorInfo *m_detectorInfo; + T *m_detectorInfo; size_t m_index; }; diff --git a/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfoIterator.h b/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfoIterator.h index 938aa15579871bbca1c5c94bda877f24678d8c21..3b40de2d809b94523fa02e271e7fc506ab2ff450 100644 --- a/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfoIterator.h +++ b/Framework/Geometry/inc/MantidGeometry/Instrument/DetectorInfoIterator.h @@ -8,7 +8,6 @@ #define MANTID_GEOMETRY_DETECTORINFOITERATOR_H_ #include "MantidGeometry/Instrument/DetectorInfoItem.h" - #include <boost/iterator/iterator_facade.hpp> using Mantid::Geometry::DetectorInfoItem; @@ -26,14 +25,14 @@ iterator. @author Bhuvan Bezawada, STFC @date 2018 */ - -class MANTID_GEOMETRY_DLL DetectorInfoIterator - : public boost::iterator_facade<DetectorInfoIterator, - const DetectorInfoItem &, +template <typename T> +class DetectorInfoIterator + : public boost::iterator_facade<DetectorInfoIterator<T>, + DetectorInfoItem<T> &, boost::random_access_traversal_tag> { public: - DetectorInfoIterator(const DetectorInfo &detectorInfo, const size_t index) + DetectorInfoIterator(T &detectorInfo, const size_t index) : m_item(detectorInfo, index) {} private: @@ -69,18 +68,18 @@ private: void setIndex(const size_t index) { m_item.m_index = index; } - bool equal(const DetectorInfoIterator &other) const { + bool equal(const DetectorInfoIterator<T> &other) const { return getIndex() == other.getIndex(); } - const DetectorInfoItem &dereference() const { return m_item; } + DetectorInfoItem<T> &dereference() const { return m_item; } - uint64_t distance_to(const DetectorInfoIterator &other) const { + uint64_t distance_to(const DetectorInfoIterator<T> &other) const { return static_cast<uint64_t>(other.getIndex()) - static_cast<uint64_t>(getIndex()); } - DetectorInfoItem m_item; + mutable DetectorInfoItem<T> m_item; }; } // namespace Geometry diff --git a/Framework/Geometry/src/Instrument/DetectorInfo.cpp b/Framework/Geometry/src/Instrument/DetectorInfo.cpp index 685ebbf6d9d6db8f13c3ba57078ec0261b5bffbf..5f8d6656a1816f61b8f526c02ff6807c87e8a67a 100644 --- a/Framework/Geometry/src/Instrument/DetectorInfo.cpp +++ b/Framework/Geometry/src/Instrument/DetectorInfo.cpp @@ -331,6 +331,14 @@ DetectorInfo::scanIntervals() const { return {intervals.begin(), intervals.end()}; } +const DetectorInfoConstIt DetectorInfo::cbegin() const { + return DetectorInfoConstIt(*this, 0); +} + +const DetectorInfoConstIt DetectorInfo::cend() const { + return DetectorInfoConstIt(*this, size()); +} + const Geometry::IDetector &DetectorInfo::getDetector(const size_t index) const { size_t thread = static_cast<size_t>(PARALLEL_THREAD_NUMBER); if (m_lastIndex[thread] != index) { @@ -350,14 +358,10 @@ DetectorInfo::getDetectorPtr(const size_t index) const { } // Begin method for iterator -DetectorInfoIterator DetectorInfo::begin() const { - return DetectorInfoIterator(*this, 0); -} +DetectorInfoIt DetectorInfo::begin() { return DetectorInfoIt(*this, 0); } // End method for iterator -DetectorInfoIterator DetectorInfo::end() const { - return DetectorInfoIterator(*this, size()); -} +DetectorInfoIt DetectorInfo::end() { return DetectorInfoIt(*this, size()); } } // namespace Geometry } // namespace Mantid diff --git a/Framework/Geometry/test/DetectorInfoIteratorTest.h b/Framework/Geometry/test/DetectorInfoIteratorTest.h index d955d91fe2070d2ba69e46644c4f5dce483781eb..bcf5aadeaa39594ba2f03c25317cf64ebda3ee8e 100644 --- a/Framework/Geometry/test/DetectorInfoIteratorTest.h +++ b/Framework/Geometry/test/DetectorInfoIteratorTest.h @@ -69,31 +69,30 @@ public: return InstrumentVisitor::makeWrappers(*visitee, nullptr).second; } - void test_iterator_begin() { + void test_iterator_cbegin() { // Get the DetectorInfo object auto detectorInfo = create_detector_info_object(); - auto iter = detectorInfo->begin(); - + auto iter = detectorInfo->cbegin(); // Check we start at the correct place - TS_ASSERT(iter != detectorInfo->end()); + TS_ASSERT(iter != detectorInfo->cend()); } - void test_iterator_end() { + void test_iterator_cend() { // Get the DetectorInfo object auto detectorInfo = create_detector_info_object(); - auto iter = detectorInfo->end(); + auto iter = detectorInfo->cend(); // Check we start at the correct place - TS_ASSERT(iter != detectorInfo->begin()); + TS_ASSERT(iter != detectorInfo->cbegin()); } void test_iterator_increment_and_positions() { // Get the DetectorInfo object auto detectorInfo = create_detector_info_object(); - auto iter = detectorInfo->begin(); + auto iter = detectorInfo->cbegin(); // Check that we start at the beginning - TS_ASSERT(iter == detectorInfo->begin()); + TS_ASSERT(iter == detectorInfo->cbegin()); // Doubles for values in the V3D position object double xValue = 0.0; @@ -114,16 +113,16 @@ public: } // Check we've reached the end - TS_ASSERT(iter == detectorInfo->end()); + TS_ASSERT(iter == detectorInfo->cend()); } void test_iterator_decrement_and_positions() { // Get the DetectorInfo object auto detectorInfo = create_detector_info_object(); - auto iter = detectorInfo->end(); + auto iter = detectorInfo->cend(); // Check that we start at the end - TS_ASSERT(iter == detectorInfo->end()); + TS_ASSERT(iter == detectorInfo->cend()); // Doubles for values in the V3D position object double xValue = 0.0; @@ -144,13 +143,13 @@ public: } // Check we've reached the beginning - TS_ASSERT(iter == detectorInfo->begin()); + TS_ASSERT(iter == detectorInfo->cbegin()); } void test_iterator_advance_and_positions() { // Get the DetectorInfo object auto detectorInfo = create_detector_info_object(); - auto iter = detectorInfo->begin(); + auto iter = detectorInfo->cbegin(); // Store the expected X value double xValue = 0.0; @@ -167,16 +166,16 @@ public: // Go to the start std::advance(iter, -4); - TS_ASSERT(iter == detectorInfo->begin()); + TS_ASSERT(iter == detectorInfo->cbegin()); } void test_copy_iterator_and_positions() { // Get the DetectorInfo object auto detectorInfo = create_detector_info_object(); - auto iter = detectorInfo->begin(); + auto iter = detectorInfo->cbegin(); // Create a copy - auto iterCopy = DetectorInfoIterator(iter); + auto iterCopy = DetectorInfoConstIt(iter); // Check TS_ASSERT_EQUALS(iter->position().X(), 11.0); @@ -190,6 +189,13 @@ public: TS_ASSERT_EQUALS(iter->position().X(), 12.0); TS_ASSERT_EQUALS(iterCopy->position().X(), 12.0); } + + void test_non_const() { + auto detectorInfo = create_detector_info_object(); + auto it = detectorInfo->begin(); + it->setMasked(true); + TS_ASSERT(it->isMasked()); + } }; #endif /* MANTID_GEOMETRY_DETECTORINFOITERATORTEST_H_ */ diff --git a/Framework/PythonInterface/inc/MantidPythonInterface/api/DetectorInfoPythonIterator.h b/Framework/PythonInterface/inc/MantidPythonInterface/api/DetectorInfoPythonIterator.h index 7ff41c8aa63e276e9f3ce393a719fcd9f0d8be33..ee194aaaa3ef3a5aa5332263cead2f8c3fe64e1a 100644 --- a/Framework/PythonInterface/inc/MantidPythonInterface/api/DetectorInfoPythonIterator.h +++ b/Framework/PythonInterface/inc/MantidPythonInterface/api/DetectorInfoPythonIterator.h @@ -38,11 +38,11 @@ without the need for indexes. class DetectorInfoPythonIterator { public: - explicit DetectorInfoPythonIterator(const DetectorInfo &detectorInfo) + explicit DetectorInfoPythonIterator(DetectorInfo &detectorInfo) : m_begin(detectorInfo.begin()), m_end(detectorInfo.end()), m_firstOrDone(true) {} - const DetectorInfoItem &next() { + const DetectorInfoItem<DetectorInfo> &next() { if (!m_firstOrDone) ++m_begin; else @@ -55,8 +55,8 @@ public: } private: - DetectorInfoIterator m_begin; - DetectorInfoIterator m_end; + DetectorInfoIterator<DetectorInfo> m_begin; + DetectorInfoIterator<DetectorInfo> m_end; bool m_firstOrDone; }; diff --git a/Framework/PythonInterface/inc/MantidPythonInterface/api/SpectrumInfoPythonIterator.h b/Framework/PythonInterface/inc/MantidPythonInterface/api/SpectrumInfoPythonIterator.h index ad1f034fde7e02b36d3727895160c92efe05b804..25000e11500aef01edbae39b91abb1fb5076e204 100644 --- a/Framework/PythonInterface/inc/MantidPythonInterface/api/SpectrumInfoPythonIterator.h +++ b/Framework/PythonInterface/inc/MantidPythonInterface/api/SpectrumInfoPythonIterator.h @@ -42,11 +42,11 @@ without the need for indexes. class SpectrumInfoPythonIterator { public: - explicit SpectrumInfoPythonIterator(const SpectrumInfo &spectrumInfo) + explicit SpectrumInfoPythonIterator(SpectrumInfo &spectrumInfo) : m_begin(spectrumInfo.begin()), m_end(spectrumInfo.end()), m_firstOrDone(true) {} - const SpectrumInfoItem &next() { + const SpectrumInfoItem<SpectrumInfo> &next() { if (!m_firstOrDone) ++m_begin; else @@ -59,8 +59,8 @@ public: } private: - SpectrumInfoIterator m_begin; - SpectrumInfoIterator m_end; + SpectrumInfoIterator<SpectrumInfo> m_begin; + SpectrumInfoIterator<SpectrumInfo> m_end; bool m_firstOrDone; }; diff --git a/Framework/PythonInterface/inc/MantidPythonInterface/kernel/Policies/VectorToNumpy.h b/Framework/PythonInterface/inc/MantidPythonInterface/kernel/Policies/VectorToNumpy.h index 2001d95836cc8b288577051340a781cfa5a3ebb2..2591c86c37cb42e9373d235d5c8cfc54e927a8ed 100644 --- a/Framework/PythonInterface/inc/MantidPythonInterface/kernel/Policies/VectorToNumpy.h +++ b/Framework/PythonInterface/inc/MantidPythonInterface/kernel/Policies/VectorToNumpy.h @@ -72,8 +72,7 @@ template <typename ConversionPolicy> struct VectorRefToNumpy { typename std::remove_reference<T>::type>::type; // MPL compile-time check that T is a reference to a std::vector using type = typename boost::mpl::if_c< - boost::mpl::and_<std::is_reference<T>, - is_std_vector<non_const_type>>::value, + is_std_vector<non_const_type>::value, VectorRefToNumpyImpl<non_const_type, ConversionPolicy>, VectorRefToNumpy_Requires_Reference_To_StdVector_Return_Type<T>>::type; }; @@ -96,13 +95,12 @@ template <typename VectorType> struct VectorToNumpyImpl { inline PyTypeObject const *get_pytype() const { return ndarrayType(); } }; -template <typename T> -struct VectorToNumpy_Requires_StdVector_Return_By_Value {}; +template <typename T> struct VectorToNumpy_Requires_StdVector_Return {}; } // namespace /** * Implements a return value policy that - * returns a numpy array from a function returning a std::vector by value + * returns a numpy array from a function returning a std::vector by ref or value * * It is only possible to clone these types since a wrapper would wrap temporary */ @@ -110,12 +108,13 @@ struct VectorToNumpy { // The boost::python framework calls return_value_policy::apply<T>::type template <class T> struct apply { // Typedef that removes any const from the type - using non_const_type = typename std::remove_const<T>::type; + using non_const_type = typename std::remove_const< + typename std::remove_reference<T>::type>::type; // MPL compile-time check that T is a std::vector using type = typename boost::mpl::if_c< is_std_vector<non_const_type>::value, VectorRefToNumpyImpl<non_const_type, Converters::Clone>, - VectorToNumpy_Requires_StdVector_Return_By_Value<T>>::type; + VectorToNumpy_Requires_StdVector_Return<T>>::type; }; }; } // namespace Policies diff --git a/Framework/PythonInterface/mantid/api/src/Exports/MatrixWorkspace.cpp b/Framework/PythonInterface/mantid/api/src/Exports/MatrixWorkspace.cpp index 3a938ebbfd1144762e67350b611f9f38da5d470e..eca9d6253fcb579db433020ace2f76db1d636a4e 100644 --- a/Framework/PythonInterface/mantid/api/src/Exports/MatrixWorkspace.cpp +++ b/Framework/PythonInterface/mantid/api/src/Exports/MatrixWorkspace.cpp @@ -370,7 +370,9 @@ void export_MatrixWorkspace() { "some subsequent algorithms may expect it to be " "monitor workspace later.") .def("clearMonitorWorkspace", &clearMonitorWorkspace, args("self"), - "Forget about monitor workspace, attached to the current workspace"); + "Forget about monitor workspace, attached to the current workspace") + .def("isCommonBins", &MatrixWorkspace::isCommonBins, + "Returns true if the workspace has common X bins."); RegisterWorkspacePtrToPython<MatrixWorkspace>(); } diff --git a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumDefinition.cpp b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumDefinition.cpp index 54175e42c25a7671b313315f1cf1106d9ba1da51..d4f21658fb579a6ae6625b5fe4fae7ade13c86f3 100644 --- a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumDefinition.cpp +++ b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumDefinition.cpp @@ -26,6 +26,10 @@ void export_SpectrumDefinition() { "Returns the pair of detector index and time index at given index " "of spectrum definition.") + .def("__len__", &SpectrumDefinition::size, arg("self"), + "Returns the size of the SpectrumDefinition i.e. the number of " + "detectors for the spectrum.") + .def("size", &SpectrumDefinition::size, arg("self"), "Returns the size of the SpectrumDefinition i.e. the number of " "detectors for the spectrum.") diff --git a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfo.cpp b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfo.cpp index 2abd3e64f5bf6c59da5eaa7b0ac46dc9dd571924..4fa354cfd32dbade436ad3c9f764880670a3f9a7 100644 --- a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfo.cpp +++ b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfo.cpp @@ -23,7 +23,7 @@ using Mantid::SpectrumDefinition; using namespace boost::python; // Helper method to make the python iterator -SpectrumInfoPythonIterator make_pyiterator(const SpectrumInfo &spectrumInfo) { +SpectrumInfoPythonIterator make_pyiterator(SpectrumInfo &spectrumInfo) { return SpectrumInfoPythonIterator(spectrumInfo); } diff --git a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoItem.cpp b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoItem.cpp index 098b2702608cb943babbd3f22e33dab8a93eef66..322e5d1706613e4580a3faf37871448151336905 100644 --- a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoItem.cpp +++ b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoItem.cpp @@ -7,9 +7,11 @@ #include "MantidAPI/SpectrumInfoItem.h" #include "MantidKernel/V3D.h" +#include "MantidAPI/SpectrumInfo.h" #include <boost/python/class.hpp> #include <boost/python/module.hpp> +using Mantid::API::SpectrumInfo; using Mantid::API::SpectrumInfoItem; using Mantid::Kernel::V3D; using namespace boost::python; @@ -18,15 +20,20 @@ using namespace boost::python; void export_SpectrumInfoItem() { // Export to Python - class_<SpectrumInfoItem>("SpectrumInfoItem", no_init) - .add_property("isMonitor", &SpectrumInfoItem::isMonitor) - .add_property("isMasked", &SpectrumInfoItem::isMasked) - .add_property("twoTheta", &SpectrumInfoItem::twoTheta) - .add_property("signedTwoTheta", &SpectrumInfoItem::signedTwoTheta) - .add_property("l2", &SpectrumInfoItem::l2) - .add_property("hasUniqueDetector", &SpectrumInfoItem::hasUniqueDetector) - .add_property("spectrumDefinition", - make_function(&SpectrumInfoItem::spectrumDefinition, - return_internal_reference<>())) - .add_property("position", &SpectrumInfoItem::position); + class_<SpectrumInfoItem<SpectrumInfo>>("SpectrumInfoItem", no_init) + .add_property("isMonitor", &SpectrumInfoItem<SpectrumInfo>::isMonitor) + .add_property("isMasked", &SpectrumInfoItem<SpectrumInfo>::isMasked) + .add_property("twoTheta", &SpectrumInfoItem<SpectrumInfo>::twoTheta) + .add_property("signedTwoTheta", + &SpectrumInfoItem<SpectrumInfo>::signedTwoTheta) + .add_property("l2", &SpectrumInfoItem<SpectrumInfo>::l2) + .add_property("hasUniqueDetector", + &SpectrumInfoItem<SpectrumInfo>::hasUniqueDetector) + .add_property( + "spectrumDefinition", + make_function(&SpectrumInfoItem<SpectrumInfo>::spectrumDefinition, + return_internal_reference<>())) + .add_property("position", &SpectrumInfoItem<SpectrumInfo>::position) + .def("setMasked", &SpectrumInfoItem<SpectrumInfo>::setMasked, + (arg("self"), arg("masked")), "Set the mask flag for the spectrum"); } diff --git a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoIterator.cpp b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoIterator.cpp index 455f0075ef0cb71dc80169f0c76ce448f3a1f78b..3b49a932a7245ac0df652f0f76275d9d3555b6cb 100644 --- a/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoIterator.cpp +++ b/Framework/PythonInterface/mantid/api/src/Exports/SpectrumInfoIterator.cpp @@ -5,10 +5,12 @@ // & Institut Laue - Langevin // SPDX - License - Identifier: GPL - 3.0 + #include "MantidAPI/SpectrumInfoIterator.h" +#include "MantidAPI/SpectrumInfo.h" #include <boost/python/class.hpp> #include <boost/python/module.hpp> +using Mantid::API::SpectrumInfo; using Mantid::API::SpectrumInfoIterator; using namespace boost::python; @@ -16,5 +18,5 @@ using namespace boost::python; void export_SpectrumInfoIterator() { // Export to Python - class_<SpectrumInfoIterator>("SpectrumInfoIterator", no_init); + class_<SpectrumInfoIterator<SpectrumInfo>>("SpectrumInfoIterator", no_init); } diff --git a/Framework/PythonInterface/mantid/fitfunctions.py b/Framework/PythonInterface/mantid/fitfunctions.py index ee7dace2df3d09f2b705492176285343b479da54..55b3c78a001a3d3569b5c3d4ce737f16ffc2321b 100644 --- a/Framework/PythonInterface/mantid/fitfunctions.py +++ b/Framework/PythonInterface/mantid/fitfunctions.py @@ -23,7 +23,7 @@ class FunctionWrapper(object): return wrapper(fun, *args, **kwargs) return FunctionWrapper(fun, **kwargs) - def __init__ (self, name, **kwargs): + def __init__(self, name, **kwargs): """ Called when creating an instance diff --git a/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfo.cpp b/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfo.cpp index 2d45bb2894735dfddd6ffaebafbeb42b76457183..f348ac26520887c9dfb1adfeeaa6fc775b4fba08 100644 --- a/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfo.cpp +++ b/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfo.cpp @@ -10,7 +10,8 @@ #include "MantidKernel/Quat.h" #include "MantidKernel/V3D.h" #include "MantidPythonInterface/api/DetectorInfoPythonIterator.h" - +#include "MantidPythonInterface/core/Converters/WrapWithNDArray.h" +#include "MantidPythonInterface/kernel/Policies/VectorToNumpy.h" #include <boost/iterator/iterator_facade.hpp> #include <boost/python/class.hpp> #include <boost/python/copy_const_reference.hpp> @@ -28,11 +29,17 @@ using Mantid::PythonInterface::DetectorInfoPythonIterator; using Mantid::Kernel::Quat; using Mantid::Kernel::V3D; using namespace boost::python; +using namespace Mantid::PythonInterface; +namespace { // Helper method to make the python iterator -DetectorInfoPythonIterator make_pyiterator(const DetectorInfo &detectorInfo) { +DetectorInfoPythonIterator make_pyiterator(DetectorInfo &detectorInfo) { return DetectorInfoPythonIterator(detectorInfo); } +/// return_value_policy for read-only numpy array +using return_readonly_numpy = + return_value_policy<Policies::VectorRefToNumpy<Converters::WrapReadOnly>>; +} // namespace // Export DetectorInfo void export_DetectorInfo() { @@ -96,5 +103,7 @@ void export_DetectorInfo() { .def("rotation", rotation, (arg("self"), arg("index")), "Returns the absolute rotation of the detector where the detector " - "is identified by 'index'."); + "is identified by 'index'.") + .def("detectorIDs", &DetectorInfo::detectorIDs, return_readonly_numpy(), + "Returns all detector ids sorted by detector index"); } diff --git a/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfoItem.cpp b/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfoItem.cpp index 15aab516630c737b93fbe207b3c0043aadcadeec..09445dbb303b113314cbe711cb0ce91842e914d3 100644 --- a/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfoItem.cpp +++ b/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfoItem.cpp @@ -5,11 +5,14 @@ // & Institut Laue - Langevin // SPDX - License - Identifier: GPL - 3.0 + #include "MantidGeometry/Instrument/DetectorInfoItem.h" +#include "MantidGeometry/Instrument/DetectorInfo.h" +#include "MantidKernel/Quat.h" #include "MantidKernel/V3D.h" #include <boost/python/class.hpp> #include <boost/python/module.hpp> +using Mantid::Geometry::DetectorInfo; using Mantid::Geometry::DetectorInfoItem; using Mantid::Kernel::V3D; using namespace boost::python; @@ -18,10 +21,12 @@ using namespace boost::python; void export_DetectorInfoItem() { // Export to Python - class_<DetectorInfoItem>("DetectorInfoItem", no_init) - .add_property("isMonitor", &DetectorInfoItem::isMonitor) - .add_property("isMasked", &DetectorInfoItem::isMasked) - .add_property("twoTheta", &DetectorInfoItem::twoTheta) - .add_property("position", &DetectorInfoItem::position) - .add_property("rotation", &DetectorInfoItem::rotation); + class_<DetectorInfoItem<DetectorInfo>>("DetectorInfoItem", no_init) + .add_property("isMonitor", &DetectorInfoItem<DetectorInfo>::isMonitor) + .add_property("isMasked", &DetectorInfoItem<DetectorInfo>::isMasked) + .add_property("twoTheta", &DetectorInfoItem<DetectorInfo>::twoTheta) + .add_property("position", &DetectorInfoItem<DetectorInfo>::position) + .add_property("rotation", &DetectorInfoItem<DetectorInfo>::rotation) + .def("setMasked", &DetectorInfoItem<DetectorInfo>::setMasked, + (arg("self"), arg("masked")), "Set the mask flag for the detector"); } diff --git a/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfoIterator.cpp b/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfoIterator.cpp index f6b73a30bb61f5af46895abad91c533d7a20630d..9f099aff92a1c60986ba11c48adce3fca83d8d2f 100644 --- a/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfoIterator.cpp +++ b/Framework/PythonInterface/mantid/geometry/src/Exports/DetectorInfoIterator.cpp @@ -5,10 +5,12 @@ // & Institut Laue - Langevin // SPDX - License - Identifier: GPL - 3.0 + #include "MantidGeometry/Instrument/DetectorInfoIterator.h" +#include "MantidGeometry/Instrument/DetectorInfo.h" #include <boost/python/class.hpp> #include <boost/python/module.hpp> +using Mantid::Geometry::DetectorInfo; using Mantid::Geometry::DetectorInfoIterator; using namespace boost::python; @@ -16,5 +18,5 @@ using namespace boost::python; void export_DetectorInfoIterator() { // Export to Python - class_<DetectorInfoIterator>("DetectorInfoIterator", no_init); + class_<DetectorInfoIterator<DetectorInfo>>("DetectorInfoIterator", no_init); } diff --git a/Framework/PythonInterface/plugins/algorithms/MaskAngle.py b/Framework/PythonInterface/plugins/algorithms/MaskAngle.py index aaecceabbb6c86e0b10fe73198545234efa6aeeb..da80b6d9255e4a1c9936f91249d2c267bcaaf0be 100644 --- a/Framework/PythonInterface/plugins/algorithms/MaskAngle.py +++ b/Framework/PythonInterface/plugins/algorithms/MaskAngle.py @@ -9,11 +9,12 @@ from __future__ import (absolute_import, division, print_function) import mantid.simpleapi import mantid.kernel import mantid.api +import mantid.geometry import numpy class MaskAngle(mantid.api.PythonAlgorithm): - """ Class to generate grouping file + """ Mask detectors between specified angles based on angle type required """ def category(self): @@ -48,24 +49,29 @@ class MaskAngle(mantid.api.PythonAlgorithm): mantid.kernel.StringListValidator(['TwoTheta', 'Phi']), 'Which angle to use') self.declareProperty(mantid.kernel.IntArrayProperty(name="MaskedDetectors", direction=mantid.kernel.Direction.Output), - doc="List of detector masked, with scatterin angles between MinAngle and MaxAngle") + doc="List of detector masked, with scattering angles between MinAngle and MaxAngle") def validateInputs(self): issues = dict() - ws = self.getProperty("Workspace").value - - try: - if type(ws).__name__ == "WorkspaceGroup": - for w in ws: - w.getInstrument().getSource().getPos() - else: - ws.getInstrument().getSource().getPos() - except (RuntimeError, ValueError, AttributeError, TypeError): + hasInstrument = True + if type(ws).__name__ == "WorkspaceGroup" and len(ws) > 0: + for item in ws: + hasInstrument = hasInstrument and len(item.componentInfo()) > 0 + else: + hasInstrument = len(ws.componentInfo()) > 0 + if not hasInstrument: issues["Workspace"] = "Workspace must have an associated instrument." - return issues + def _get_phi(self, spectra_pos): + ''' + The implementation here assumes that z is the beam direction. + That assumption is not universally true, it depends on the geometry configuration. + This returns the phi spherical coordinate value + ''' + return numpy.fabs(numpy.arctan2(spectra_pos.Y(), spectra_pos.X())) + def PyExec(self): ws = self.getProperty("Workspace").value ttmin = numpy.radians(self.getProperty("MinAngle").value) @@ -73,36 +79,28 @@ class MaskAngle(mantid.api.PythonAlgorithm): if ttmin > ttmax : raise ValueError("MinAngle > MaxAngle, please check angle range for masking") - angle = self.getProperty('Angle').value - - detlist=[] - - numspec = ws.getNumberHistograms() - spectrumInfo = ws.spectrumInfo() - - if angle == 'Phi': - for i in range(numspec): - if not spectrumInfo.isMonitor(i): - det = ws.getDetector(i) - phi=abs(det.getPhi()) - if phi>= ttmin and phi<= ttmax: - detlist.append(det.getID()) - else: - source=ws.getInstrument().getSource().getPos() - sample=ws.getInstrument().getSample().getPos() - beam = sample-source - for i in range(numspec): - if not spectrumInfo.isMonitor(i): - det = ws.getDetector(i) - tt=det.getTwoTheta(sample,beam) - if tt>= ttmin and tt<= ttmax: - detlist.append(det.getID()) - - if len(detlist)> 0: - mantid.simpleapi.MaskDetectors(Workspace=ws,DetectorList=detlist) - else: + angle_phi = self.getProperty('Angle').value == 'Phi' + spectrum_info = ws.spectrumInfo() + detector_info = ws.detectorInfo() + det_ids = detector_info.detectorIDs() + masked_ids = list() + for spectrum in spectrum_info: + if not spectrum.isMonitor: + # Get the first detector of spectrum. Ignore time aspects. + if angle_phi: + val = self._get_phi(spectrum.position) + else: + # Two theta + val =spectrum.twoTheta + if val>= ttmin and val<= ttmax: + detectors = spectrum.spectrumDefinition + for j in range(len(detectors)): + masked_ids.append(det_ids[detectors[j][0]]) + if not masked_ids: self.log().information("no detectors within this range") - self.setProperty("MaskedDetectors", numpy.array(detlist)) + else: + mantid.simpleapi.MaskDetectors(Workspace=ws,DetectorList=numpy.array(masked_ids)) + self.setProperty("MaskedDetectors", numpy.array(masked_ids)) mantid.api.AlgorithmFactory.subscribe(MaskAngle) diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/MSDFit.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/MSDFit.py index c2070b3f8379190e2cb2c970ece8d363ecdf41a2..3824447f5e1400ee7fb57850e62c91f2029e3023 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/MSDFit.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/MSDFit.py @@ -31,7 +31,8 @@ class MSDFit(DataProcessorAlgorithm): self.declareProperty(MatrixWorkspaceProperty('InputWorkspace', '', direction=Direction.Input), doc='Sample input workspace') self.declareProperty(name='Model', defaultValue='Gauss', - validator=StringListValidator(['Gauss', 'Peters', 'Yi']), + validator=StringListValidator( + ['Gauss', 'Peters', 'Yi']), doc='Model options : Gauss, Peters, Yi') self.declareProperty(name='XStart', defaultValue=0.0, @@ -100,9 +101,12 @@ class MSDFit(DataProcessorAlgorithm): progress = Progress(self, 0.0, 0.05, 3) self._original_ws = self._input_ws - rename_alg = self.createChildAlgorithm("RenameWorkspace", enableLogging=False) + rename_alg = self.createChildAlgorithm( + "RenameWorkspace", enableLogging=False) rename_alg.setProperty("InputWorkspace", self._input_ws) - rename_alg.setProperty("OutputWorkspace", self._input_ws + "_" + self._model) + rename_alg.setProperty( + "OutputWorkspace", + self._input_ws + "_" + self._model) rename_alg.execute() self._input_ws = self._input_ws + "_" + self._model input_params = [self._input_ws + ',i%d' % i for i in range(self._spec_range[0], @@ -111,19 +115,19 @@ class MSDFit(DataProcessorAlgorithm): # Fit line to each of the spectra if self._model == 'Gauss': logger.information('Model : Gaussian approximation') - function = 'name=MsdGauss, Height=1.0, MSD=0.1' - function += ',constraint=(Height>0.0, MSD>0.0)' - params_list = ['Height', 'MSD'] + function = 'name=MsdGauss, Height=1.0, Msd=0.1' + function += ',constraint=(Height>0.0, Msd>0.0)' + params_list = ['Height', 'Msd'] elif self._model == 'Peters': logger.information('Model : Peters & Kneller') - function = 'name=MsdPeters, Height=1.0, MSD=1.0, Beta=1.0' - function += ',constraint=(Height>0.0, MSD>0.0, 100.0>Beta>0.3)' - params_list = ['Height', 'MSD', 'Beta'] + function = 'name=MsdPeters, Height=1.0, Msd=1.0, Beta=1.0' + function += ',constraint=(Height>0.0, Msd>0.0, 100.0>Beta>0.3)' + params_list = ['Height', 'Msd', 'Beta'] elif self._model == 'Yi': logger.information('Model : Yi et al') - function = 'name=MsdYi, Height=1.0, MSD=1.0, Sigma=0.1' - function += ',constraint=(Height>0.0, MSD>0.0, Sigma>0.0)' - params_list = ['Height', 'MSD', 'Sigma'] + function = 'name=MsdYi, Height=1.0, Msd=1.0, Sigma=0.1' + function += ',constraint=(Height>0.0, Msd>0.0, Sigma>0.0)' + params_list = ['Height', 'Msd', 'Sigma'] else: raise ValueError('No Model defined') @@ -137,12 +141,20 @@ class MSDFit(DataProcessorAlgorithm): FitType='Sequential', CreateOutput=True) - delete_alg = self.createChildAlgorithm("DeleteWorkspace", enableLogging=False) - delete_alg.setProperty("Workspace", self._output_msd_ws + '_NormalisedCovarianceMatrices') + delete_alg = self.createChildAlgorithm( + "DeleteWorkspace", enableLogging=False) + delete_alg.setProperty( + "Workspace", + self._output_msd_ws + + '_NormalisedCovarianceMatrices') delete_alg.execute() - delete_alg.setProperty("Workspace", self._output_msd_ws + '_Parameters') + delete_alg.setProperty( + "Workspace", + self._output_msd_ws + + '_Parameters') delete_alg.execute() - rename_alg = self.createChildAlgorithm("RenameWorkspace", enableLogging=False) + rename_alg = self.createChildAlgorithm( + "RenameWorkspace", enableLogging=False) rename_alg.setProperty("InputWorkspace", self._output_msd_ws) rename_alg.setProperty("OutputWorkspace", self._output_param_ws) rename_alg.execute() @@ -154,31 +166,50 @@ class MSDFit(DataProcessorAlgorithm): for par in params_list: ws_name = self._output_msd_ws + '_' + par parameter_ws_group.append(ws_name) - convert_alg = self.createChildAlgorithm("ConvertTableToMatrixWorkspace", enableLogging=False) + convert_alg = self.createChildAlgorithm( + "ConvertTableToMatrixWorkspace", enableLogging=False) convert_alg.setProperty("InputWorkspace", self._output_param_ws) convert_alg.setProperty("OutputWorkspace", ws_name) convert_alg.setProperty("ColumnX", 'axis-1') convert_alg.setProperty("ColumnY", par) convert_alg.setProperty("ColumnE", par + '_Err') convert_alg.execute() - mtd.addOrReplace(ws_name, convert_alg.getProperty("OutputWorkspace").value) - - append_alg = self.createChildAlgorithm("AppendSpectra", enableLogging=False) - append_alg.setProperty("InputWorkspace1", self._output_msd_ws + '_' + params_list[0]) - append_alg.setProperty("InputWorkspace2", self._output_msd_ws + '_' + params_list[1]) + mtd.addOrReplace( + ws_name, convert_alg.getProperty("OutputWorkspace").value) + + append_alg = self.createChildAlgorithm( + "AppendSpectra", enableLogging=False) + append_alg.setProperty( + "InputWorkspace1", + self._output_msd_ws + + '_' + + params_list[0]) + append_alg.setProperty( + "InputWorkspace2", + self._output_msd_ws + + '_' + + params_list[1]) append_alg.setProperty("ValidateInputs", False) append_alg.setProperty("OutputWorkspace", self._output_msd_ws) append_alg.execute() - mtd.addOrReplace(self._output_msd_ws, append_alg.getProperty("OutputWorkspace").value) + mtd.addOrReplace( + self._output_msd_ws, + append_alg.getProperty("OutputWorkspace").value) if len(params_list) > 2: append_alg.setProperty("InputWorkspace1", self._output_msd_ws) - append_alg.setProperty("InputWorkspace2", self._output_msd_ws + '_' + params_list[2]) + append_alg.setProperty( + "InputWorkspace2", + self._output_msd_ws + + '_' + + params_list[2]) append_alg.setProperty("ValidateInputs", False) append_alg.setProperty("OutputWorkspace", self._output_msd_ws) append_alg.execute() - mtd.addOrReplace(self._output_msd_ws, append_alg.getProperty("OutputWorkspace").value) + mtd.addOrReplace(self._output_msd_ws, + append_alg.getProperty("OutputWorkspace").value) for par in params_list: - delete_alg.setProperty("Workspace", self._output_msd_ws + '_' + par) + delete_alg.setProperty( + "Workspace", self._output_msd_ws + '_' + par) delete_alg.execute() progress.report('Change axes') @@ -187,7 +218,9 @@ class MSDFit(DataProcessorAlgorithm): sort_alg.setProperty("InputWorkspace", self._output_msd_ws) sort_alg.setProperty("OutputWorkspace", self._output_msd_ws) sort_alg.execute() - mtd.addOrReplace(self._output_msd_ws, sort_alg.getProperty("OutputWorkspace").value) + mtd.addOrReplace( + self._output_msd_ws, + sort_alg.getProperty("OutputWorkspace").value) # Create a new x axis for the Q and Q**2 workspaces xunit = mtd[self._output_msd_ws].getAxis(0).setUnit('Label') xunit.setLabel('Temperature', 'K') @@ -200,7 +233,10 @@ class MSDFit(DataProcessorAlgorithm): # Rename fit workspace group original_fit_ws_name = self._output_msd_ws + '_Workspaces' if original_fit_ws_name != self._output_fit_ws: - rename_alg.setProperty("InputWorkspace", self._output_msd_ws + '_Workspaces') + rename_alg.setProperty( + "InputWorkspace", + self._output_msd_ws + + '_Workspaces') rename_alg.setProperty("OutputWorkspace", self._output_fit_ws) rename_alg.execute() @@ -213,7 +249,8 @@ class MSDFit(DataProcessorAlgorithm): copy_alg.setProperty("OutputWorkspace", self._output_fit_ws) copy_alg.execute() - rename_alg = self.createChildAlgorithm("RenameWorkspace", enableLogging=False) + rename_alg = self.createChildAlgorithm( + "RenameWorkspace", enableLogging=False) rename_alg.setProperty("InputWorkspace", self._input_ws) rename_alg.setProperty("OutputWorkspace", self._original_ws) rename_alg.execute() diff --git a/Framework/PythonInterface/plugins/functions/MsdGauss.py b/Framework/PythonInterface/plugins/functions/MsdGauss.py index 0f7f8e9d0ed1cdcdf829e1ded284740b3f61db9a..1f796e1367d63d2ffeb8958a373ed7ee264b97e7 100644 --- a/Framework/PythonInterface/plugins/functions/MsdGauss.py +++ b/Framework/PythonInterface/plugins/functions/MsdGauss.py @@ -27,11 +27,11 @@ class MsdGauss(IFunction1D): def init(self): # Active fitting parameters self.declareParameter("Height", 1.0, 'Height') - self.declareParameter("MSD", 0.05, 'Mean square displacement') + self.declareParameter("Msd", 0.05, 'Mean square displacement') def function1D(self, xvals): height = self.getParameterValue("Height") - msd = self.getParameterValue("MSD") + msd = self.getParameterValue("Msd") xvals = np.array(xvals) intensity = height * np.exp(-msd * xvals**2) @@ -40,7 +40,7 @@ class MsdGauss(IFunction1D): def functionDeriv1D(self, xvals, jacobian): height = self.getParameterValue("Height") - msd = self.getParameterValue("MSD") + msd = self.getParameterValue("Msd") for i, x in enumerate(xvals): e = math.exp(-msd * x**2) diff --git a/Framework/PythonInterface/plugins/functions/MsdPeters.py b/Framework/PythonInterface/plugins/functions/MsdPeters.py index b3278bc1eaf8dff62ed1e409c7eda2c0751201f3..9974d21ec72df219503536a556219a5e35656c07 100644 --- a/Framework/PythonInterface/plugins/functions/MsdPeters.py +++ b/Framework/PythonInterface/plugins/functions/MsdPeters.py @@ -28,12 +28,12 @@ class MsdPeters(IFunction1D): def init(self): # Active fitting parameters self.declareParameter("Height", 1.0, 'Height') - self.declareParameter("MSD", 0.05, 'Mean square displacement') + self.declareParameter("Msd", 0.05, 'Mean square displacement') self.declareParameter("Beta", 1.0, 'beta') def function1D(self, xvals): height = self.getParameterValue("Height") - msd = self.getParameterValue("MSD") + msd = self.getParameterValue("Msd") beta = self.getParameterValue("Beta") xvals = np.array(xvals) @@ -44,7 +44,7 @@ class MsdPeters(IFunction1D): def functionDeriv1D(self, xvals, jacobian): height = self.getParameterValue("Height") - msd = self.getParameterValue("MSD") + msd = self.getParameterValue("Msd") beta = self.getParameterValue("Beta") for i, x in enumerate(xvals): diff --git a/Framework/PythonInterface/plugins/functions/MsdYi.py b/Framework/PythonInterface/plugins/functions/MsdYi.py index 50c25f4fd24c6e63a488317727d3f2d15eef8813..3e92154c30074bd59135b1da19897c175ad7f517 100644 --- a/Framework/PythonInterface/plugins/functions/MsdYi.py +++ b/Framework/PythonInterface/plugins/functions/MsdYi.py @@ -1,43 +1,46 @@ -# Mantid Repository : https://github.com/mantidproject/mantid -# -# Copyright © 2007 ISIS Rutherford Appleton Laboratory UKRI, -# NScD Oak Ridge National Laboratory, European Spallation Source -# & Institut Laue - Langevin -# SPDX - License - Identifier: GPL - 3.0 + -# pylint: disable=no-init,invalid-name ''' @author Spencer Howells, ISIS @date December 05, 2013 + +Copyright © 2007-8 ISIS Rutherford Appleton Laboratory, +NScD Oak Ridge National Laboratory & European Spallation Source + +pylint: disable=no-init,invalid-name + +This file is part of Mantid. +Mantid Repository : https://github.com/mantidproject/mantid +File change history is stored at: <https://github.com/mantidproject/mantid> +Code Documentation is available at: <http://doxygen.mantidproject.org> ''' from __future__ import (absolute_import, division, print_function) - import math import numpy as np - from mantid.api import IFunction1D, FunctionFactory - # The model of Yi et al(J Phys Chem B 1316 5029 2012) takes into account motional heterogeneity. -# The elastic intensity is propotional to exp(-1/6*Q^2*msd)*(1+Q^4*sigma/72) +# The elastic intensity is proportional to exp(-(1/6)*Q^2*msd)*(1+Q^4*sigma/72) # where the mean square displacement msd = <r^2> and sigma^2 is the variance of the msd. +# In the limit sigma = 0 the model becomes Gaussian. + class MsdYi(IFunction1D): + def category(self): return "QuasiElastic" def init(self): # Active fitting parameters self.declareParameter("Height", 1.0, 'Height') - self.declareParameter("MSD", 0.05, 'Mean square displacement') + self.declareParameter("Msd", 0.05, 'Mean square displacement') self.declareParameter("Sigma", 1.0, 'Sigma') def function1D(self, xvals): height = self.getParameterValue("Height") - msd = self.getParameterValue("MSD") + msd = self.getParameterValue("Msd") sigma = self.getParameterValue("Sigma") xvals = np.array(xvals) - i1 = np.exp(-1.0 / (6.0 * xvals**2 * msd)) + i1 = np.exp((-1.0 / 6.0) * xvals * xvals * msd) i2 = 1.0 + (np.power(xvals, 4) * sigma / 72.0) intensity = height * i1 * i2 @@ -45,13 +48,12 @@ class MsdYi(IFunction1D): def functionDeriv1D(self, xvals, jacobian): height = self.getParameterValue("Height") - msd = self.getParameterValue("MSD") + msd = self.getParameterValue("Msd") sigma = self.getParameterValue("Sigma") for i, x in enumerate(xvals): - q = msd * x**2 - f1 = math.exp(-1.0 / (6.0 * q)) - df1 = f1 / (6.0 * x * q) + f1 = math.exp((-1.0 / 6.0) * x * x * msd) + df1 = -f1 * ((2.0 / 6.0) * x * msd) x4 = math.pow(x, 4) f2 = 1.0 + (x4 * sigma / 72.0) df2 = x4 / 72.0 diff --git a/Framework/PythonInterface/test/python/mantid/api/MatrixWorkspaceTest.py b/Framework/PythonInterface/test/python/mantid/api/MatrixWorkspaceTest.py index f45ea9b60de95cca6752978e33a722057591e3cd..9b6a5c7db5810330efd7e604b7bf579040e9c18c 100644 --- a/Framework/PythonInterface/test/python/mantid/api/MatrixWorkspaceTest.py +++ b/Framework/PythonInterface/test/python/mantid/api/MatrixWorkspaceTest.py @@ -417,6 +417,9 @@ class MatrixWorkspaceTest(unittest.TestCase): self.assertEquals(specInfo.isMasked(0), False) self.assertEquals(specInfo.isMasked(1), False) + def test_isCommonBins(self): + self.assertTrue(self._test_ws.isCommonBins()) + if __name__ == '__main__': unittest.main() #Testing particular test from Mantid diff --git a/Framework/PythonInterface/test/python/mantid/api/SpectrumInfoTest.py b/Framework/PythonInterface/test/python/mantid/api/SpectrumInfoTest.py index 3ef053c88a4b335ae3328fa18a41634e20750632..44e349b9d4f1d6973a012b69fd8445c196a05d7d 100644 --- a/Framework/PythonInterface/test/python/mantid/api/SpectrumInfoTest.py +++ b/Framework/PythonInterface/test/python/mantid/api/SpectrumInfoTest.py @@ -141,6 +141,16 @@ class SpectrumInfoTest(unittest.TestCase): for item in it: self.assertFalse(item.isMasked) + def test_iterator_for_setting_masked(self): + info = self._ws.spectrumInfo() + # nothing should be masked + it = iter(info) + next(it) # skip first as detectors cleared + for item in it: + # mask and check + item.setMasked(True) + self.assertTrue(item.isMasked) + def test_iteration_for_position(self): info = self._ws.spectrumInfo() lastY = None diff --git a/Framework/PythonInterface/test/python/mantid/geometry/DetectorInfoTest.py b/Framework/PythonInterface/test/python/mantid/geometry/DetectorInfoTest.py index d2cd69bc4dfed69945c5cf982587831e405a708b..a9ca56956b6369c20a6f475597f0fd513c4dd91c 100644 --- a/Framework/PythonInterface/test/python/mantid/geometry/DetectorInfoTest.py +++ b/Framework/PythonInterface/test/python/mantid/geometry/DetectorInfoTest.py @@ -87,11 +87,15 @@ class DetectorInfoTest(unittest.TestCase): workspace = CreateWorkspace(DataX=dataX, DataY=dataY) info = workspace.detectorInfo() self.assertEquals(info.size(), 0) + + def test_detectorIds(self): + info = self._ws.detectorInfo() + ids = info.detectorIDs() + # Should be read-only + self.assertFalse(ids.flags.writeable) + for a, b in zip(ids, [1,2]): + self.assertEquals(a,b) - """ - The following test cases test for returned objects to do with position - and rotation. - """ def test_position(self): """ Test that the detector's position is returned. """ @@ -129,6 +133,15 @@ class DetectorInfoTest(unittest.TestCase): # nothing should be masked for item in info: self.assertFalse(item.isMasked) + + def test_iterator_for_masked(self): + info = self._ws.detectorInfo() + it = iter(info) + item = next(it) + item.setMasked(True) + self.assertTrue(item.isMasked) + item.setMasked(False) + self.assertFalse(item.isMasked) def test_iteration_for_position(self): info = self._ws.detectorInfo() diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/MaskAngleTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/MaskAngleTest.py index 57e07ac09e36913207a8f0687fb26117d83f2f01..ba7d62fd58caee2d107f7220dac3751fe1ddffcf 100644 --- a/Framework/PythonInterface/test/python/plugins/algorithms/MaskAngleTest.py +++ b/Framework/PythonInterface/test/python/plugins/algorithms/MaskAngleTest.py @@ -17,7 +17,7 @@ class MaskAngleTest(unittest.TestCase): def testMaskAngle(self): w=WorkspaceCreationHelper.create2DWorkspaceWithFullInstrument(30,5,False,False) AnalysisDataService.add('w',w) - masklist = MaskAngle(w,10,20) + masklist = MaskAngle(w,MinAngle=10,MaxAngle=20) detInfo = w.detectorInfo() for i in arange(w.getNumberHistograms()): if (i<9) or (i>18): diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/WANDPowderReductionTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/WANDPowderReductionTest.py index c2e890f55927f96e49a16e70a9a0f9b8da59b8ae..dbf44069da87890e1105c02ce9477daddde41970 100644 --- a/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/WANDPowderReductionTest.py +++ b/Framework/PythonInterface/test/python/plugins/algorithms/WorkflowAlgorithms/WANDPowderReductionTest.py @@ -130,11 +130,11 @@ class WANDPowderReductionTest(unittest.TestCase): x = pd_out4.extractX() y = pd_out4.extractY() - self.assertAlmostEqual(x.min(), 0.53479223) - self.assertAlmostEqual(x.max(), 3.21684994) - self.assertAlmostEqual(y.min(), 0) - self.assertAlmostEqual(y.max(), 19.9948756) - self.assertAlmostEqual(x[0,y.argmax()], 2.9122841) + self.assertAlmostEqual(x.min(), 0.53479223, places=4) + self.assertAlmostEqual(x.max(), 3.21684994, places=4) + self.assertAlmostEqual(y.min(), 0, places=4) + self.assertAlmostEqual(y.max(), 19.9948756, places=4) + self.assertAlmostEqual(x[0,y.argmax()], 2.9122841, places=4) # data, cal and background, scale background pd_out4=WANDPowderReduction(InputWorkspace=data, diff --git a/Framework/PythonInterface/test/python/plugins/functions/MsdGaussTest.py b/Framework/PythonInterface/test/python/plugins/functions/MsdGaussTest.py index 60f0cba3d67a5d7c06789a26dc548e2eff9047b5..2126d5f4edc9f23ae6b2f98bf2dceced5a42265a 100644 --- a/Framework/PythonInterface/test/python/plugins/functions/MsdGaussTest.py +++ b/Framework/PythonInterface/test/python/plugins/functions/MsdGaussTest.py @@ -23,15 +23,15 @@ class MsdGaussTest(unittest.TestCase): input = np.array([[0, 1], [2, 3]]) expected = np.array([[1., 0.95122942], [0.81873075, 0.63762815]]) tolerance = 0.000001 - status, output = check_output("MsdGauss", input, expected, tolerance, Height=1.0, MSD=0.05) + status, output = check_output("MsdGauss", input, expected, tolerance, Height=1.0, Msd=0.05) if not status: self.fail("Computed output " + str(output) + " from input " + str(input) + " is not equal to the expected output: " + str(expected)) def test_use_in_fit(self): - workspace = create_test_workspace(create_model("MsdGauss", Height=1.0, MSD=0.05), 1000) - function_string = create_function_string("MsdGauss", Height=1.0, MSD=0.05) + workspace = create_test_workspace(create_model("MsdGauss", Height=1.0, Msd=0.05), 1000) + function_string = create_function_string("MsdGauss", Height=1.0, Msd=0.05) Fit(Function=function_string, InputWorkspace=workspace, StartX=1.2, EndX=1200) diff --git a/Framework/PythonInterface/test/python/plugins/functions/MsdPetersTest.py b/Framework/PythonInterface/test/python/plugins/functions/MsdPetersTest.py index 62797361d291a8747625b2e835738f7f18b532bc..86ff0730ccdaf2d7c59367c7aa96855ba4c57474 100644 --- a/Framework/PythonInterface/test/python/plugins/functions/MsdPetersTest.py +++ b/Framework/PythonInterface/test/python/plugins/functions/MsdPetersTest.py @@ -23,15 +23,15 @@ class MsdPetersTest(unittest.TestCase): input = np.array([[0, 1], [2, 3]]) expected = np.array([[1., 0.99173554], [0.96774194, 0.93023256]]) tolerance = 0.000001 - status, output = check_output("MsdPeters", input, expected, tolerance, Height=1.0, MSD=0.05, Beta=1.0) + status, output = check_output("MsdPeters", input, expected, tolerance, Height=1.0, Msd=0.05, Beta=1.0) if not status: self.fail("Computed output " + str(output) + " from input " + str(input) + " is not equal to the expected output: " + str(expected)) def test_use_in_fit(self): - workspace = create_test_workspace(create_model("MsdPeters", Height=1.0, MSD=0.05, Beta=1.0), 1000) - function_string = create_function_string("MsdPeters", Height=1.0, MSD=0.05, Beta=1.0) + workspace = create_test_workspace(create_model("MsdPeters", Height=1.0, Msd=0.05, Beta=1.0), 1000) + function_string = create_function_string("MsdPeters", Height=1.0, Msd=0.05, Beta=1.0) Fit(Function=function_string, InputWorkspace=workspace, StartX=1.2, EndX=1200) diff --git a/Framework/PythonInterface/test/python/plugins/functions/MsdYiTest.py b/Framework/PythonInterface/test/python/plugins/functions/MsdYiTest.py index 2558863e09090090db257620551250cf09e50232..4a1e5431d573283d2c73a486801b50ac2adc5d42 100644 --- a/Framework/PythonInterface/test/python/plugins/functions/MsdYiTest.py +++ b/Framework/PythonInterface/test/python/plugins/functions/MsdYiTest.py @@ -21,18 +21,30 @@ class MsdYiTest(unittest.TestCase): def test_function_output(self): input = np.array([[1, 2], [3, 4]]) - expected = np.array([[0.03616947, 0.53117559], [1.46726692, 3.69882113]]) + expected = np.array( + [[1.00547492, 1.18215301], [1.97145491, 3.98690068]]) tolerance = 0.000001 - status, output = check_output("MsdYi", input, expected, tolerance, Height=1.0, MSD=0.05, Sigma=1.0) + status, output = check_output( + "MsdYi", input, expected, tolerance, Height=1.0, Msd=0.05, Sigma=1.0) if not status: self.fail("Computed output " + str(output) + " from input " + str(input) + " is not equal to the expected output: " + str(expected)) def test_use_in_fit(self): - workspace = create_test_workspace(create_model("MsdYi", Height=1.0, MSD=0.05, Sigma=1.0), 1000) - function_string = create_function_string("MsdYi", Height=1.0, MSD=0.05, Sigma=1.0) - Fit(Function=function_string, InputWorkspace=workspace, StartX=1.2, EndX=1200) + workspace = create_test_workspace( + create_model( + "MsdYi", + Height=1.0, + Msd=0.05, + Sigma=1.0), + 1000) + function_string = create_function_string( + "MsdYi", Height=1.0, Msd=0.05, Sigma=1.0) + Fit(Function=function_string, + InputWorkspace=workspace, + StartX=1.2, + EndX=1200) if __name__ == '__main__': diff --git a/MantidPlot/src/ApplicationWindow.cpp b/MantidPlot/src/ApplicationWindow.cpp index a18e2353f52c4e7d7395de4895651208ffd79c26..dd8f20c9c2e5f52e95df87c65cc9e65ace8d6e4b 100644 --- a/MantidPlot/src/ApplicationWindow.cpp +++ b/MantidPlot/src/ApplicationWindow.cpp @@ -16667,8 +16667,13 @@ void ApplicationWindow::onAboutToStart() { resultsLog->scrollToTop(); // Kick off project recovery - g_log.debug("Starting project autosaving."); - checkForProjectRecovery(); + if (Mantid::Kernel::ConfigService::Instance().getString( + "projectRecovery.enabled") == "true") { + g_log.debug("Starting project autosaving."); + checkForProjectRecovery(); + } else { + g_log.debug("Project Recovery is disabled."); + } } /** diff --git a/MantidPlot/src/ProjectRecovery.cpp b/MantidPlot/src/ProjectRecovery.cpp index 0bb88fea74dc07c71f590369138f9ff39eb5a674..1c642ccccc67372f1aebafc38da4b4eddb0cb8e4 100644 --- a/MantidPlot/src/ProjectRecovery.cpp +++ b/MantidPlot/src/ProjectRecovery.cpp @@ -57,10 +57,6 @@ boost::optional<T> getConfigValue(const std::string &key) { return Mantid::Kernel::ConfigService::Instance().getValue<T>(key); } -boost::optional<bool> getConfigBool(const std::string &key) { - return Mantid::Kernel::ConfigService::Instance().getValue<bool>(key); -} - /// Returns a string to the folder it should output to std::string getRecoveryFolderOutput() { static std::string appData = @@ -247,14 +243,9 @@ Poco::File addLockFile(const Poco::Path &lockFilePath) { const std::string OUTPUT_PROJ_NAME = "recovery.mantid"; -// Config keys -const std::string SAVING_ENABLED_CONFIG_KEY = "projectRecovery.enabled"; const std::string SAVING_TIME_KEY = "projectRecovery.secondsBetween"; const std::string NO_OF_CHECKPOINTS_KEY = "projectRecovery.numberOfCheckpoints"; -// Config values -bool SAVING_ENABLED = - getConfigBool(SAVING_ENABLED_CONFIG_KEY).get_value_or(false); const int SAVING_TIME = getConfigValue<int>(SAVING_TIME_KEY).get_value_or(60); // Seconds const int NO_OF_CHECKPOINTS = @@ -276,7 +267,6 @@ namespace MantidQt { */ ProjectRecovery::ProjectRecovery(ApplicationWindow *windowHandle) : m_backgroundSavingThread(), m_stopBackgroundThread(true), - m_configKeyObserver(*this, &ProjectRecovery::configKeyChanged), m_windowPtr(windowHandle) {} /// Destructor which also stops any background threads currently in progress @@ -381,22 +371,6 @@ std::thread ProjectRecovery::createBackgroundThread() { return std::thread([this] { projectSavingThreadWrapper(); }); } -/// Callback for POCO when a config change had fired for the enabled key -void ProjectRecovery::configKeyChanged( - Mantid::Kernel::ConfigValChangeNotification_ptr notif) { - if (notif->key() != (SAVING_ENABLED_CONFIG_KEY)) { - return; - } - - if (notif->curValue() == "True") { - SAVING_ENABLED = true; - startProjectSaving(); - } else { - SAVING_ENABLED = false; - stopProjectSaving(); - } -} - void ProjectRecovery::compileRecoveryScript(const Poco::Path &inputFolder, const Poco::Path &outputFile) { const std::string algName = "OrderWorkspaceHistory"; @@ -476,10 +450,6 @@ void ProjectRecovery::startProjectSaving() { // Close the existing thread first stopProjectSaving(); - if (!SAVING_ENABLED) { - return; - } - // Spin up a new thread { std::lock_guard<std::mutex> lock(m_notifierMutex); @@ -492,6 +462,7 @@ void ProjectRecovery::startProjectSaving() { /// Stops any existing background threads which are running void ProjectRecovery::stopProjectSaving() { { + std::lock_guard<std::mutex> lock(m_notifierMutex); m_stopBackgroundThread = true; m_threadNotifier.notify_all(); diff --git a/MantidPlot/src/ProjectRecovery.h b/MantidPlot/src/ProjectRecovery.h index 971d5b521cd17e6d122fc535a9fb40335148844a..c957f550bff7350bfc56cc0d5925e0fa694b1800 100644 --- a/MantidPlot/src/ProjectRecovery.h +++ b/MantidPlot/src/ProjectRecovery.h @@ -77,9 +77,6 @@ private: /// Captures the current object in the background thread std::thread createBackgroundThread(); - /// Triggers when the config key is updated to a new value - void configKeyChanged(Mantid::Kernel::ConfigValChangeNotification_ptr notif); - /// Creates a recovery script based on all .py scripts in a folder void compileRecoveryScript(const Poco::Path &inputFolder, const Poco::Path &outputFile); @@ -129,10 +126,6 @@ private: /// Atomic to detect when the thread should fire or exit std::condition_variable m_threadNotifier; - /// Config observer to monitor the key - Poco::NObserver<ProjectRecovery, Mantid::Kernel::ConfigValChangeNotification> - m_configKeyObserver; - /// Pointer to main GUI window ApplicationWindow *m_windowPtr; diff --git a/docs/source/algorithms/MSDFit-v1.rst b/docs/source/algorithms/MSDFit-v1.rst index f7b51e2cda468e30da3ebfe74171c9a69af73817..4e01c6d43b5b051247d008e7ace9b83591a1d160 100644 --- a/docs/source/algorithms/MSDFit-v1.rst +++ b/docs/source/algorithms/MSDFit-v1.rst @@ -61,8 +61,8 @@ Output: A0: [ 0.87079958] A1: [ 0.03278263] Using Yi Model - A0: [ 0.75677983] - A1: [ 1.76943372] + A0: [ 0.95770532] + A1: [ 0.58819225] .. categories:: diff --git a/docs/source/release/v3.14.0/diffraction.rst b/docs/source/release/v3.14.0/diffraction.rst index 2097332f85cc399ab9256557f949bf6eeeb7feb4..1f696d5826987be49e4e0adcb57041bd22ff0f9e 100644 --- a/docs/source/release/v3.14.0/diffraction.rst +++ b/docs/source/release/v3.14.0/diffraction.rst @@ -56,6 +56,7 @@ Improvements - Focusing on Gem now crops values that would be divided by very small or zero vanadium values - Removed save_angles flag for Gem , as it was set by the texture mode. - Added save_all flag to Gem that is set to true by default, setting it to false disables the saving of .NXS files. +- Added subtract_empty_instrument flag to Gem that is true by default, setting it to false disables subrtracting the empty. - Changed spline coefficient so that the default for long_mode on and long_mode off can be set separately. Bugfixes diff --git a/docs/source/release/v3.14.0/indirect_inelastic.rst b/docs/source/release/v3.14.0/indirect_inelastic.rst index 44567e5958ffcdf0a4f416bdb9c632d8ca1a88d8..c9fd7a9a92b6c0e5eeb7ef7ee757a27b39b64fbb 100644 --- a/docs/source/release/v3.14.0/indirect_inelastic.rst +++ b/docs/source/release/v3.14.0/indirect_inelastic.rst @@ -49,6 +49,7 @@ Bugfixes message is now displayed. - The Probability Density Functions (PDF) workspaces for the FABADA minimiser in ConvFit no longer overwrite each other. Various other improvements in the display of the FABADA PDF's have also been finished. +- The expression for the Fit type Yi in MSDFit was incorrect and has now been corrected. Data Corrections Interface diff --git a/docs/source/release/v3.14.0/ui.rst b/docs/source/release/v3.14.0/ui.rst index f67ec7c58e608416535c4a514c30ef7155f8e571..6a9711a87deb9f7ed3f6a2778b0457daaa01ba5d 100644 --- a/docs/source/release/v3.14.0/ui.rst +++ b/docs/source/release/v3.14.0/ui.rst @@ -33,6 +33,7 @@ Changes - MantidPlot no longer checks for the existence of files in the "Recent Files" menu. Fixes case where files on slow mounted network drives can cause a lag on MantidPlot startup. - Workspaces now save locally as a number of how many workspaces have already been saved instead of workspace names - Project Recovery will now attempt to recover multiple instances of mantid that are ran at the same time. +- Project Recovery will now output less unhelpful logging information into the results log Bugfixes ######## @@ -40,6 +41,7 @@ Bugfixes - Project Recovery will actually recover fully cases where multiple workspaces were passed as a list to an algorithm (Fixes a known bug with GroupWorkspaces as well) - Project Recovery will now run normally when you select no or the recovery fails when recovering from a ungraceful exit. - When autosaving or saving a recovery checkpoint with the Instrument View open the results log would be filled with excess logging and no longer does this. +- Fixed an issue where Project Recovery would start regardless of the config options MantidPlot ---------- diff --git a/qt/python/mantidqt/widgets/instrumentview/CMakeLists.txt b/qt/python/mantidqt/widgets/instrumentview/CMakeLists.txt index aa4c801705de4c2ea50f89d61b01b23c25f6fe84..31f02d565474207434366a88f79f3c02a952f1c2 100644 --- a/qt/python/mantidqt/widgets/instrumentview/CMakeLists.txt +++ b/qt/python/mantidqt/widgets/instrumentview/CMakeLists.txt @@ -27,6 +27,6 @@ mtd_add_sip_module ( INSTALL_DIR ${LIB_DIR}/mantidqt/widgets/instrumentview LINUX_INSTALL_RPATH - "\$ORIGIN/.." + "\$ORIGIN/../../.." FOLDER Qt5 ) diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp index 57278280971452ae8d4e627f4e08044cc52df6ce..9d5a2233695520235761bba34c1dc18f26630079 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp +++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.cpp @@ -224,6 +224,8 @@ void IndirectFitAnalysisTab::connectDataAndSpectrumPresenters() { void IndirectFitAnalysisTab::connectDataAndFitBrowserPresenters() { connect(m_dataPresenter.get(), SIGNAL(dataChanged()), this, SLOT(updateBrowserFittingRange())); + connect(m_dataPresenter.get(), SIGNAL(dataChanged()), this, + SLOT(setBrowserWorkspace())); connect(m_fitPropertyBrowser, SIGNAL(startXChanged(double)), this, SLOT(setDataTableStartX(double))); connect(m_fitPropertyBrowser, SIGNAL(endXChanged(double)), this, @@ -371,6 +373,14 @@ void IndirectFitAnalysisTab::updateBrowserFittingRange() { setBrowserEndX(range.second); } +void IndirectFitAnalysisTab::setBrowserWorkspace() { + if (m_fittingModel->numberOfWorkspaces() != 0) { + auto const name = + m_fittingModel->getWorkspace(getSelectedDataIndex())->getName(); + m_fitPropertyBrowser->setWorkspaceName(QString::fromStdString(name)); + } +} + void IndirectFitAnalysisTab::setBrowserWorkspace(std::size_t dataIndex) { const auto name = m_fittingModel->getWorkspace(dataIndex)->getName(); m_fitPropertyBrowser->setWorkspaceName(QString::fromStdString(name)); diff --git a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h index 29fff8a4dae2fd9e0330aaac7c621b55003a8e29..ac9f4713d8dfa69f4821727037e6fa601274e0a0 100644 --- a/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h +++ b/qt/scientific_interfaces/Indirect/IndirectFitAnalysisTab.h @@ -171,6 +171,7 @@ protected slots: void setBrowserStartX(double startX); void setBrowserEndX(double endX); void updateBrowserFittingRange(); + void setBrowserWorkspace(); void setBrowserWorkspace(std::size_t dataIndex); void setBrowserWorkspaceIndex(std::size_t spectrum); void tableStartXChanged(double startX, std::size_t dataIndex, diff --git a/qt/widgets/common/src/FileDialogHandler.cpp b/qt/widgets/common/src/FileDialogHandler.cpp index 534fa30e020a3f1ddc142a7970de7f77bb3abdc3..16a9466dbc8ea02b68e845fcf8f571a2d2949f9d 100644 --- a/qt/widgets/common/src/FileDialogHandler.cpp +++ b/qt/widgets/common/src/FileDialogHandler.cpp @@ -12,35 +12,24 @@ #include <sstream> namespace { // anonymous namespace -const boost::regex FILE_EXT_REG_EXP{R"(^.+\s+\((\S+)\)$)"}; const QString ALL_FILES("All Files (*)"); QString getExtensionFromFilter(const QString &selectedFilter) { - // empty returns empty - if (selectedFilter.isEmpty()) { - return QString(""); - } - + QString extension; // search for single extension + static const boost::regex FILE_EXT_REG_EXP{R"(\*\.[[:word:]]+)"}; boost::smatch result; - if (boost::regex_search(selectedFilter.toStdString(), result, - FILE_EXT_REG_EXP) && - result.size() == 2) { + const auto filter = selectedFilter.toStdString(); + if (boost::regex_search(filter, result, FILE_EXT_REG_EXP)) { // clang fails to cast result[1] to std::string. - std::string output = result[1]; - auto extension = QString::fromStdString(output); + const std::string output = result.str(0); + extension = QString::fromStdString(output); if (extension.startsWith("*")) - return extension.remove(0, 1); - else - return extension; - } else { - // failure to match suggests multi-extension filter - std::stringstream msg; - msg << "Failed to determine single extension from \"" - << selectedFilter.toStdString() << "\""; - throw std::runtime_error(msg.str()); + extension.remove(0, 1); } + return extension; } + } // anonymous namespace namespace MantidQt { @@ -60,10 +49,9 @@ QString getSaveFileName(QWidget *parent, QString selectedFilter; // create the file browser - QString filename = QFileDialog::getSaveFileName( + const QString filename = QFileDialog::getSaveFileName( parent, caption, AlgorithmInputHistory::Instance().getPreviousDirectory(), filter, &selectedFilter, options); - return addExtension(filename, selectedFilter); } @@ -91,22 +79,22 @@ QString getFilter(const Mantid::Kernel::Property *baseProp) { // multiple file version const auto *multiProp = dynamic_cast<const Mantid::API::MultipleFileProperty *>(baseProp); - if (bool(multiProp)) + if (multiProp) return getFilter(multiProp->getExts()); // regular file version const auto *singleProp = dynamic_cast<const Mantid::API::FileProperty *>(baseProp); // The allowed values in this context are file extensions - if (bool(singleProp)) + if (singleProp) return getFilter(singleProp->allowedValues()); // otherwise only the all files exists return ALL_FILES; } -/** For file dialogs. Have each filter on a separate line with the default as - * the first. +/** For file dialogs. Have each filter on a separate line with Data Files + * as the first and All Files as the last * * @param exts :: vector of extensions * @return a string that filters files by extenstions diff --git a/qt/widgets/common/test/FileDialogHandlerTest.h b/qt/widgets/common/test/FileDialogHandlerTest.h index 480c6ededec10c06ea1ddde77995b9873a63809f..81af5b9af54ec5ae64db4d4bcdcc8fa03fd4f224 100644 --- a/qt/widgets/common/test/FileDialogHandlerTest.h +++ b/qt/widgets/common/test/FileDialogHandlerTest.h @@ -17,37 +17,46 @@ public: const QString singleExt(".nxs (*.nxs)"); const QString nexusResult("/tmp/testing.nxs"); - auto result1 = MantidQt::API::FileDialogHandler::addExtension( + auto result = MantidQt::API::FileDialogHandler::addExtension( QString::fromStdString("/tmp/testing"), singleExt); - TS_ASSERT_EQUALS(nexusResult.toStdString(), result1.toStdString()); + TS_ASSERT_EQUALS(nexusResult.toStdString(), result.toStdString()); - auto result2 = MantidQt::API::FileDialogHandler::addExtension( + result = MantidQt::API::FileDialogHandler::addExtension( QString::fromStdString("/tmp/testing."), singleExt); - TS_ASSERT_EQUALS(nexusResult.toStdString(), result2.toStdString()); + TS_ASSERT_EQUALS(nexusResult.toStdString(), result.toStdString()); - auto result3 = + result = MantidQt::API::FileDialogHandler::addExtension(nexusResult, singleExt); - TS_ASSERT_EQUALS(nexusResult.toStdString(), result3.toStdString()); + TS_ASSERT_EQUALS(nexusResult.toStdString(), result.toStdString()); // don't override if it is already specified const QString singleH5("/tmp/testing.h5"); - auto result4 = + result = MantidQt::API::FileDialogHandler::addExtension(singleH5, singleExt); - TS_ASSERT_EQUALS(singleH5.toStdString(), result4.toStdString()); + TS_ASSERT_EQUALS(singleH5.toStdString(), result.toStdString()); // --- double extensions const QString doubleExt("JPEG (*.jpg *.jpeg)"); const QString jpegResult("/tmp/testing.jpg"); - // this can't work because you can't determine one extension - TS_ASSERT_THROWS(MantidQt::API::FileDialogHandler::addExtension( - QString::fromStdString("/tmp/testing"), doubleExt), - std::runtime_error); + // this picks the first extension in doubleExt + result = MantidQt::API::FileDialogHandler::addExtension( + QString::fromStdString("/tmp/testing"), doubleExt); + TS_ASSERT_EQUALS(jpegResult.toStdString(), result.toStdString()); // this shouldn't do anything - auto result5 = + result = MantidQt::API::FileDialogHandler::addExtension(jpegResult, doubleExt); - TS_ASSERT_EQUALS(jpegResult.toStdString(), result5.toStdString()); + TS_ASSERT_EQUALS(jpegResult.toStdString(), result.toStdString()); + + // Just the wildcard * + const QString wildcardExt("All files (*)"); + const QString wildcardResult("/tmp/testing"); + + // this shouldn't do anything + result = MantidQt::API::FileDialogHandler::addExtension( + QString::fromStdString("/tmp/testing"), wildcardExt); + TS_ASSERT_EQUALS(wildcardResult.toStdString(), result.toStdString()); } void test_getFileDialogFilter() { diff --git a/scripts/Diffraction/isis_powder/gem.py b/scripts/Diffraction/isis_powder/gem.py index 94934c9b3c8a43fa06db590b0a8fa20e32ad5f1e..2773d6a431ddec8017c9c8ddc2fea86d5ffceeb6 100644 --- a/scripts/Diffraction/isis_powder/gem.py +++ b/scripts/Diffraction/isis_powder/gem.py @@ -29,15 +29,16 @@ class Gem(AbstractInst): # Public API def focus(self, **kwargs): - self._switch_texture_mode_specific_inst_settings(kwargs.get("texture_mode")) self._inst_settings.update_attributes(kwargs=kwargs) + self._switch_texture_mode_specific_inst_settings(kwargs.get("texture_mode")) + return self._focus( run_number_string=self._inst_settings.run_number, do_van_normalisation=self._inst_settings.do_van_norm, do_absorb_corrections=self._inst_settings.do_absorb_corrections) def create_vanadium(self, **kwargs): - self._switch_texture_mode_specific_inst_settings(kwargs.get("texture_mode")) self._inst_settings.update_attributes(kwargs=kwargs) + self._switch_texture_mode_specific_inst_settings(kwargs.get("texture_mode")) return self._create_vanadium(run_number_string=self._inst_settings.run_in_range, do_absorb_corrections=self._inst_settings.do_absorb_corrections) @@ -50,6 +51,9 @@ class Gem(AbstractInst): " set the following argument: " + kwarg_name) self._sample_details = sample_details_obj + def should_subtract_empty_inst(self): + return self._inst_settings.subtract_empty_inst + # Private methods def _get_run_details(self, run_number_string): @@ -176,8 +180,10 @@ class Gem(AbstractInst): if mode is None and hasattr(self._inst_settings, "texture_mode"): mode = self._inst_settings.texture_mode save_all = not hasattr(self._inst_settings, "save_all") - self._inst_settings.update_attributes(advanced_config=gem_advanced_config.get_mode_specific_variables(mode, - save_all), + adv_config_variables = gem_advanced_config.get_mode_specific_variables(mode, save_all) + if self.should_subtract_empty_inst() is None: + adv_config_variables.update({"subtract_empty_instrument": True}) + self._inst_settings.update_attributes(advanced_config=adv_config_variables, suppress_warnings=True) diff --git a/scripts/Diffraction/isis_powder/gem_routines/gem_param_mapping.py b/scripts/Diffraction/isis_powder/gem_routines/gem_param_mapping.py index 79323970d80aa090720660273f4d8dbb5c8a6393..188b23b556ab7c2e8a7b1c3599ebd3b429c19904 100644 --- a/scripts/Diffraction/isis_powder/gem_routines/gem_param_mapping.py +++ b/scripts/Diffraction/isis_powder/gem_routines/gem_param_mapping.py @@ -35,6 +35,7 @@ attr_mapping = \ ParamMapEntry(ext_name="save_maud_calib", int_name="save_maud_calib"), ParamMapEntry(ext_name="save_maud", int_name="save_maud"), ParamMapEntry(ext_name="spline_coefficient", int_name="spline_coeff"), + ParamMapEntry(ext_name="subtract_empty_instrument", int_name="subtract_empty_inst", optional =True), ParamMapEntry(ext_name="suffix", int_name="suffix", optional=True), ParamMapEntry(ext_name="texture_mode", int_name="texture_mode", optional=True), ParamMapEntry(ext_name="output_directory", int_name="output_dir"), diff --git a/scripts/Interface/ui/sans_isis/sans_data_processor_gui.py b/scripts/Interface/ui/sans_isis/sans_data_processor_gui.py index 2d519d2474eae58f6cd4dea73f6137ba9458f0bf..56961a6426239b6ccc8394dcc43cd45bf4b15dea 100644 --- a/scripts/Interface/ui/sans_isis/sans_data_processor_gui.py +++ b/scripts/Interface/ui/sans_isis/sans_data_processor_gui.py @@ -28,7 +28,7 @@ except ImportError: from . import ui_sans_data_processor_window as ui_sans_data_processor_window from sans.common.enums import (ReductionDimensionality, OutputMode, SaveType, SANSInstrument, - RangeStepType, SampleShape, ReductionMode, FitType) + RangeStepType, ReductionMode, FitType) from sans.common.file_information import SANSFileInformationFactory from sans.gui_logic.gui_common import (get_reduction_mode_from_gui_selection, get_reduction_mode_strings_for_gui, get_string_for_gui_from_reduction_mode, GENERIC_SETTINGS, load_file, @@ -107,6 +107,10 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S def on_multi_period_selection(self, show_periods): pass + @abstractmethod + def on_sample_geometry_selection(self, show_geometry): + pass + @abstractmethod def on_data_changed(self, row, column, new_value, old_value): pass @@ -276,6 +280,7 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S self._setup_main_tab() self.multi_period_check_box.stateChanged.connect(self._on_multi_period_selection) + self.sample_geometry_checkbox.stateChanged.connect(self._on_sample_geometry_selection) self.wavelength_step_type_combo_box.currentIndexChanged.connect(self._on_wavelength_step_type_changed) @@ -334,6 +339,7 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S self.q_resolution_moderator_file_push_button.clicked.connect(self._on_load_moderator_file) self.wavelength_stacked_widget.setCurrentIndex(0) + self.hide_geometry() return True @@ -358,7 +364,8 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S self.data_processor_table = MantidQt.MantidWidgets.Batch.JobTreeView( ["Sample Scatter", "ssp", "Sample Transmission", "stp", "Sample Direct", "sdp","Can Scatter", "csp", - "Can Transmission", "ctp", "Can Direct", "cdp", "Output Name", "User File", "Sample Thickness", "Options"] + "Can Transmission", "ctp", "Can Direct", "cdp", "Output Name", "User File", "Sample Thickness", + "Sample Height", "Sample Width", "Sample Shape", "Options"] , self.cell(""), self) self.data_processor_table.setRootIsDecorated(False) @@ -713,6 +720,9 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S def _on_multi_period_selection(self): self._call_settings_listeners(lambda listener: listener.on_multi_period_selection(self.is_multi_period_view())) + def _on_sample_geometry_selection(self): + self._call_settings_listeners(lambda listener: listener.on_sample_geometry_selection(self.is_sample_geometry())) + def _on_manage_directories(self): self._call_settings_listeners(lambda listener: listener.on_manage_directories()) @@ -780,6 +790,12 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S def set_multi_period_view_mode(self, mode): self.multi_period_check_box.setChecked(mode) + def is_sample_geometry(self): + return self.sample_geometry_checkbox.isChecked() + + def set_sample_geometry_mode(self, mode): + self.sample_geometry_checkbox.setChecked(mode) + # $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # START ACCESSORS @@ -1119,23 +1135,6 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S # ------------------------------------------------------------------------------------------------------------------ # Scale Group # ------------------------------------------------------------------------------------------------------------------ - @property - def sample_shape(self): - geometry_as_string = self.geometry_combo_box.currentText().encode('utf-8') - # Either the selection is something that can be converted to a SampleShape or we need to read from file - try: - return SampleShape.from_string(geometry_as_string) - except RuntimeError: - return None - - @sample_shape.setter - def sample_shape(self, value): - if value is None: - # Set to the default - self.geometry_combo_box.setCurrentIndex(0) - else: - self.update_gui_combo_box(value=value, expected_type=SampleShape, combo_box="geometry_combo_box") - @property def absolute_scale(self): return self.get_simple_line_edit_field(line_edit="absolute_scale_line_edit", expected_type=float) @@ -1144,30 +1143,6 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S def absolute_scale(self, value): self.update_simple_line_edit_field(line_edit="absolute_scale_line_edit", value=value) - @property - def sample_height(self): - return self.get_simple_line_edit_field(line_edit="height_line_edit", expected_type=float) - - @sample_height.setter - def sample_height(self, value): - self.update_simple_line_edit_field(line_edit="height_line_edit", value=value) - - @property - def sample_width(self): - return self.get_simple_line_edit_field(line_edit="width_line_edit", expected_type=float) - - @sample_width.setter - def sample_width(self, value): - self.update_simple_line_edit_field(line_edit="width_line_edit", value=value) - - @property - def sample_thickness(self): - return self.get_simple_line_edit_field(line_edit="thickness_line_edit", expected_type=float) - - @sample_thickness.setter - def sample_thickness(self, value): - self.update_simple_line_edit_field(line_edit="thickness_line_edit", value=value) - @property def z_offset(self): return self.get_simple_line_edit_field(line_edit="z_offset_line_edit", expected_type=float) @@ -1727,9 +1702,6 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S self.wavelength_step_line_edit.setValidator(positive_double_validator) self.absolute_scale_line_edit.setValidator(double_validator) - self.height_line_edit.setValidator(positive_double_validator) - self.width_line_edit.setValidator(positive_double_validator) - self.thickness_line_edit.setValidator(positive_double_validator) self.z_offset_line_edit.setValidator(double_validator) # -------------------------------- @@ -1796,10 +1768,6 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S self.wavelength_slices_line_edit.setText("") self.absolute_scale_line_edit.setText("") - self.geometry_combo_box.setCurrentIndex(0) - self.height_line_edit.setText("") - self.width_line_edit.setText("") - self.thickness_line_edit.setText("") self.z_offset_line_edit.setText("") # -------------------------------- @@ -1969,3 +1937,13 @@ class SANSDataProcessorGui(QtGui.QMainWindow, ui_sans_data_processor_window.Ui_S self.data_processor_table.showColumn(7) self.data_processor_table.showColumn(9) self.data_processor_table.showColumn(11) + + def show_geometry(self): + self.data_processor_table.showColumn(15) + self.data_processor_table.showColumn(16) + self.data_processor_table.showColumn(17) + + def hide_geometry(self): + self.data_processor_table.hideColumn(15) + self.data_processor_table.hideColumn(16) + self.data_processor_table.hideColumn(17) diff --git a/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui b/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui index 6ea10a07484e75168eb18a8032ebf6611a75db3e..f3df4cf3256f696bf48cdbf9f13d415a7d53f9b8 100644 --- a/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui +++ b/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui @@ -351,6 +351,13 @@ QGroupBox::title { </item> <item> <layout class="QGridLayout" name="gridLayout_7"> + <item row="0" column="2"> + <widget class="QCheckBox" name="multi_period_check_box"> + <property name="text"> + <string>Multi-period</string> + </property> + </widget> + </item> <item row="0" column="3"> <widget class="QGroupBox" name="reduction_dimensionality_group_box"> <property name="maximumSize"> @@ -380,20 +387,7 @@ QGroupBox::title { </layout> </widget> </item> - <item row="0" column="1"> - <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 row="0" column="4" rowspan="2"> + <item row="0" column="4" rowspan="3"> <widget class="QGroupBox" name="save_group_box"> <property name="styleSheet"> <string notr="true"/> @@ -540,10 +534,23 @@ QGroupBox::title { </layout> </widget> </item> - <item row="0" column="2"> - <widget class="QCheckBox" name="multi_period_check_box"> + <item row="0" column="1"> + <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 row="1" column="2"> + <widget class="QCheckBox" name="sample_geometry_checkbox"> <property name="text"> - <string>Multi-period</string> + <string>Sample Geometry</string> </property> </widget> </item> @@ -827,46 +834,6 @@ QGroupBox::title { <layout class="QGridLayout" name="gridLayout_5"> <item row="0" column="0"> <layout class="QGridLayout" name="gridLayout_4"> - <item row="3" column="0"> - <widget class="QLabel" name="width_label"> - <property name="toolTip"> - <string><html><head/><body><p>The width of the sample. If this input is left empty, then the value stored in the metadata of the file is used.</p></body></html></string> - </property> - <property name="text"> - <string>Width [mm]</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="height_label"> - <property name="toolTip"> - <string><html><head/><body><p>The height of the sample. If this input is left empty, then the value stored in the metadata of the file is used.</p></body></html></string> - </property> - <property name="text"> - <string>Height [mm]</string> - </property> - </widget> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="thickness_label"> - <property name="toolTip"> - <string><html><head/><body><p>The thickness of the sample. If this input is left empty, then the value stored in the metadata of the file is used.</p></body></html></string> - </property> - <property name="text"> - <string>Thickness [mm]</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="geometry_label"> - <property name="toolTip"> - <string><html><head/><body><p>The geometry of the sample. If this is left at <span style=" font-style:italic;">Read from file </span>then the value stored in the metadata of the file is used.</p></body></html></string> - </property> - <property name="text"> - <string>Geometry</string> - </property> - </widget> - </item> <item row="0" column="0"> <widget class="QLabel" name="absolute_scale_label"> <property name="toolTip"> @@ -877,16 +844,6 @@ QGroupBox::title { </property> </widget> </item> - <item row="5" column="0"> - <widget class="QLabel" name="z_offset_label"> - <property name="toolTip"> - <string><html><head/><body><p>The z offset of the sample position.</p></body></html></string> - </property> - <property name="text"> - <string>Z Offset</string> - </property> - </widget> - </item> <item row="0" column="1"> <widget class="QLineEdit" name="absolute_scale_line_edit"> <property name="toolTip"> @@ -894,35 +851,17 @@ QGroupBox::title { </property> </widget> </item> - <item row="2" column="1"> - <widget class="QLineEdit" name="height_line_edit"> - <property name="toolTip"> - <string><html><head/><body><p>The height of the sample. If this input is left empty, then the value stored in the metadata of the file is used.</p></body></html></string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QLineEdit" name="width_line_edit"> + <item row="1" column="0"> + <widget class="QLabel" name="z_offset_label"> <property name="toolTip"> - <string><html><head/><body><p>The width of the sample. If this input is left empty, then the value stored in the metadata of the file is used.</p></body></html></string> + <string><html><head/><body><p>The z offset of the sample position.</p></body></html></string> </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QLineEdit" name="thickness_line_edit"> - <property name="toolTip"> - <string><html><head/><body><p>The thickness of the sample. If this input is left empty, then the value stored in the metadata of the file is used.</p></body></html></string> + <property name="text"> + <string>Z Offset</string> </property> </widget> </item> <item row="1" column="1"> - <widget class="QComboBox" name="geometry_combo_box"> - <property name="toolTip"> - <string><html><head/><body><p>The geometry of the sample. If this is left at <span style=" font-style:italic;">Read from file </span>then the value stored in the metadata of the file is used.</p></body></html></string> - </property> - </widget> - </item> - <item row="5" column="1"> <widget class="QLineEdit" name="z_offset_line_edit"> <property name="toolTip"> <string><html><head/><body><p>The z offset of the sample position.</p></body></html></string> @@ -2287,10 +2226,6 @@ QGroupBox::title { <tabstop>event_binning_group_box</tabstop> <tabstop>event_binning_line_edit</tabstop> <tabstop>absolute_scale_line_edit</tabstop> - <tabstop>geometry_combo_box</tabstop> - <tabstop>height_line_edit</tabstop> - <tabstop>width_line_edit</tabstop> - <tabstop>thickness_line_edit</tabstop> <tabstop>z_offset_line_edit</tabstop> <tabstop>mask_file_input_line_edit</tabstop> <tabstop>mask_file_browse_push_button</tabstop> diff --git a/scripts/Interface/ui/sans_isis/work_handler.py b/scripts/Interface/ui/sans_isis/work_handler.py index 86c6066ad3aad8aca0af84615885ff8712d10ce0..a093cd5142b5e9c5318f1a66588a8ce399d084f5 100644 --- a/scripts/Interface/ui/sans_isis/work_handler.py +++ b/scripts/Interface/ui/sans_isis/work_handler.py @@ -53,6 +53,7 @@ class WorkHandler(object): self.thread_pool = None self._listener = {} self._worker = {} + self.thread_pool = QThreadPool() def _add_listener(self, listener, process_id, id): if not isinstance(listener, WorkHandler.WorkListener): @@ -97,7 +98,7 @@ class WorkHandler(object): self._worker.update({process_id: {'id': id, 'worker': worker}}) - QThreadPool.globalInstance().start(self._worker[process_id]['worker']) + self.thread_pool.start(self._worker[process_id]['worker']) def remove_already_processing(self, id): """ @@ -109,9 +110,8 @@ class WorkHandler(object): self._listener.pop(key) self._worker.pop(key) - @staticmethod - def wait_for_done(): - QThreadPool.globalInstance().waitForDone() + def wait_for_done(self): + self.thread_pool.waitForDone() def __eq__(self, other): return self.__dict__ == other.__dict__ diff --git a/scripts/SANS/sans/gui_logic/models/create_state.py b/scripts/SANS/sans/gui_logic/models/create_state.py index 1401ad9d72694dc2114a4bf60a578bd3faec9476..6504e0fb6e1a2b45b4858af6b303521caadbdabf 100644 --- a/scripts/SANS/sans/gui_logic/models/create_state.py +++ b/scripts/SANS/sans/gui_logic/models/create_state.py @@ -31,6 +31,8 @@ def create_states(state_model, table_model, instrument, facility, row_index=None gui_state_director = GuiStateDirector(table_model, state_model, facility) for row in rows: + if file_lookup: + table_model.wait_for_file_information(row) state = _create_row_state(row, table_model, state_model, facility, instrument, file_lookup, gui_state_director) if isinstance(state, State): states.update({row: state}) @@ -43,6 +45,13 @@ def _create_row_state(row, table_model, state_model, facility, instrument, file_ try: sans_logger.information("Generating state for row {}".format(row)) state = None + + table_entry = table_model.get_table_entry(row) + if not table_entry.file_information and file_lookup: + error_message = "Trying to find the SANS file {0}, but cannot find it. Make sure that " \ + "the relevant paths are added and the correct instrument is selected." + raise RuntimeError(error_message.format(table_entry.sample_scatter)) + if not __is_empty_row(row, table_model): row_user_file = table_model.get_row_user_file(row) if row_user_file: diff --git a/scripts/SANS/sans/gui_logic/models/diagnostics_page_model.py b/scripts/SANS/sans/gui_logic/models/diagnostics_page_model.py index d8b65cd951c08e0c1e8c9a4ba063904e37b7342e..87f94b7cc311adb32ea3d2a649bc35e5c117fcb1 100644 --- a/scripts/SANS/sans/gui_logic/models/diagnostics_page_model.py +++ b/scripts/SANS/sans/gui_logic/models/diagnostics_page_model.py @@ -165,7 +165,7 @@ def get_detector_size_from_sans_file(state, detector): def create_state(state_model_with_view_update, file, period, facility): table_row = TableIndexModel(file, period, '', '', '', '', '', '', '', '', '', '') table = TableModel() - table.add_table_entry(0, table_row) + table.add_table_entry_no_thread_or_signal(0, table_row) gui_state_director = GuiStateDirector(table, state_model_with_view_update, facility) diff --git a/scripts/SANS/sans/gui_logic/models/table_model.py b/scripts/SANS/sans/gui_logic/models/table_model.py index 57306215f4da28cdcb4bbf8567f7e796f88abd8c..71042f752d8beb174d5f179e35fcdb1edb53acd3 100644 --- a/scripts/SANS/sans/gui_logic/models/table_model.py +++ b/scripts/SANS/sans/gui_logic/models/table_model.py @@ -17,10 +17,11 @@ import re from sans.common.constants import ALL_PERIODS from sans.gui_logic.models.basic_hint_strategy import BasicHintStrategy -from sans.common.enums import RowState +from sans.common.enums import RowState, SampleShape import functools from sans.gui_logic.presenter.create_file_information import create_file_information from ui.sans_isis.work_handler import WorkHandler +from sans.common.file_information import SANSFileInformationFactory class TableModel(object): @@ -28,7 +29,8 @@ class TableModel(object): "sample_transmission_period", "sample_direct", "sample_direct_period", "can_scatter", "can_scatter_period", "can_transmission", "can_transmission_period", "can_direct", "can_direct_period", - "output_name", "user_file", "sample_thickness", "options_column_model"] + "output_name", "user_file", "sample_thickness", "sample_height", "sample_width", + "sample_shape", "options_column_model"] THICKNESS_ROW = 14 def __init__(self): @@ -94,7 +96,8 @@ class TableModel(object): if not self._table_entries: row_index_model = self.create_empty_row() self.append_table_entry(row_index_model) - self.notify_subscribers() + else: + self.notify_subscribers() def replace_table_entries(self, row_to_replace_index, rows_to_insert): self.remove_table_entries(row_to_replace_index) @@ -105,7 +108,6 @@ class TableModel(object): self._table_entries = [] row_index_model = self.create_empty_row() self.append_table_entry(row_index_model) - self.notify_subscribers() def get_number_of_rows(self): return len(self._table_entries) @@ -155,25 +157,37 @@ class TableModel(object): entry = self._table_entries[row] if entry.is_empty(): continue - success_callback = functools.partial(self.update_thickness_from_file_information, - entry.id) + entry.file_finding = True + success_callback = functools.partial(self.update_thickness_from_file_information, entry.id) + error_callback = functools.partial(self.failure_handler, entry.id) create_file_information(entry.sample_scatter, error_callback, success_callback, self.work_handler, entry.id) def failure_handler(self, id, error): row = self.get_row_from_id(id) - self.update_table_entry(row, self.THICKNESS_ROW, '') self._table_entries[row].update_attribute('file_information', '') + self._table_entries[row].update_attribute('sample_thickness', '') + self._table_entries[row].update_attribute('sample_height', '') + self._table_entries[row].update_attribute('sample_width', '') + self._table_entries[row].update_attribute('sample_shape', '') + self._table_entries[row].file_finding = False self.set_row_to_error(row, str(error[1])) def update_thickness_from_file_information(self, id, file_information): row = self.get_row_from_id(id) if file_information: rounded_file_thickness = round(file_information.get_thickness(), 2) - self.update_table_entry(row, self.THICKNESS_ROW, rounded_file_thickness) + rounded_file_height = round(file_information.get_height(), 2) + rounded_file_width = round(file_information.get_width(), 2) + self._table_entries[row].update_attribute('file_information', file_information) - self.notify_subscribers() + self._table_entries[row].update_attribute('sample_thickness', rounded_file_thickness) + self._table_entries[row].update_attribute('sample_height', rounded_file_height) + self._table_entries[row].update_attribute('sample_width', rounded_file_width) + self._table_entries[row].update_attribute('sample_shape', file_information.get_shape()) + self._table_entries[row].file_finding = False + self.reset_row_state(row) def subscribe_to_model_changes(self, subscriber): self._subscriber_list.append(subscriber) @@ -191,14 +205,36 @@ class TableModel(object): return row return None - def wait_for_done(self): + def wait_for_file_finding_done(self): self.work_handler.wait_for_done() + def wait_for_file_information(self, row): + if self._table_entries[row].file_finding: + self.wait_for_file_finding_done() + + def add_table_entry_no_thread_or_signal(self, row, table_index_model): + table_index_model.id = self._id_count + self._id_count += 1 + self._table_entries.insert(row, table_index_model) + if row >= self.get_number_of_rows(): + row = self.get_number_of_rows() - 1 + + entry = self._table_entries[row] + file_information_factory = SANSFileInformationFactory() + file_information = file_information_factory.create_sans_file_information(entry.sample_scatter) + self.update_thickness_from_file_information(entry.id, file_information) + def __eq__(self, other): - return self.__dict__ == other.__dict__ + return self.equal_dicts(self.__dict__, other.__dict__, ['work_handler']) def __ne__(self, other): - return self.__dict__ != other.__dict__ + return not self.equal_dicts(self.__dict__, other.__dict__, ['work_handler']) + + @staticmethod + def equal_dicts(d1, d2, ignore_keys): + d1_filtered = dict((k, v) for k, v in d1.items() if k not in ignore_keys) + d2_filtered = dict((k, v) for k, v in d2.items() if k not in ignore_keys) + return d1_filtered == d2_filtered class TableIndexModel(object): @@ -208,7 +244,8 @@ class TableIndexModel(object): can_scatter, can_scatter_period, can_transmission, can_transmission_period, can_direct, can_direct_period, - output_name="", user_file="", sample_thickness='0.0', options_column_string=""): + output_name="", user_file="", sample_thickness='', sample_height='', sample_width='', + sample_shape='', options_column_string=""): super(TableIndexModel, self).__init__() self.id = None self.sample_scatter = sample_scatter @@ -227,6 +264,9 @@ class TableIndexModel(object): self.user_file = user_file self.sample_thickness = sample_thickness + self.sample_height = sample_height + self.sample_width = sample_width + self.sample_shape = sample_shape self.output_name = output_name self.options_column_model = options_column_string @@ -234,6 +274,7 @@ class TableIndexModel(object): self.row_state = RowState.Unprocessed self.tool_tip = '' self.file_information = None + self.file_finding = False # Options column entries @property @@ -260,8 +301,15 @@ class TableIndexModel(object): self._string_period(self.can_scatter_period), self.can_transmission, self._string_period(self.can_transmission_period), self.can_direct, self._string_period(self.can_direct_period), self.output_name, self.user_file, self.sample_thickness, + self.sample_height, self.sample_width, self._convert_sample_shape_to_string(self.sample_shape), self.options_column_model.get_options_string()] + def _convert_sample_shape_to_string(self, shape): + if shape: + return SampleShape.to_string(shape) + else: + return '' + def isMultiPeriod(self): return any ((self.sample_scatter_period, self.sample_transmission_period ,self.sample_direct_period, self.can_scatter_period, self.can_transmission_period, self.can_direct_period)) diff --git a/scripts/SANS/sans/gui_logic/presenter/gui_state_director.py b/scripts/SANS/sans/gui_logic/presenter/gui_state_director.py index 4c41bc8ce635fc30196dd8ee02784f8a45646d07..280b76a2b0a23e9f4f09292a716e149ab9f6d2fd 100644 --- a/scripts/SANS/sans/gui_logic/presenter/gui_state_director.py +++ b/scripts/SANS/sans/gui_logic/presenter/gui_state_director.py @@ -16,7 +16,6 @@ import copy from sans.state.data import get_data_builder from sans.user_file.state_director import StateDirectorISIS -from sans.common.file_information import SANSFileInformationFactory from sans.common.enums import (SANSInstrument) from sans.test_helper.file_information_mock import SANSFileInformationMock @@ -34,10 +33,8 @@ class GuiStateDirector(object): def create_state(self, row, file_lookup=True, instrument=SANSInstrument.SANS2D): # 1. Get the data settings, such as sample_scatter, etc... and create the data state. table_index_model = self._table_model.get_table_entry(row) - file_name = table_index_model.sample_scatter if file_lookup: - file_information_factory = SANSFileInformationFactory() - file_information = file_information_factory.create_sans_file_information(file_name) + file_information = table_index_model.file_information else: file_information = SANSFileInformationMock(instrument=instrument, facility=self._facility) @@ -70,6 +67,12 @@ class GuiStateDirector(object): if table_index_model.sample_thickness: state_gui_model.sample_thickness = float(table_index_model.sample_thickness) + if table_index_model.sample_height: + state_gui_model.sample_height = float(table_index_model.sample_height) + if table_index_model.sample_width: + state_gui_model.sample_width = float(table_index_model.sample_width) + if table_index_model.sample_shape: + state_gui_model.sample_shape = table_index_model.sample_shape # 4. Create the rest of the state based on the builder. user_file_state_director = StateDirectorISIS(data, file_information) diff --git a/scripts/SANS/sans/gui_logic/presenter/run_tab_presenter.py b/scripts/SANS/sans/gui_logic/presenter/run_tab_presenter.py index 921b594d93a5a7428bf3bc9a7a90f1db8cb07a28..5573a594ac8ecd2e601df3bb29be5e0986f10ed5 100644 --- a/scripts/SANS/sans/gui_logic/presenter/run_tab_presenter.py +++ b/scripts/SANS/sans/gui_logic/presenter/run_tab_presenter.py @@ -98,6 +98,9 @@ class RunTabPresenter(object): def on_cut_rows(self): self._presenter.on_cut_rows_requested() + def on_sample_geometry_selection(self, show_geometry): + self._presenter.on_sample_geometry_view_changed(show_geometry) + class ProcessListener(WorkHandler.WorkListener): def __init__(self, presenter): super(RunTabPresenter.ProcessListener, self).__init__() @@ -469,6 +472,12 @@ class RunTabPresenter(object): def on_manage_directories(self): self._view.show_directory_manager() + def on_sample_geometry_view_changed(self, show_geometry): + if show_geometry: + self._view.show_geometry() + else: + self._view.hide_geometry() + def get_row_indices(self): """ Gets the indices of row which are not empty. @@ -614,10 +623,6 @@ class RunTabPresenter(object): self._set_on_view("wavelength_step") self._set_on_view("absolute_scale") - self._set_on_view("sample_shape") - self._set_on_view("sample_height") - self._set_on_view("sample_width") - self._set_on_view("sample_thickness") self._set_on_view("z_offset") # Adjustment tab @@ -805,10 +810,6 @@ class RunTabPresenter(object): self._set_on_state_model("wavelength_range", state_model) self._set_on_state_model("absolute_scale", state_model) - self._set_on_state_model("sample_shape", state_model) - self._set_on_state_model("sample_height", state_model) - self._set_on_state_model("sample_width", state_model) - self._set_on_state_model("sample_thickness", state_model) self._set_on_state_model("z_offset", state_model) # Adjustment tab diff --git a/scripts/SCD_Reduction/BVGFitTools.py b/scripts/SCD_Reduction/BVGFitTools.py index 5207ba765884ce8274c486b20d578d68c982797b..77c07ef6d129aa3fdac95da8d5e048a4adebb098 100644 --- a/scripts/SCD_Reduction/BVGFitTools.py +++ b/scripts/SCD_Reduction/BVGFitTools.py @@ -38,7 +38,7 @@ def get3DPeak(peak, peaks_ws, box, padeCoefficients, qMask, nTheta=150, nPhi=150 n_events, peak=peak, box=box, qMask=qMask, calc_pp_lambda=True, padeCoefficients=padeCoefficients, neigh_length_m=neigh_length_m, pp_lambda=None, pplmin_frac=pplmin_frac, pplmax_frac=pplmax_frac, mindtBinWidth=mindtBinWidth, maxdtBinWidth=maxdtBinWidth, - peakMaskSize=peakMaskSize, iccFitDict=iccFitDict) + peakMaskSize=peakMaskSize, iccFitDict=iccFitDict, fitPenalty=fitPenalty) YTOF, fICC, x_lims = fitTOFCoordinate( box, peak, padeCoefficients, dtSpread=dtSpread, qMask=qMask, bgPolyOrder=bgPolyOrder, zBG=zBG, plotResults=plotResults, pp_lambda=pp_lambda, neigh_length_m=neigh_length_m, pplmin_frac=pplmin_frac, @@ -58,7 +58,7 @@ def get3DPeak(peak, peaks_ws, box, padeCoefficients, qMask, nTheta=150, nPhi=150 fICC['KConv'] = fICCParams[11] goodIDX, _ = ICCFT.getBGRemovedIndices( n_events, pp_lambda=pp_lambda, qMask=qMask, peakMaskSize=peakMaskSize, - iccFitDict=iccFitDict) + iccFitDict=iccFitDict, fitPenalty=fitPenalty) chiSqTOF = fICCParams[4] #Last entry # Get the 3D TOF component, YTOF @@ -304,8 +304,7 @@ def fitTOFCoordinate(box, peak, padeCoefficients, dtSpread=0.03, minFracPixels=0 neigh_length_m=neigh_length_m, zBG=zBG, pp_lambda=pp_lambda, pplmin_frac=pplmin_frac, pplmax_frac=pplmax_frac, mindtBinWidth=mindtBinWidth, maxdtBinWidth=maxdtBinWidth, - peakMaskSize=peakMaskSize, - iccFitDict=iccFitDict) + peakMaskSize=peakMaskSize, iccFitDict=iccFitDict, fitPenalty=fitPenalty) fitResults, fICC = ICCFT.doICCFit(tofWS, energy, flightPath, padeCoefficients, fitOrder=bgPolyOrder, constraintScheme=1, diff --git a/scripts/SCD_Reduction/ICCFitTools.py b/scripts/SCD_Reduction/ICCFitTools.py index 6d58d49de2b2c23028ed496a2eebb3f917699c3f..819470ede8b56a102aed85ea8eaf518cb98c30f4 100644 --- a/scripts/SCD_Reduction/ICCFitTools.py +++ b/scripts/SCD_Reduction/ICCFitTools.py @@ -114,7 +114,7 @@ def getQXQYQZ(box): def getQuickTOFWS(box, peak, padeCoefficients, goodIDX=None, dtSpread=0.03, qMask=None, pp_lambda=None, minppl_frac=0.8, maxppl_frac=1.5, mindtBinWidth=1, maxdtBinWidth=50, - constraintScheme=1, peakMaskSize=5, iccFitDict=None): + constraintScheme=1, peakMaskSize=5, iccFitDict=None, fitPenalty=None): """ getQuickTOFWS - generates a quick-and-dirty TOFWS. Useful for determining the background. Input: @@ -153,10 +153,10 @@ def getQuickTOFWS(box, peak, padeCoefficients, goodIDX=None, dtSpread=0.03, qMas minFracPixels=0.01, neigh_length_m=3, zBG=1.96, pp_lambda=pp_lambda, calc_pp_lambda=calc_pp_lambda, pplmin_frac=minppl_frac, pplmax_frac=minppl_frac, mindtBinWidth=mindtBinWidth, maxdtBinWidth=maxdtBinWidth, - peakMaskSize=peakMaskSize, iccFitDict=iccFitDict) + peakMaskSize=peakMaskSize, iccFitDict=iccFitDict, fitPenalty=fitPenalty) fitResults, fICC = doICCFit( tofWS, energy, flightPath, padeCoefficients, fitOrder=1, constraintScheme=constraintScheme, - iccFitDict=iccFitDict) + iccFitDict=iccFitDict, fitPenalty=fitPenalty) h = [tofWS.readY(0), tofWS.readX(0)] chiSq = fitResults.OutputChi2overDoF @@ -220,7 +220,7 @@ def getPoissionGoodIDX(n_events, zBG=1.96, neigh_length_m=3): def getOptimizedGoodIDX(n_events, padeCoefficients, zBG=1.96, neigh_length_m=3, qMask=None, peak=None, box=None, pp_lambda=None, peakNumber=-1, minppl_frac=0.8, maxppl_frac=1.5, mindtBinWidth=1, maxdtBinWidth=50, - constraintScheme=1, peakMaskSize=5, iccFitDict=None): + constraintScheme=1, peakMaskSize=5, iccFitDict=None, fitPenalty=None): """ getOptimizedGoodIDX - returns a numpy arrays which is true if the voxel contains events at the zBG z level (1.96=95%CI). Rather than using Poission statistics, this function @@ -308,7 +308,7 @@ def getOptimizedGoodIDX(n_events, padeCoefficients, zBG=1.96, neigh_length_m=3, chiSq, h, intens, sigma = getQuickTOFWS(box, peak, padeCoefficients, goodIDX=goodIDX, qMask=qMask, pp_lambda=pp_lambda, minppl_frac=minppl_frac, maxppl_frac=maxppl_frac, mindtBinWidth=mindtBinWidth, maxdtBinWidth=maxdtBinWidth, constraintScheme=constraintScheme, - peakMaskSize=peakMaskSize, iccFitDict=iccFitDict) + peakMaskSize=peakMaskSize, iccFitDict=iccFitDict, fitPenalty=fitPenalty) except: #raise break @@ -332,8 +332,7 @@ def getOptimizedGoodIDX(n_events, padeCoefficients, zBG=1.96, neigh_length_m=3, chiSq, h, intens, sigma = getQuickTOFWS(box, peak, padeCoefficients, goodIDX=goodIDX, qMask=qMask, pp_lambda=pp_lambda, minppl_frac=minppl_frac, maxppl_frac=maxppl_frac, mindtBinWidth=mindtBinWidth, maxdtBinWidth=maxdtBinWidth, - peakMaskSize=peakMaskSize, - iccFitDict=iccFitDict) + peakMaskSize=peakMaskSize, iccFitDict=iccFitDict, fitPenalty=fitPenalty) if qMask is not None: return goodIDX*qMask, pp_lambda return goodIDX, pp_lambda @@ -342,7 +341,7 @@ def getOptimizedGoodIDX(n_events, padeCoefficients, zBG=1.96, neigh_length_m=3, def getBGRemovedIndices(n_events, zBG=1.96, calc_pp_lambda=False, neigh_length_m=3, qMask=None, peak=None, box=None, pp_lambda=None, peakNumber=-1, padeCoefficients=None, pplmin_frac=0.8, pplmax_frac=1.5, mindtBinWidth=1, maxdtBinWidth=50, - constraintScheme=1, peakMaskSize=5, iccFitDict=None): + constraintScheme=1, peakMaskSize=5, iccFitDict=None, fitPenalty=None): """ getBGRemovedIndices - A wrapper for getOptimizedGoodIDX Input: @@ -400,8 +399,8 @@ def getBGRemovedIndices(n_events, zBG=1.96, calc_pp_lambda=False, neigh_length_m minppl_frac=pplmin_frac, maxppl_frac=pplmax_frac, qMask=qMask, peak=peak, box=box, pp_lambda=pp_lambda, peakNumber=peakNumber, mindtBinWidth=mindtBinWidth, maxdtBinWidth=maxdtBinWidth, - constraintScheme=constraintScheme, - peakMaskSize=peakMaskSize, iccFitDict=iccFitDict) + constraintScheme=constraintScheme, peakMaskSize=peakMaskSize, + iccFitDict=iccFitDict, fitPenalty=fitPenalty) except KeyboardInterrupt: sys.exit() except: @@ -562,7 +561,7 @@ def get_pp_lambda(n_events, hasEventsIDX): def getTOFWS(box, flightPath, scatteringHalfAngle, tofPeak, peak, qMask, zBG=-1.0, dtSpread=0.02, minFracPixels=0.005, workspaceNumber=None, neigh_length_m=0, pp_lambda=None, calc_pp_lambda=False, padeCoefficients=None, pplmin_frac=0.8, pplmax_frac=1.5, peakMaskSize=5, - mindtBinWidth=1, maxdtBinWidth=50, constraintScheme=1, iccFitDict=None): + mindtBinWidth=1, maxdtBinWidth=50, constraintScheme=1, iccFitDict=None, fitPenalty=None): """ Builds a TOF profile from the data in box which is nominally centered around a peak. Input: @@ -606,8 +605,8 @@ def getTOFWS(box, flightPath, scatteringHalfAngle, tofPeak, peak, qMask, zBG=-1. calc_pp_lambda=calc_pp_lambda, padeCoefficients=padeCoefficients, pplmin_frac=pplmin_frac, pplmax_frac=pplmax_frac, mindtBinWidth=mindtBinWidth, maxdtBinWidth=maxdtBinWidth, - constraintScheme=constraintScheme, - peakMaskSize=peakMaskSize, iccFitDict=iccFitDict) + constraintScheme=constraintScheme, peakMaskSize=peakMaskSize, + iccFitDict=iccFitDict, fitPenalty=fitPenalty) hasEventsIDX = np.logical_and(goodIDX, qMask) boxMeanIDX = np.where(hasEventsIDX) else: # don't do background removal - just consider one pixel at a time @@ -903,7 +902,6 @@ def doICCFit(tofWS, energy, flightPath, padeCoefficients, constraintScheme=None, 0.0, 1.0e10], T00=[0, 1.0e10], KConv0=[100., 140.], penalty=fitPenalty) f = FunctionWrapper(fICC) bg = Polynomial(n=fitOrder) - for i in range(fitOrder+1): bg['A'+str(fitOrder-i)] = bgx0[i] bg.constrain('-1.0 < A%i < 1.0' % fitOrder) @@ -918,7 +916,7 @@ def integrateSample(run, MDdata, peaks_ws, paramList, UBMatrix, dQ, qMask, padeC dQPixel=0.005, p=None, neigh_length_m=0, zBG=-1.0, bgPolyOrder=1, doIterativeBackgroundFitting=False, q_frame='sample', progressFile=None, minpplfrac=0.8, maxpplfrac=1.5, mindtBinWidth=1, maxdtBinWidth=50, - keepFitDict=False, constraintScheme=1, peakMaskSize=5, iccFitDict=None): + keepFitDict=False, constraintScheme=1, peakMaskSize=5, iccFitDict=None, fitPenalty=None): """ integrateSample contains the loop that integrates over all of the peaks in a run and saves the results. Importantly, it also handles errors (mostly by passing and recording special values for failed fits.) @@ -994,13 +992,13 @@ def integrateSample(run, MDdata, peaks_ws, paramList, UBMatrix, dQ, qMask, padeC mindtBinWidth=mindtBinWidth, maxdtBinWidth=maxdtBinWidth, pplmin_frac=minpplfrac, pplmax_frac=maxpplfrac, - constraintScheme=constraintScheme, - peakMaskSize=peakMaskSize, iccFitDict=iccFitDict) + constraintScheme=constraintScheme, peakMaskSize=peakMaskSize, + iccFitDict=iccFitDict, fitPenalty=fitPenalty) tofWS = mtd['__tofWS'] fitResults, fICC = doICCFit( tofWS, energy, flightPath, padeCoefficients, fitOrder=bgPolyOrder, constraintScheme=constraintScheme, - iccFitDict=iccFitDict) + iccFitDict=iccFitDict, fitPenalty=fitPenalty) chiSq = fitResults.OutputChi2overDoF r = mtd['fit_Workspace'] diff --git a/scripts/test/SANS/gui_logic/create_state_test.py b/scripts/test/SANS/gui_logic/create_state_test.py index c40ab60539cee59ddd8bf4ec1dce07beac066718..805c5d298f55841946ec8da9d30d01041175e999 100644 --- a/scripts/test/SANS/gui_logic/create_state_test.py +++ b/scripts/test/SANS/gui_logic/create_state_test.py @@ -15,6 +15,7 @@ from sans.common.enums import (SANSInstrument, ISISReductionMode, SANSFacility, from sans.gui_logic.models.state_gui_model import StateGuiModel from sans.gui_logic.models.table_model import TableModel, TableIndexModel from sans.state.state import State +from PyQt4.QtCore import QCoreApplication if sys.version_info.major == 3: from unittest import mock @@ -23,13 +24,15 @@ else: class GuiCommonTest(unittest.TestCase): def setUp(self): + self.qApp = QCoreApplication(['test_app']) self.table_model = TableModel() self.state_gui_model = StateGuiModel({}) table_index_model_0 = TableIndexModel('LOQ74044', '', '', '', '', '', '', '', '', '', '', '') table_index_model_1 = TableIndexModel('LOQ74044', '', '', '', '', '', '', '', '', '', '', '') self.table_model.add_table_entry(0, table_index_model_0) self.table_model.add_table_entry(1, table_index_model_1) - self.table_model.wait_for_done() + self.table_model.wait_for_file_finding_done() + self.qApp.processEvents() self.fake_state = mock.MagicMock(spec=State) self.gui_state_director_instance = mock.MagicMock() @@ -55,6 +58,8 @@ class GuiCommonTest(unittest.TestCase): def test_skips_empty_rows(self): table_index_model = TableIndexModel('', '', '', '', '', '', '', '', '', '', '', '') self.table_model.add_table_entry(1, table_index_model) + self.table_model.wait_for_file_finding_done() + self.qApp.processEvents() states, errors = create_states(self.state_gui_model, self.table_model, SANSInstrument.LOQ, SANSFacility.ISIS, row_index=[0,1, 2]) @@ -68,6 +73,8 @@ class GuiCommonTest(unittest.TestCase): user_file='MaskLOQData.txt') table_model = TableModel() table_model.add_table_entry(0, table_index_model) + table_model.wait_for_file_finding_done() + self.qApp.processEvents() states, errors = create_states(self.state_gui_model, table_model, SANSInstrument.LOQ, SANSFacility.ISIS, row_index=[0,1, 2]) diff --git a/scripts/test/SANS/gui_logic/gui_state_director_test.py b/scripts/test/SANS/gui_logic/gui_state_director_test.py index e462b23b75bfd2f2715d548b2178739ebb422a72..ee97977ef15451b1a679269db13816abc2a432d6 100644 --- a/scripts/test/SANS/gui_logic/gui_state_director_test.py +++ b/scripts/test/SANS/gui_logic/gui_state_director_test.py @@ -25,7 +25,7 @@ class GuiStateDirectorTest(unittest.TestCase): "", "", "",options_column_string=option_string, sample_thickness=sample_thickness) table_model = TableModel() - table_model.add_table_entry(0, table_index_model) + table_model.add_table_entry_no_thread_or_signal(0, table_index_model) return table_model @staticmethod @@ -72,28 +72,14 @@ class GuiStateDirectorTest(unittest.TestCase): self.assertTrue(state.wavelength.wavelength_high == [10.3]) def test_that_sample_thickness_set_on_state(self): - table_model = self._get_table_model(sample_thickness = 78.0) - state_model = self._get_state_gui_model() - director = GuiStateDirector(table_model, state_model, SANSFacility.ISIS) - - state = director.create_state(0) - self.assertTrue(isinstance(state, State)) - - self.assertEqual(state.scale.thickness, 78.0) - - def test_state_created_with_default_sample_thickness_when_file_lookup_disabled(self): table_model = self._get_table_model() state_model = self._get_state_gui_model() director = GuiStateDirector(table_model, state_model, SANSFacility.ISIS) - state = director.create_state(0, file_lookup=False) + state = director.create_state(0) self.assertTrue(isinstance(state, State)) - self.assertEqual(state.scale.thickness_from_file, 1.0) - self.assertEqual(state.scale.height_from_file, 8.0) - self.assertEqual(state.scale.width_from_file, 8.0) - self.assertEqual(state.scale.thickness, 8.0) - + self.assertEqual(state.scale.thickness, 1.0) if __name__ == '__main__': unittest.main() diff --git a/scripts/test/SANS/gui_logic/run_tab_presenter_test.py b/scripts/test/SANS/gui_logic/run_tab_presenter_test.py index 4368b73facc515d2370d593d1e254a29484b814d..4a303877cc305abf75bb34b65d96eea01ce9e83f 100644 --- a/scripts/test/SANS/gui_logic/run_tab_presenter_test.py +++ b/scripts/test/SANS/gui_logic/run_tab_presenter_test.py @@ -21,7 +21,7 @@ from sans.test_helper.mock_objects import (create_mock_view) from sans.test_helper.common import (remove_file) from sans.common.enums import BatchReductionEntry from sans.gui_logic.models.table_model import TableModel, TableIndexModel - +from sans.test_helper.file_information_mock import SANSFileInformationMock if sys.version_info.major == 3: from unittest import mock @@ -180,17 +180,17 @@ class RunTabPresenterTest(unittest.TestCase): presenter.on_batch_file_load() # Assert - self.assertEqual(view.add_row.call_count, 9) + self.assertEqual(view.add_row.call_count, 8) if use_multi_period: expected_first_row = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', - '', 'test_file', '', '', ''] + '', 'test_file', '', '', '', '', '', ''] expected_second_row = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', - '', ''] + '', '', '', '', ''] else: expected_first_row = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', - '', 'test_file', '', '', ''] + '', 'test_file', '', '', '', '', '', ''] expected_second_row = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '' - , ''] + , '', '', '', ''] calls = [mock.call(expected_first_row), mock.call(expected_second_row)] view.add_row.assert_has_calls(calls) @@ -220,10 +220,11 @@ class RunTabPresenterTest(unittest.TestCase): presenter.on_batch_file_load() # Assert - self.assertEqual(view.add_row.call_count, 5) + self.assertEqual(view.add_row.call_count, 4) self.assertEqual(view.show_period_columns.call_count, 2) - expected_row = ['SANS2D00022024', '3', '', '', '', '', '', '', '', '', '', '', 'test_file', '', '', ''] + expected_row = ['SANS2D00022024', '3', '', '', '', '', '', '', '', '', '', '', 'test_file', '', '', + '', '', '', ''] calls = [mock.call(expected_row)] view.add_row.assert_has_calls(calls) @@ -251,6 +252,8 @@ class RunTabPresenterTest(unittest.TestCase): batch_file_path, user_file_path, presenter, _ = self._get_files_and_mock_presenter(BATCH_FILE_TEST_CONTENT_2) presenter.on_user_file_load() presenter.on_batch_file_load() + presenter._table_model.update_thickness_from_file_information(1, self.get_file_information_mock()) + presenter._table_model.update_thickness_from_file_information(2, self.get_file_information_mock()) # Act states, errors = presenter.get_states(row_index=[0, 1]) @@ -300,6 +303,10 @@ class RunTabPresenterTest(unittest.TestCase): row_user_file_path = row_user_file_path) presenter.on_user_file_load() presenter.on_batch_file_load() + presenter._table_model.update_thickness_from_file_information(1, self.get_file_information_mock()) + presenter._table_model.update_thickness_from_file_information(2, self.get_file_information_mock()) + + # Act state = presenter.get_state_for_row(1) state0 = presenter.get_state_for_row(0) @@ -313,6 +320,8 @@ class RunTabPresenterTest(unittest.TestCase): presenter.on_user_file_load() presenter.on_batch_file_load() + presenter._table_model.update_thickness_from_file_information(1, self.get_file_information_mock()) + presenter._table_model.update_thickness_from_file_information(2, self.get_file_information_mock()) # Act state = presenter.get_state_for_row(1) @@ -353,6 +362,8 @@ class RunTabPresenterTest(unittest.TestCase): presenter.on_user_file_load() presenter.on_batch_file_load() + presenter._table_model.update_thickness_from_file_information(1, self.get_file_information_mock()) + presenter._table_model.update_thickness_from_file_information(2, self.get_file_information_mock()) # Act presenter.on_mask_file_add() @@ -386,7 +397,7 @@ class RunTabPresenterTest(unittest.TestCase): expected_table_model.subscribe_to_model_changes(presenter) expected_table_model.subscribe_to_model_changes(presenter._masking_table_presenter) expected_table_model.subscribe_to_model_changes(presenter._beam_centre_presenter) - + self.maxDiff = None self.assertEqual(presenter._table_model, expected_table_model) def test_on_insert_row_updates_table_model(self): @@ -397,6 +408,7 @@ class RunTabPresenterTest(unittest.TestCase): index = 0 expected_table_index_model = TableIndexModel(*row) expected_table_index_model.id = 0 + expected_table_index_model.file_finding = True presenter.on_row_inserted(index, row) @@ -433,6 +445,7 @@ class RunTabPresenterTest(unittest.TestCase): value = '74040' expected_table_index_model = TableIndexModel(*expected_row) expected_table_index_model.id = 0 + expected_table_index_model.file_finding = True presenter.on_data_changed(row, column, value, '') @@ -458,14 +471,16 @@ class RunTabPresenterTest(unittest.TestCase): rows = [0, 2] expected_row_0 = TableIndexModel(*row_1) expected_row_0.id = 1 + expected_row_0.file_finding = True expected_row_1 = TableIndexModel(*row_3) expected_row_1.id = 3 + expected_row_1.file_finding = True + presenter.on_rows_removed(rows) self.assertEqual(presenter._table_model.get_number_of_rows(), 2) model_row_0 = presenter._table_model.get_table_entry(0) - self.assertEqual(model_row_0, expected_row_0) model_row_1 = presenter._table_model.get_table_entry(1) self.assertEqual(model_row_1, expected_row_1) @@ -515,8 +530,9 @@ class RunTabPresenterTest(unittest.TestCase): for item, row in enumerate(parsed_data): presenter._add_row_to_table_model(row, item) expected_call_0 = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '', ''] - expected_call_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '', ''] + 'test_file', '', '', '', '', '', ''] + expected_call_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '', '', + '', '', ''] presenter.update_view_from_table_model() @@ -544,7 +560,7 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[0]) presenter.set_view(view) test_row = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] presenter.on_row_inserted(0, test_row) @@ -558,8 +574,9 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[]) presenter.set_view(view) test_row_0 = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] - test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] + test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', '', + '', '', ''] presenter.on_row_inserted(0, test_row_0) presenter.on_row_inserted(1, test_row_1) presenter._clipboard = [test_row_0] @@ -575,8 +592,9 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[1]) presenter.set_view(view) test_row_0 = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] - test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] + test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', '', + '', '', ''] presenter.on_row_inserted(0, test_row_0) presenter.on_row_inserted(1, test_row_1) presenter.on_row_inserted(2, test_row_0) @@ -593,9 +611,11 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[0, 2]) presenter.set_view(view) test_row_0 = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] - test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', ''] - test_row_2 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file3', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] + test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', '', + '', '', ''] + test_row_2 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file3', '', '1.0', '', + '', '', ''] presenter.on_row_inserted(0, test_row_0) presenter.on_row_inserted(1, test_row_1) presenter.on_row_inserted(2, test_row_2) @@ -630,15 +650,16 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[0]) presenter.set_view(view) test_row_0 = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] - test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] + test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', '', + '', '', ''] presenter.on_row_inserted(0, test_row_0) presenter.on_row_inserted(1, test_row_1) presenter.on_insert_row() self.assertEqual(presenter._table_model.get_number_of_rows(), 3) - self.assertEqual(presenter._table_model.get_table_entry(1).to_list(), [''] * 16) + self.assertEqual(presenter._table_model.get_table_entry(1).to_list(), [''] * 19) self.assertEqual(presenter._table_model.get_table_entry(0).to_list(), test_row_0) self.assertEqual(presenter._table_model.get_table_entry(2).to_list(), test_row_1) @@ -648,8 +669,9 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[0]) presenter.set_view(view) test_row_0 = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] - test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] + test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', + '', '', '', ''] presenter.on_row_inserted(0, test_row_0) presenter.on_row_inserted(1, test_row_1) presenter.update_view_from_table_model = mock.MagicMock() @@ -664,7 +686,7 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[1, 2]) presenter.set_view(view) test_row_0 = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] empty_row = TableModel.create_empty_row() presenter.on_row_inserted(0, test_row_0) @@ -686,7 +708,7 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[1, 2]) presenter.set_view(view) test_row_0 = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] presenter.on_row_inserted(0, test_row_0) presenter.on_row_inserted(1, test_row_0) presenter.on_row_inserted(2, test_row_0) @@ -696,13 +718,13 @@ class RunTabPresenterTest(unittest.TestCase): self.assertEqual(presenter._table_model._table_entries[0].to_list(), ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', '']) + 'test_file', '', '1.0', '', '', '', '']) self.assertEqual(presenter._table_model._table_entries[1].to_list(), ['', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '']) + '', '', '', '', '', '', '']) self.assertEqual(presenter._table_model._table_entries[2].to_list(), ['', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '']) + '', '', '', '', '', '', '']) def test_on_cut_rows_requested_updates_clipboard(self): presenter = RunTabPresenter(SANSFacility.ISIS) @@ -710,7 +732,7 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[0]) presenter.set_view(view) test_row = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] presenter.on_row_inserted(0, test_row) @@ -724,8 +746,9 @@ class RunTabPresenterTest(unittest.TestCase): view.get_selected_rows = mock.MagicMock(return_value=[0]) presenter.set_view(view) test_row_0 = ['SANS2D00022024', '', 'SANS2D00022048', '', 'SANS2D00022048', '', '', '', '', '', '', '', - 'test_file', '', '1.0', ''] - test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', ''] + 'test_file', '', '1.0', '', '', '', ''] + test_row_1 = ['SANS2D00022024', '', '', '', '', '', '', '', '', '', '', '', 'test_file2', '', '1.0', + '', '', '', ''] presenter.on_row_inserted(0, test_row_0) presenter.on_row_inserted(1, test_row_1) @@ -787,6 +810,9 @@ class RunTabPresenterTest(unittest.TestCase): if batch_file_path: remove_file(batch_file_path) + @staticmethod + def get_file_information_mock(): + return SANSFileInformationMock(instrument=SANSInstrument.SANS2D) if __name__ == '__main__': unittest.main() diff --git a/scripts/test/SANS/gui_logic/table_model_test.py b/scripts/test/SANS/gui_logic/table_model_test.py index 19b6b895939603b35a9a9100107643081e56e3d4..f48de1999b33de9c30f06e4a8ba25543dc0840ed 100644 --- a/scripts/test/SANS/gui_logic/table_model_test.py +++ b/scripts/test/SANS/gui_logic/table_model_test.py @@ -48,7 +48,7 @@ class TableModelTest(unittest.TestCase): def test_that_can_set_the_options_column_model(self): table_index_model = TableIndexModel('0', "", "", "", "", "", "", "", "", "", "", "", "", "", "", - "WavelengthMin=1, WavelengthMax=3, NotRegister2=1") + options_column_string="WavelengthMin=1, WavelengthMax=3, NotRegister2=1") options_column_model = table_index_model.options_column_model options = options_column_model.get_options() self.assertTrue(len(options) == 2) @@ -56,9 +56,9 @@ class TableModelTest(unittest.TestCase): self.assertTrue(options["WavelengthMax"] == 3.) def test_that_raises_for_missing_equal(self): - args = [0, "", "", "", "", "", "", "", "", "", "", "", "", "", "", - "WavelengthMin=1, WavelengthMax=3, NotRegister2"] - self.assertRaises(ValueError, TableIndexModel, *args) + args = [0, "", "", "", "", "", "", "", "", "", "", "", "", "", ""] + kwargs = {'options_column_string': "WavelengthMin=1, WavelengthMax=3, NotRegister2"} + self.assertRaises(ValueError, TableIndexModel, *args, **kwargs) def test_that_querying_nonexistent_row_index_raises_IndexError_exception(self): table_model = TableModel()