diff --git a/Code/Mantid/Build/dev-packages/rpm/mantid-developer/mantid-developer-f20.spec b/Code/Mantid/Build/dev-packages/rpm/mantid-developer/mantid-developer-f20.spec new file mode 100644 index 000000000000..9fb0d286adeb --- /dev/null +++ b/Code/Mantid/Build/dev-packages/rpm/mantid-developer/mantid-developer-f20.spec @@ -0,0 +1,105 @@ +Name: mantid-developer +Version: 1.3 +Release: 3%{?dist} +Summary: Meta Package to install dependencies for Mantid Development + +Group: Development/Tools +License: GPL + +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +Requires: rpmfusion-nonfree-release +Requires: cmake-gui >= 2.8.5 +Requires: boost-devel +Requires: gperftools-devel +Requires: gperftools-libs +Requires: gcc-c++ +Requires: git-all +Requires: gsl-devel +Requires: hdf-devel +Requires: hdf5-devel +Requires: muParser-devel +Requires: mxml-devel +Requires: nexus >= 4.2 +Requires: nexus-devel >= 4.2 +Requires: numpy +Requires: OCE-devel +Requires: poco-devel +Requires: PyQt4-devel +Requires: python-devel +Requires: python-sphinx +Requires: qscintilla-devel +Requires: qt-devel >= 4.6 +Requires: qwt5-qt4-devel +Requires: qwtplot3d-qt4-devel +Requires: redhat-lsb +Requires: rpmdevtools +Requires: sip-devel +Requires: git +Requires: openssl-devel +Requires: texlive-latex +Requires: dvipng +Requires: qt-devel +Requires: qt-assistant + +BuildArch: noarch + +%description +A virtual package which requires all the dependencies and tools that are +required for Mantid development. + +%prep + +%build + +%install + +%clean + +%post +# Remove myself once I have installed all the required packages. +#rpm -e %{name} + +%files + +%changelog +* Fri Dec 20 2013 Stuart Campbell +- Added python-sphinx + +* Thu Dec 19 2013 Stuart Campbell +- Changed to use OCE rather than OpenCASCADE. + +* Tue Aug 20 2013 Peter Peterson +- Removed things not necessary for fedora 19. + +* Tue May 07 2013 Stuart Campbell +- Added dvipng and latex for qt-assistant stuff +- Added software collection dependencies + +* Thu Jun 7 2012 Russell Taylor +- Remove gmock & gtest now that we include them in our repo +- Remove subversion dependency now that we use git + +* Mon Mar 19 2012 Stuart Campbell +- Updated for google-perftools -> gperftools package rename. + +* Wed Feb 22 2012 Stuart Campbell +- Added nexus as it is not required by it's devel package. + +* Wed Feb 22 2012 Stuart Campbell +- Added git as a dependency +- Added openssl-devel dependency + +* Mon Feb 20 2012 Stuart Campbell +- Added dependency on NeXus development after nexus rpm split. +- Updated CMake dependency to 2.8.5 following 'the virus'! +- Added Google Mock and GTest. + +* Fri Jun 3 2011 Stuart Campbell +- Added rpmdevtools and lsb dependencies + +* Fri Jun 3 2011 Stuart Campbell +- Added versions for some packages + +* Fri Jun 3 2011 Stuart Campbell +- Initial release diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/FunctionFactory.h b/Code/Mantid/Framework/API/inc/MantidAPI/FunctionFactory.h index c7122ea6c266..b9fed8c63c82 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/FunctionFactory.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/FunctionFactory.h @@ -8,9 +8,11 @@ #include "MantidAPI/DllConfig.h" #include "MantidKernel/DynamicFactory.h" #include "MantidKernel/SingletonHolder.h" +#include "MantidKernel/MultiThreaded.h" #include + namespace Mantid { @@ -76,7 +78,13 @@ namespace API /// Query available functions based on the template type template - std::vector getFunctionNames() const; + const std::vector& getFunctionNames() const; + + using Kernel::DynamicFactory::subscribe; + void subscribe(const std::string& className, AbstractFactory* pAbstractFactory, + Kernel::DynamicFactory::SubscribeAction replace=ErrorIfExists); + + void unsubscribe(const std::string& className); private: friend struct Mantid::Kernel::CreateUsingNew; @@ -116,9 +124,11 @@ namespace API /// Add a tie to the created function void addTie(boost::shared_ptr fun,const Expression& expr)const; - ///static reference to the logger class + /// Reference to the logger class Kernel::Logger& g_log; + mutable std::map> m_cachedFunctionNames; + mutable Kernel::Mutex m_mutex; }; /** @@ -127,13 +137,20 @@ namespace API * @returns A vector of the names of the functions matching the template type */ template - std::vector FunctionFactoryImpl::getFunctionNames() const + const std::vector& FunctionFactoryImpl::getFunctionNames() const { + Kernel::Mutex::ScopedLock _lock(m_mutex); + + const std::string soughtType(typeid(FunctionType).name()); + if ( m_cachedFunctionNames.find( soughtType ) != m_cachedFunctionNames.end() ) + { + return m_cachedFunctionNames[soughtType]; + } + + // Create the entry in the cache and work with it directly + std::vector& typeNames = m_cachedFunctionNames[soughtType]; const std::vector names = this->getKeys(); - std::vector typeNames; - typeNames.reserve(names.size()); - for( std::vector::const_iterator it = names.begin(); - it != names.end(); ++it ) + for( auto it = names.begin(); it != names.end(); ++it ) { boost::shared_ptr func = this->createFunction(*it); if ( func && dynamic_cast(func.get()) ) @@ -143,12 +160,13 @@ namespace API } return typeNames; } - ///Forward declaration of a specialisation of SingletonHolder for AlgorithmFactoryImpl (needed for dllexport/dllimport) and a typedef for it. + + ///Forward declaration of a specialisation of SingletonHolder for AlgorithmFactoryImpl (needed for dllexport/dllimport) and a typedef for it. #ifdef _WIN32 // this breaks new namespace declaraion rules; need to find a better fix - template class MANTID_API_DLL Mantid::Kernel::SingletonHolder; + template class MANTID_API_DLL Mantid::Kernel::SingletonHolder; #endif /* _WIN32 */ - typedef MANTID_API_DLL Mantid::Kernel::SingletonHolder FunctionFactory; + typedef MANTID_API_DLL Mantid::Kernel::SingletonHolder FunctionFactory; /// Convenient typedef for an UpdateNotification typedef FunctionFactoryImpl::UpdateNotification FunctionFactoryUpdateNotification; diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/FunctionProperty.h b/Code/Mantid/Framework/API/inc/MantidAPI/FunctionProperty.h index b5f36906a675..eba0b6c6c551 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/FunctionProperty.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/FunctionProperty.h @@ -12,7 +12,6 @@ #include -#include #include namespace Mantid diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/IMDHistoWorkspace.h b/Code/Mantid/Framework/API/inc/MantidAPI/IMDHistoWorkspace.h index b74b4ec7353c..e58fa0a91b9c 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/IMDHistoWorkspace.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/IMDHistoWorkspace.h @@ -45,9 +45,9 @@ namespace API /// See the MDHistoWorkspace definition for descriptions of these virtual coord_t getInverseVolume() const = 0; - virtual signal_t * getSignalArray() = 0; - virtual signal_t * getErrorSquaredArray() = 0; - virtual signal_t * getNumEventsArray() = 0; + virtual signal_t * getSignalArray() const = 0; + virtual signal_t * getErrorSquaredArray() const = 0; + virtual signal_t * getNumEventsArray() const = 0; virtual void setTo(signal_t signal, signal_t errorSquared, signal_t numEvents) = 0; virtual Mantid::Kernel::VMD getCenter(size_t linearIndex) const = 0; virtual void setSignalAt(size_t index, signal_t value) = 0; diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/IMDNode.h b/Code/Mantid/Framework/API/inc/MantidAPI/IMDNode.h index df36deeb8c83..bbba909b9cc1 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/IMDNode.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/IMDNode.h @@ -177,6 +177,7 @@ class IMDNode * @param length :: length of cylinder below which to integrate * @param signal [out] :: set to the integrated signal * @param errorSquared [out] :: set to the integrated squared error. + * @param signal_fit [out] :: array of values for the fit. */ virtual void integrateCylinder(Mantid::API::CoordTransform & radiusTransform, const coord_t radius, const coord_t length, signal_t & signal, signal_t & errorSquared, std::vector & signal_fit) const = 0; diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/ScopedWorkspace.h b/Code/Mantid/Framework/API/inc/MantidAPI/ScopedWorkspace.h index c6f6feeb7d58..5069047a2b80 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/ScopedWorkspace.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/ScopedWorkspace.h @@ -60,6 +60,9 @@ namespace API /// Retrieve workspace from the ADS Workspace_sptr retrieve() const; + /// Removes the workspace entry from the ADS + void remove(); + /// Operator for conversion to boolean operator bool() const; diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/WorkspaceProperty.h b/Code/Mantid/Framework/API/inc/MantidAPI/WorkspaceProperty.h index 33cd3c00576d..9f8e259e6000 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/WorkspaceProperty.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/WorkspaceProperty.h @@ -12,7 +12,6 @@ #include "MantidKernel/Exception.h" #include "MantidAPI/WorkspaceGroup.h" -#include #include namespace Mantid diff --git a/Code/Mantid/Framework/API/src/FileLoaderRegistry.cpp b/Code/Mantid/Framework/API/src/FileLoaderRegistry.cpp index 084ce2c20f3e..a94e14a42e5e 100644 --- a/Code/Mantid/Framework/API/src/FileLoaderRegistry.cpp +++ b/Code/Mantid/Framework/API/src/FileLoaderRegistry.cpp @@ -23,7 +23,7 @@ namespace Mantid { void apply(Kernel::FileDescriptor & descriptor) { descriptor.resetStreamToStart(); } }; - ///endcond + /// @endcond /** * @param filename A string giving a filename @@ -79,7 +79,7 @@ namespace Mantid /** * If the name does not exist then it does nothing * @param name Name of the algorithm to remove from the search list - * @aparam version An optional version to remove. -1 indicates remove all (Default=-1) + * @param version An optional version to remove. -1 indicates remove all (Default=-1) */ void FileLoaderRegistryImpl::unsubscribe(const std::string &name, const int version) { diff --git a/Code/Mantid/Framework/API/src/FileProperty.cpp b/Code/Mantid/Framework/API/src/FileProperty.cpp index 26b6ded0c04b..8a76e4065d4d 100644 --- a/Code/Mantid/Framework/API/src/FileProperty.cpp +++ b/Code/Mantid/Framework/API/src/FileProperty.cpp @@ -42,7 +42,7 @@ FileProperty::FileProperty(const std::string & name, const std::string& default_ /* Create either a FileValidator or a DirectoryValidator, depending on Action */ (action == FileProperty::Directory || action == FileProperty::OptionalDirectory) ? boost::make_shared(action == FileProperty::Directory) : - boost::make_shared(exts, (action == FileProperty::Load) ) + boost::make_shared(exts, (action == FileProperty::Load), (action == FileProperty::Save) ) , direction), m_action(action), m_defaultExt(""), @@ -69,7 +69,7 @@ FileProperty::FileProperty(const std::string & name, const std::string& default_ /* Create either a FileValidator or a DirectoryValidator, depending on Action */ (action == FileProperty::Directory || action == FileProperty::OptionalDirectory) ? boost::make_shared(action == FileProperty::Directory) : - boost::make_shared(std::vector(1,ext), (action == FileProperty::Load) ) + boost::make_shared(std::vector(1,ext), (action == FileProperty::Load), (action == FileProperty::Save) ) , direction), m_action(action), m_defaultExt(ext), @@ -370,7 +370,7 @@ std::string FileProperty::createDirectory(const std::string & path) const { stempath.makeParent(); } - std::string error(""); + if( !stempath.toString().empty() ) { Poco::File stem(stempath); @@ -378,19 +378,22 @@ std::string FileProperty::createDirectory(const std::string & path) const { try { - stem.createDirectories(); + stem.createDirectories(); } catch(Poco::Exception &e) { - error = e.what(); + std::stringstream msg; + msg << "Failed to create directory \"" << stempath.toString() + << "\": " << e.what() ; + return msg.str(); } } } else { - error = "Invalid directory."; + return "Invalid directory."; } - return error; + return ""; // everything went fine } /** diff --git a/Code/Mantid/Framework/API/src/FunctionFactory.cpp b/Code/Mantid/Framework/API/src/FunctionFactory.cpp index 819f2ce008b0..0b81cca608ff 100644 --- a/Code/Mantid/Framework/API/src/FunctionFactory.cpp +++ b/Code/Mantid/Framework/API/src/FunctionFactory.cpp @@ -335,6 +335,20 @@ namespace Mantid } } + void FunctionFactoryImpl::subscribe(const std::string& className, AbstractFactory* pAbstractFactory, + Kernel::DynamicFactory::SubscribeAction replace) + { + // Clear the cache, then do all the work in the base class method + m_cachedFunctionNames.clear(); + Kernel::DynamicFactory::subscribe(className,pAbstractFactory,replace); + } + + void FunctionFactoryImpl::unsubscribe(const std::string& className) + { + // Clear the cache, then do all the work in the base class method + m_cachedFunctionNames.clear(); + Kernel::DynamicFactory::unsubscribe(className); + } } // namespace API } // namespace Mantid diff --git a/Code/Mantid/Framework/API/src/IPowderDiffPeakFunction.cpp b/Code/Mantid/Framework/API/src/IPowderDiffPeakFunction.cpp index f6de38ca92c6..1bdacae671fc 100644 --- a/Code/Mantid/Framework/API/src/IPowderDiffPeakFunction.cpp +++ b/Code/Mantid/Framework/API/src/IPowderDiffPeakFunction.cpp @@ -213,83 +213,6 @@ namespace API return; } - //---------------------------------------------------------------------------------------------- - /** General implementation of the method for all peaks. Limits the peak evaluation to - * a certain number of FWHMs around the peak centre. The outside points are set to 0. - * Calls functionLocal() to compute the actual values - * @param out :: Output function values - * @param xValues :: X values for data points - * @param nData :: Number of data points - - void IPowderDiffPeakFunction::functionLocal(double* out, const double* xValues, const size_t nData)const - { - double c = this->centre(); - double dx = fabs(s_peakRadius*this->fwhm()); - int i0 = -1; - int n = 0; - for(size_t i = 0; i < nData; ++i) - { - if (fabs(xValues[i] - c) < dx) - { - if (i0 < 0) i0 = static_cast(i); - ++n; - } - else - { - out[i] = 0.0; - } - } - - if (i0 < 0 || n == 0) - return; - this->functionLocal(out+i0, xValues+i0, n); - - return; - } - */ - - //---------------------------------------------------------------------------------------------- - /** General implementation of the method for all peaks. Calculates derivatives only - * for a range of x values limited to a certain number of FWHM around the peak centre. - * For the points outside the range all derivatives are set to 0. - * Calls functionDerivLocal() to compute the actual values - * @param out :: Derivatives - * @param xValues :: X values for data points - * @param nData :: Number of data points - - void IPowderDiffPeakFunction::functionDeriv1D(Jacobian* out, const double* xValues, const size_t nData) const - { - double c = this->centre(); - double dx = fabs(s_peakRadius*this->fwhm()); - int i0 = -1; - int n = 0; - for(size_t i = 0; i < nData; ++i) - { - if (fabs(xValues[i] - c) < dx) - { - if (i0 < 0) i0 = static_cast(i); - ++n; - } - else - { - for(size_t ip = 0; ip < this->nParams(); ++ip) - { - out->set(i,ip, 0.0); - } - } - } - if (i0 < 0 || n == 0) return; -#if 0 - PartialJacobian1 J(out,i0); - this->functionDerivLocal(&J,xValues+i0,n); -#else - throw runtime_error("Need to think how to implement! Message 1026."); -#endif - - return; - } - */ - //---------------------------------------------------------------------------------------------- /** Set peak radius * @param r :: radius diff --git a/Code/Mantid/Framework/API/src/LogManager.cpp b/Code/Mantid/Framework/API/src/LogManager.cpp index dd6b988c68cd..7bd9a55bac8a 100644 --- a/Code/Mantid/Framework/API/src/LogManager.cpp +++ b/Code/Mantid/Framework/API/src/LogManager.cpp @@ -87,47 +87,30 @@ Kernel::Logger& LogManager::g_log = Kernel::Logger::get("LogManager"); const Kernel::DateAndTime LogManager::startTime() const { const std::string start_prop("start_time"); - const std::string run_start_prop("run_start"); - if (this->hasProperty(start_prop)) + if ( hasProperty(start_prop) ) { - std::string start = this->getProperty(start_prop)->value(); - if (DateAndTime(start) != DateAndTimeHelpers::GPS_EPOCH) - { - return DateAndTime(start); - } - else if (this->hasProperty(run_start_prop)) - { - std::string start = this->getProperty(run_start_prop)->value(); - if (DateAndTime(start) != DateAndTimeHelpers::GPS_EPOCH) + try { + DateAndTime start_time(getProperty(start_prop)->value()); + if (start_time != DateAndTimeHelpers::GPS_EPOCH) { - return DateAndTime(start); + return start_time; } - else - { - throw std::runtime_error("Run::startTime() - No start time has been set for this run."); - } - } - else - { - throw std::runtime_error("Run::startTime() - No start time has been set for this run."); - } - } - else if (this->hasProperty(run_start_prop)) - { - std::string start = this->getProperty(run_start_prop)->value(); - if (DateAndTime(start) != DateAndTimeHelpers::GPS_EPOCH) - { - return DateAndTime(start); - } - else - { - throw std::runtime_error("Run::startTime() - No start time has been set for this run."); - } + } catch (std::invalid_argument&) { /*Swallow and move on*/ } } - else + + const std::string run_start_prop("run_start"); + if ( hasProperty(run_start_prop) ) { - throw std::runtime_error("Run::startTime() - No start time has been set for this run."); + try { + DateAndTime start_time(getProperty(run_start_prop)->value()); + if (start_time != DateAndTimeHelpers::GPS_EPOCH) + { + return start_time; + } + } catch (std::invalid_argument&) { /*Swallow and move on*/ } } + + throw std::runtime_error("No valid start time has been set for this run."); } /** Return the run end time as given by the 'end_time' or 'run_end' property. @@ -138,21 +121,22 @@ Kernel::Logger& LogManager::g_log = Kernel::Logger::get("LogManager"); const Kernel::DateAndTime LogManager::endTime() const { const std::string end_prop("end_time"); - const std::string run_end_prop("run_end"); - if (this->hasProperty(end_prop)) - { - std::string end = this->getProperty(end_prop)->value(); - return DateAndTime(end); - } - else if (this->hasProperty(run_end_prop)) + if ( hasProperty(end_prop) ) { - std::string end = this->getProperty(run_end_prop)->value(); - return DateAndTime(end); + try { + return DateAndTime(getProperty(end_prop)->value()); + } catch (std::invalid_argument&) { /*Swallow and move on*/ } } - else + + const std::string run_end_prop("run_end"); + if (hasProperty(run_end_prop)) { - throw std::runtime_error("Run::endTime() - No end time has been set for this run."); + try { + return DateAndTime(getProperty(run_end_prop)->value()); + } catch (std::invalid_argument&) { /*Swallow and move on*/ } } + + throw std::runtime_error("No valid end time has been set for this run."); } diff --git a/Code/Mantid/Framework/API/src/Run.cpp b/Code/Mantid/Framework/API/src/Run.cpp index a1eb556b79f7..bb98b5b83f11 100644 --- a/Code/Mantid/Framework/API/src/Run.cpp +++ b/Code/Mantid/Framework/API/src/Run.cpp @@ -32,6 +32,9 @@ namespace const char * GONIOMETER_LOG_NAME = "goniometer"; /// Name of the stored histogram bins log when saved to NeXus const char * HISTO_BINS_LOG_NAME = "processed_histogram_bins"; + const char * PEAK_RADIUS_GROUP = "peak_radius"; + const char * INNER_BKG_RADIUS_GROUP = "inner_bkg_radius"; + const char * OUTER_BKG_RADIUS_GROUP = "outer_bkg_radius"; } // Get a reference to the logger Kernel::Logger& Run::g_log = Kernel::Logger::get("Run"); @@ -358,7 +361,31 @@ Kernel::Logger& Run::g_log = Kernel::Logger::get("Run"); file->writeData("value", m_histoBins); file->closeGroup(); } + if( this->hasProperty("PeakRadius") ) + { + const std::vector & values = + this->getPropertyValueAsType >("PeakRadius"); + file->makeGroup(PEAK_RADIUS_GROUP, "NXdata", 1); + file->writeData("value", values); + file->closeGroup(); + } + if( this->hasProperty("BackgroundInnerRadius") ) + { + file->makeGroup(INNER_BKG_RADIUS_GROUP, "NXdata", 1); + const std::vector & values = + this->getPropertyValueAsType >("BackgroundInnerRadius"); + file->writeData("value", values); + file->closeGroup(); + } + if( this->hasProperty("BackgroundOuterRadius") ) + { + file->makeGroup(OUTER_BKG_RADIUS_GROUP, "NXdata", 1); + const std::vector & values = + this->getPropertyValueAsType >("BackgroundOuterRadius"); + file->writeData("value", values); + file->closeGroup(); + } if(!keepOpen)file->closeGroup(); } @@ -392,6 +419,30 @@ Kernel::Logger& Run::g_log = Kernel::Logger::get("Run"); file->readData("value",m_histoBins); file->closeGroup(); } + else if(name_class.first == PEAK_RADIUS_GROUP) + { + file->openGroup(name_class.first, "NXdata"); + std::vector values; + file->readData("value",values); + file->closeGroup(); + this->addProperty("PeakRadius",values, true); + } + else if(name_class.first == INNER_BKG_RADIUS_GROUP) + { + file->openGroup(name_class.first, "NXdata"); + std::vector values; + file->readData("value",values); + file->closeGroup(); + this->addProperty("BackgroundInnerRadius",values, true); + } + else if(name_class.first == OUTER_BKG_RADIUS_GROUP) + { + file->openGroup(name_class.first, "NXdata"); + std::vector values; + file->readData("value",values); + file->closeGroup(); + this->addProperty("BackgroundOuterRadius",values, true); + } else if (name_class.first == "proton_charge" && !this->hasProperty("proton_charge")) { // Old files may have a proton_charge field, single value (not even NXlog) diff --git a/Code/Mantid/Framework/API/src/ScopedWorkspace.cpp b/Code/Mantid/Framework/API/src/ScopedWorkspace.cpp index 5dd0012c8b5a..b0a6130ff8e7 100644 --- a/Code/Mantid/Framework/API/src/ScopedWorkspace.cpp +++ b/Code/Mantid/Framework/API/src/ScopedWorkspace.cpp @@ -32,21 +32,7 @@ namespace API */ ScopedWorkspace::~ScopedWorkspace() { - AnalysisDataServiceImpl& ads = AnalysisDataService::Instance(); - - // When destructed, remove workspace from the ADS if was added and still exists - if ( ads.doesExist(m_name) ) - { - if ( ads.retrieveWS(m_name) ) - { - // If is a group, need to remove all the members as well - ads.deepRemoveGroup(m_name); - } - else - { - ads.remove(m_name); - } - } + remove(); } /** @@ -73,6 +59,28 @@ namespace API return Workspace_sptr(); } + + /** + * Removes the workspace entry from the ADS. + */ + void ScopedWorkspace::remove() + { + AnalysisDataServiceImpl& ads = AnalysisDataService::Instance(); + + // When destructed, remove workspace from the ADS if was added and still exists + if ( ads.doesExist(m_name) ) + { + if ( ads.retrieveWS(m_name) ) + { + // If is a group, need to remove all the members as well + ads.deepRemoveGroup(m_name); + } + else + { + ads.remove(m_name); + } + } + } /** * Make ADS entry to point to the given workspace. @@ -84,6 +92,9 @@ namespace API if ( ! newWS->name().empty() && ads.doesExist( newWS->name() ) ) throw std::invalid_argument( "Workspace is already in the ADS under the name " + newWS->name() ); + // Remove previous workspace entry + remove(); + ads.add(m_name, newWS); } diff --git a/Code/Mantid/Framework/API/test/FunctionFactoryTest.h b/Code/Mantid/Framework/API/test/FunctionFactoryTest.h index 9de1d0be112c..0654f99aca5c 100644 --- a/Code/Mantid/Framework/API/test/FunctionFactoryTest.h +++ b/Code/Mantid/Framework/API/test/FunctionFactoryTest.h @@ -378,6 +378,15 @@ class FunctionFactoryTest : public CxxTest::TestSuite // TODO: add more asserts } + void test_getFunctionNames() + { + const auto& names = FunctionFactory::Instance().getFunctionNames(); + TS_ASSERT( !names.empty() ); + TS_ASSERT( std::find(names.begin(), names.end(), "FunctionFactoryTest_FunctA") != names.end() ); + // Call it again to indirectly test caching + TS_ASSERT_EQUALS( names, FunctionFactory::Instance().getFunctionNames()); + } + }; #endif /*FUNCTIONFACTORYTEST_H_*/ diff --git a/Code/Mantid/Framework/API/test/LogManagerTest.h b/Code/Mantid/Framework/API/test/LogManagerTest.h index 9e4100ce351f..ac90623d21a9 100644 --- a/Code/Mantid/Framework/API/test/LogManagerTest.h +++ b/Code/Mantid/Framework/API/test/LogManagerTest.h @@ -91,9 +91,79 @@ class LogManagerTest : public CxxTest::TestSuite TS_ASSERT_EQUALS( runInfo.getProperties().size(), 0 ); } - + void testStartTime() + { + LogManager runInfo; + // Nothing there yet + TS_ASSERT_THROWS( runInfo.startTime(), std::runtime_error ); + // Add run_start and see that get picked up + const std::string run_start("2013-12-19T13:38:00"); + auto run_start_prop = new PropertyWithValue("run_start",run_start); + runInfo.addProperty(run_start_prop); + TS_ASSERT_EQUALS( runInfo.startTime(), DateAndTime(run_start) ); + // Add start_time and see that get picked up in preference + const std::string start_time("2013-12-19T13:40:00"); + auto start_time_prop = new PropertyWithValue("start_time",start_time); + runInfo.addProperty(start_time_prop); + TS_ASSERT_EQUALS( runInfo.startTime(), DateAndTime(start_time) ); + // But get back run_start again if start_time is equal to the epoch + const std::string epoch("1990-01-01T00:00:00"); + start_time_prop->setValue(epoch); + TS_ASSERT_EQUALS( runInfo.startTime(), DateAndTime(run_start) ); + // And back to failure if they're both that + run_start_prop->setValue(epoch); + TS_ASSERT_THROWS( runInfo.startTime(), std::runtime_error ); + + // Set run_start back to valid value and make start_time contain nonsense + run_start_prop->setValue(run_start); + start_time_prop->setValue("__"); + TS_ASSERT_EQUALS( runInfo.startTime(), DateAndTime(run_start) ); + // Now make start_time a completely different property type + runInfo.removeProperty("start_time"); + runInfo.addProperty(new PropertyWithValue("start_time",3.33)); + TS_ASSERT_EQUALS( runInfo.startTime(), DateAndTime(run_start) ); + // Now make run_start something invalid + run_start_prop->setValue("notADate"); + TS_ASSERT_THROWS( runInfo.startTime(), std::runtime_error ); + // And check things if it's the wrong property type + runInfo.removeProperty("run_start"); + addTimeSeriesEntry(runInfo,"run_start",4.44); + TS_ASSERT_THROWS( runInfo.startTime(), std::runtime_error ); + } + + void testEndTime() + { + LogManager runInfo; + // Nothing there yet + TS_ASSERT_THROWS( runInfo.endTime(), std::runtime_error ); + // Add run_end and see that get picked up + const std::string run_end("2013-12-19T13:38:00"); + auto run_end_prop = new PropertyWithValue("run_end",run_end); + runInfo.addProperty(run_end_prop); + TS_ASSERT_EQUALS( runInfo.endTime(), DateAndTime(run_end) ); + // Add end_time and see that get picked up in preference + const std::string end_time("2013-12-19T13:40:00"); + auto end_time_prop = new PropertyWithValue("end_time",end_time); + runInfo.addProperty(end_time_prop); + TS_ASSERT_EQUALS( runInfo.endTime(), DateAndTime(end_time) ); + + // Set run_end back to valid value and make end_time contain nonsense + run_end_prop->setValue(run_end); + end_time_prop->setValue("__"); + TS_ASSERT_EQUALS( runInfo.endTime(), DateAndTime(run_end) ); + // Now make end_time a completely different property type + runInfo.removeProperty("end_time"); + runInfo.addProperty(new PropertyWithValue("end_time",3.33)); + TS_ASSERT_EQUALS( runInfo.endTime(), DateAndTime(run_end) ); + // Now make run_end something invalid + run_end_prop->setValue("notADate"); + TS_ASSERT_THROWS( runInfo.endTime(), std::runtime_error ); + // And check things if it's the wrong property type + runInfo.removeProperty("run_end"); + addTimeSeriesEntry(runInfo,"run_end",4.44); + TS_ASSERT_THROWS( runInfo.endTime(), std::runtime_error ); + } - void testMemory() { LogManager runInfo; diff --git a/Code/Mantid/Framework/API/test/ScopedWorkspaceTest.h b/Code/Mantid/Framework/API/test/ScopedWorkspaceTest.h index 234dc6337541..245f6bf364f9 100644 --- a/Code/Mantid/Framework/API/test/ScopedWorkspaceTest.h +++ b/Code/Mantid/Framework/API/test/ScopedWorkspaceTest.h @@ -161,6 +161,23 @@ class ScopedWorkspaceTest : public CxxTest::TestSuite TS_ASSERT( test ); } + void test_settingTwice() + { + ScopedWorkspace test; + + MockWorkspace_sptr ws1 = MockWorkspace_sptr(new MockWorkspace); + test.set(ws1); + + TS_ASSERT_EQUALS( ws1->name(), test.name() ); + + MockWorkspace_sptr ws2 = MockWorkspace_sptr(new MockWorkspace); + test.set(ws2); + + TS_ASSERT_EQUALS( ws2->name(), test.name() ); + TS_ASSERT( ws1->name().empty() ); + TS_ASSERT_EQUALS( m_ads.getObjectNamesInclHidden().size(), 1 ); + } + private: AnalysisDataServiceImpl& m_ads; }; diff --git a/Code/Mantid/Framework/Algorithms/CMakeLists.txt b/Code/Mantid/Framework/Algorithms/CMakeLists.txt index 74aa53ac57af..f5a8c245a3ed 100644 --- a/Code/Mantid/Framework/Algorithms/CMakeLists.txt +++ b/Code/Mantid/Framework/Algorithms/CMakeLists.txt @@ -60,6 +60,7 @@ set ( SRC_FILES src/CreatePSDBleedMask.cpp src/CreatePeaksWorkspace.cpp src/CreateSingleValuedWorkspace.cpp + src/CreateTransmissionWorkspace.cpp src/CreateWorkspace.cpp src/CropWorkspace.cpp src/CrossCorrelate.cpp @@ -139,6 +140,7 @@ set ( SRC_FILES src/MultipleScatteringCylinderAbsorption.cpp src/Multiply.cpp src/MultiplyRange.cpp + src/MuonGroupDetectors.cpp src/NormaliseByCurrent.cpp src/NormaliseByDetector.cpp src/NormaliseToMonitor.cpp @@ -146,6 +148,7 @@ set ( SRC_FILES src/OneMinusExponentialCor.cpp src/PDFFourierTransform.cpp src/Pause.cpp + src/PerformIndexOperations.cpp src/PlotAsymmetryByLogValue.cpp src/Plus.cpp src/PointByPointVCorrection.cpp @@ -168,6 +171,7 @@ set ( SRC_FILES src/Rebunch.cpp src/RecordPythonScript.cpp src/ReflectometryReductionOne.cpp + src/ReflectometryWorkflowBase.cpp src/Regroup.cpp src/RemoveBins.cpp src/RemoveExpDecay.cpp @@ -281,6 +285,7 @@ set ( INC_FILES inc/MantidAlgorithms/CreatePSDBleedMask.h inc/MantidAlgorithms/CreatePeaksWorkspace.h inc/MantidAlgorithms/CreateSingleValuedWorkspace.h + inc/MantidAlgorithms/CreateTransmissionWorkspace.h inc/MantidAlgorithms/CreateWorkspace.h inc/MantidAlgorithms/CropWorkspace.h inc/MantidAlgorithms/CrossCorrelate.h @@ -361,6 +366,7 @@ set ( INC_FILES inc/MantidAlgorithms/MultipleScatteringCylinderAbsorption.h inc/MantidAlgorithms/Multiply.h inc/MantidAlgorithms/MultiplyRange.h + inc/MantidAlgorithms/MuonGroupDetectors.h inc/MantidAlgorithms/NormaliseByCurrent.h inc/MantidAlgorithms/NormaliseByDetector.h inc/MantidAlgorithms/NormaliseToMonitor.h @@ -368,6 +374,7 @@ set ( INC_FILES inc/MantidAlgorithms/OneMinusExponentialCor.h inc/MantidAlgorithms/PDFFourierTransform.h inc/MantidAlgorithms/Pause.h + inc/MantidAlgorithms/PerformIndexOperations.h inc/MantidAlgorithms/PlotAsymmetryByLogValue.h inc/MantidAlgorithms/Plus.h inc/MantidAlgorithms/PointByPointVCorrection.h @@ -390,6 +397,7 @@ set ( INC_FILES inc/MantidAlgorithms/Rebunch.h inc/MantidAlgorithms/RecordPythonScript.h inc/MantidAlgorithms/ReflectometryReductionOne.h + inc/MantidAlgorithms/ReflectometryWorkflowBase.h inc/MantidAlgorithms/Regroup.h inc/MantidAlgorithms/RemoveBins.h inc/MantidAlgorithms/RemoveExpDecay.h @@ -581,12 +589,14 @@ set ( TEST_FILES MultipleScatteringCylinderAbsorptionTest.h MultiplyRangeTest.h MultiplyTest.h + MuonGroupDetectorsTest.h NormaliseByCurrentTest.h NormaliseByDetectorTest.h NormaliseToMonitorTest.h OneMinusExponentialCorTest.h PDFFourierTransformTest.h PauseTest.h + PerformIndexOperationsTest.h PlotAsymmetryByLogValueTest.h PlusTest.h PointByPointVCorrectionTest.h diff --git a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/CopySample.h b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/CopySample.h index b6bf2960d8f2..5b9df6ab7865 100644 --- a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/CopySample.h +++ b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/CopySample.h @@ -55,7 +55,6 @@ namespace Algorithms virtual int version() const { return 1;}; /// Algorithm's category for identification virtual const std::string category() const { return "Sample;Utility\\Workspaces;DataHandling";} - /// @inheritdocs virtual std::map validateInputs(); private: /// Sets documentation strings for this algorithm diff --git a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/CreateTransmissionWorkspace.h b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/CreateTransmissionWorkspace.h new file mode 100644 index 000000000000..830af898cffc --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/CreateTransmissionWorkspace.h @@ -0,0 +1,65 @@ +#ifndef MANTID_ALGORITHMS_CREATETRANSMISSIONWORKSPACE_H_ +#define MANTID_ALGORITHMS_CREATETRANSMISSIONWORKSPACE_H_ + +#include "MantidKernel/System.h" +#include "MantidAlgorithms/ReflectometryWorkflowBase.h" + +namespace Mantid +{ + namespace Algorithms + { + + /** CreateTransmissionWorkspace : Create a transmission run workspace in Wavelength given one or more TOF workspaces + + Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: + Code Documentation is available at: + */ + class DLLExport CreateTransmissionWorkspace: public ReflectometryWorkflowBase + { + public: + CreateTransmissionWorkspace(); + virtual ~CreateTransmissionWorkspace(); + + virtual const std::string name() const; + virtual int version() const; + virtual const std::string category() const; + + private: + + /// Make a transmission correction workspace + API::MatrixWorkspace_sptr makeTransmissionCorrection(const std::string& processingCommands, + const MinMax& wavelengthInterval, const MinMax& wavelengthMonitorBackgroundInterval, + const MinMax& wavelengthMonitorIntegrationInterval, const int& i0MonitorIndex, + API::MatrixWorkspace_sptr firstTransmissionRun, + OptionalMatrixWorkspace_sptr secondTransmissionRun, const OptionalDouble& stitchingStartQ, + const OptionalDouble& stitchingDeltaQ, const OptionalDouble& stitchingEndQ, + const OptionalDouble& stitchingStartOverlapQ, const OptionalDouble& stitchingEndOverlapQ, + const double& wavelengthStep); + + virtual void initDocs(); + void init(); + void exec(); + + }; + + } // namespace Algorithms +} // namespace Mantid + +#endif /* MANTID_ALGORITHMS_CREATETRANSMISSIONWORKSPACE_H_ */ diff --git a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/MultipleScatteringCylinderAbsorption.h b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/MultipleScatteringCylinderAbsorption.h index 9bf241c66c10..cf67562e7c71 100644 --- a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/MultipleScatteringCylinderAbsorption.h +++ b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/MultipleScatteringCylinderAbsorption.h @@ -75,20 +75,7 @@ class DLLExport MultipleScatteringCylinderAbsorption : public API::Algorithm // Wavelength function double wavelength( double path_length_m, double tof_us ); - /** MultipleScatteringCylinderAbsorption correction calculation. This - * method will change the values in the y_val array to correct for - * multiple scattering absorption. - * - * @param total_path :: The total flight path in meters - * @param angle_deg :: The scattering angle (two theta) in degrees - * @param radius :: The sample rod radius in cm - * @param coeff1 :: The absorption cross section / 1.81 - * @param coeff2 :: The density - * @param coeff3 :: The total scattering cross section - * @param tof :: Array of times-of-flight at bin boundaries - * (or bin centers) for the spectrum, in microseconds - * @param y_val :: The spectrum values - */ + /// MultipleScatteringCylinderAbsorption correction calculation. void apply_msa_correction( double total_path, double angle_deg, double radius, diff --git a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/MuonGroupDetectors.h b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/MuonGroupDetectors.h new file mode 100644 index 000000000000..6fa3570e3e8e --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/MuonGroupDetectors.h @@ -0,0 +1,56 @@ +#ifndef MANTID_ALGORITHMS_MUONGROUPDETECTORS_H_ +#define MANTID_ALGORITHMS_MUONGROUPDETECTORS_H_ + +#include "MantidKernel/System.h" +#include "MantidAPI/Algorithm.h" + +namespace Mantid +{ +namespace Algorithms +{ + + /** MuonGroupDetectors : applies detector grouping to a workspace. (Muon version) + + Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: + Code Documentation is available at: + */ + class DLLExport MuonGroupDetectors : public API::Algorithm + { + public: + MuonGroupDetectors(); + virtual ~MuonGroupDetectors(); + + virtual const std::string name() const; + virtual int version() const; + virtual const std::string category() const; + + private: + virtual void initDocs(); + void init(); + void exec(); + + + }; + + +} // namespace Algorithms +} // namespace Mantid + +#endif /* MANTID_ALGORITHMS_MUONGROUPDETECTORS_H_ */ diff --git a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/PerformIndexOperations.h b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/PerformIndexOperations.h new file mode 100644 index 000000000000..2e028005f5dd --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/PerformIndexOperations.h @@ -0,0 +1,56 @@ +#ifndef MANTID_ALGORITHMS_PERFORMINDEXOPERATIONS_H_ +#define MANTID_ALGORITHMS_PERFORMINDEXOPERATIONS_H_ + +#include "MantidKernel/System.h" +#include "MantidAPI/DataProcessorAlgorithm.h" + +namespace Mantid +{ +namespace Algorithms +{ + + /** PerformIndexOperations : Crop and sum a workspace according to the parsed workspace index operations provided. + + Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: + Code Documentation is available at: + */ + class DLLExport PerformIndexOperations : public API::DataProcessorAlgorithm + { + public: + PerformIndexOperations(); + virtual ~PerformIndexOperations(); + + virtual const std::string name() const; + virtual int version() const; + virtual const std::string category() const; + + private: + virtual void initDocs(); + void init(); + void exec(); + + + }; + + +} // namespace Algorithms +} // namespace Mantid + +#endif /* MANTID_ALGORITHMS_PERFORMINDEXOPERATIONS_H_ */ diff --git a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/ReflectometryReductionOne.h b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/ReflectometryReductionOne.h index 3df25b1a9b96..0513fd377549 100644 --- a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/ReflectometryReductionOne.h +++ b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/ReflectometryReductionOne.h @@ -2,14 +2,11 @@ #define MANTID_ALGORITHMS_REFLECTOMETRYREDUCTIONONE_H_ #include "MantidKernel/System.h" -#include "MantidAPI/DataProcessorAlgorithm.h" #include "MantidAPI/MatrixWorkspace.h" #include "MantidGeometry/Instrument.h" #include "MantidGeometry/IComponent.h" #include "MantidGeometry/IDetector.h" -#include -#include -#include +#include "MantidAlgorithms/ReflectometryWorkflowBase.h" namespace Mantid { @@ -38,18 +35,10 @@ namespace Mantid File change history is stored at: Code Documentation is available at: */ - class DLLExport ReflectometryReductionOne: public API::DataProcessorAlgorithm + class DLLExport ReflectometryReductionOne: public ReflectometryWorkflowBase { public: - // Class typedefs - typedef boost::tuple MinMax; - typedef boost::optional OptionalDouble; - typedef boost::optional OptionalMatrixWorkspace_sptr; - typedef std::vector WorkspaceIndexList; - typedef boost::optional > OptionalWorkspaceIndexes; - typedef boost::tuple DetectorMonitorWorkspacePair; - /// Constructor ReflectometryReductionOne(); /// Destructor @@ -59,14 +48,9 @@ namespace Mantid virtual int version() const; virtual const std::string category() const; - /// Convert the input workspace to wavelength, splitting according to the properties provided. - DetectorMonitorWorkspacePair toLam(Mantid::API::MatrixWorkspace_sptr toConvert, - const WorkspaceIndexList& detectorIndexRange, const int monitorIndex, - const MinMax& wavelengthMinMax, const MinMax& backgroundMinMax, const double& wavelengthStep); - /// Convert to an IvsQ workspace. Performs detector positional corrections based on the component name and the theta value. - Mantid::API::MatrixWorkspace_sptr toIvsQ(API::MatrixWorkspace_sptr toConvert, const bool correctPosition, const bool isPointDetector, - OptionalDouble& thetaInDeg, Geometry::IComponent_const_sptr sample, Geometry::IComponent_const_sptr detector); + Mantid::API::MatrixWorkspace_sptr toIvsQ(API::MatrixWorkspace_sptr toConvert, const bool correctPosition, + OptionalDouble& thetaInDeg, const bool isPointDetector); private: @@ -77,49 +61,6 @@ namespace Mantid void exec(); - /** Auxillary getters and validators **/ - bool isPropertyDefault(const std::string& propertyName) const; - - /// Get a workspace index list. - WorkspaceIndexList getWorkspaceIndexList() const; - - /// Get min max indexes. - void fetchOptionalLowerUpperPropertyValue(const std::string& propertyName, bool isPointDetector, - OptionalWorkspaceIndexes& optionalUpperLower) const; - - /// Get the min/max property values - MinMax getMinMax(const std::string& minProperty, const std::string& maxProperty) const; - - /// Get the transmission correction properties - void getTransmissionRunInfo(OptionalMatrixWorkspace_sptr& firstTransmissionRun, - OptionalMatrixWorkspace_sptr& secondTransmissionRun, OptionalDouble& stitchingStartQ, - OptionalDouble& stitchingDeltaQ, OptionalDouble& stitchingEndQ, - OptionalDouble& stitchingStartOverlapQ, OptionalDouble& stitchingEndOverlapQ) const; - - /// Validate the transmission correction property inputs - void validateTransmissionInputs() const; - - /** Algorithm running methods **/ - - /// Convert the monitor parts of the input workspace to wavelength - API::MatrixWorkspace_sptr toLamMonitor(const API::MatrixWorkspace_sptr& toConvert, - const int monitorIndex, const MinMax& backgroundMinMax); - - /// Convert the detector spectrum of the input workspace to wavelength - API::MatrixWorkspace_sptr toLamDetector(const WorkspaceIndexList& detectorIndexRange, - const API::MatrixWorkspace_sptr& toConvert, const MinMax& wavelengthMinMax, const double& wavelengthStep); - - /// Perform a transmission correction on the input IvsLam workspace - API::MatrixWorkspace_sptr transmissonCorrection(API::MatrixWorkspace_sptr IvsLam, - const MinMax& wavelengthInterval, const MinMax& wavelengthMonitorBackgroundInterval, - const MinMax& wavelengthMonitorIntegrationInterval, const int& i0MonitorIndex, - API::MatrixWorkspace_sptr firstTransmissionRun, - OptionalMatrixWorkspace_sptr secondTransmissionRun, const OptionalDouble& stitchingStartQ, - const OptionalDouble& stitchingDeltaQ, const OptionalDouble& stitchingEndQ, - const OptionalDouble& stitchingStartOverlapQ, const OptionalDouble& stitchingEndOverlapQ, - const double& wavelengthStep - ); - /// Get the surface sample component Mantid::Geometry::IComponent_const_sptr getSurfaceSampleComponent(Mantid::Geometry::Instrument_const_sptr inst); @@ -133,6 +74,16 @@ namespace Mantid /// Sum spectra. Mantid::API::MatrixWorkspace_sptr sumSpectraOverRange(API::MatrixWorkspace_sptr inWS, const int startIndex, const int endIndex); + /// Perform a transmission correction on the input IvsLam workspace + API::MatrixWorkspace_sptr transmissonCorrection(API::MatrixWorkspace_sptr IvsLam, + const MinMax& wavelengthInterval, const MinMax& wavelengthMonitorBackgroundInterval, + const MinMax& wavelengthMonitorIntegrationInterval, const int& i0MonitorIndex, + API::MatrixWorkspace_sptr firstTransmissionRun, + OptionalMatrixWorkspace_sptr secondTransmissionRun, const OptionalDouble& stitchingStartQ, + const OptionalDouble& stitchingDeltaQ, const OptionalDouble& stitchingEndQ, + const OptionalDouble& stitchingStartOverlapQ, const OptionalDouble& stitchingEndOverlapQ, + const double& wavelengthStep ); + }; } // namespace Algorithms diff --git a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/ReflectometryWorkflowBase.h b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/ReflectometryWorkflowBase.h new file mode 100644 index 000000000000..b40b408c5dd5 --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/ReflectometryWorkflowBase.h @@ -0,0 +1,110 @@ +#ifndef MANTID_ALGORITHMS_REFLECTOMETRYWORKFLOWBASE_H_ +#define MANTID_ALGORITHMS_REFLECTOMETRYWORKFLOWBASE_H_ + +#include "MantidKernel/System.h" +#include "MantidAPI/DataProcessorAlgorithm.h" + +#include +#include +#include + +namespace Mantid +{ + namespace Algorithms + { + + /** ReflectometryWorkflowBase : Abstract workflow algortithm base class containing common implementation functionality usable + * by concrete reflectometry workflow algorithms. + + Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: + Code Documentation is available at: + */ + class DLLExport ReflectometryWorkflowBase: public API::DataProcessorAlgorithm + { + public: + + // Class typedefs + typedef boost::tuple MinMax; + typedef boost::optional OptionalDouble; + typedef boost::optional OptionalMatrixWorkspace_sptr; + typedef std::vector WorkspaceIndexList; + typedef boost::optional > OptionalWorkspaceIndexes; + typedef boost::tuple DetectorMonitorWorkspacePair; + + ReflectometryWorkflowBase(); + virtual ~ReflectometryWorkflowBase(); + + /// Convert the input workspace to wavelength, splitting according to the properties provided. + DetectorMonitorWorkspacePair toLam(Mantid::API::MatrixWorkspace_sptr toConvert, + const std::string& processingCommands, const int monitorIndex, + const MinMax& wavelengthMinMax, const MinMax& backgroundMinMax, const double& wavelengthStep); + + /// Convert the detector spectrum of the input workspace to wavelength + API::MatrixWorkspace_sptr toLamDetector(const std::string& processingCommands, + const API::MatrixWorkspace_sptr& toConvert, const MinMax& wavelengthMinMax, + const double& wavelengthStep); + + protected: + + /// Determine if the property has it's default value. + bool isPropertyDefault(const std::string& propertyName) const; + + /// Get a workspace index list. + const std::string getWorkspaceIndexList() const; + + /// Get min max indexes. + void fetchOptionalLowerUpperPropertyValue(const std::string& propertyName, bool isPointDetector, + OptionalWorkspaceIndexes& optionalUpperLower) const; + + /// Get the min/max property values + MinMax getMinMax(const std::string& minProperty, const std::string& maxProperty) const; + + /// Get the transmission correction properties + void getTransmissionRunInfo(OptionalMatrixWorkspace_sptr& firstTransmissionRun, + OptionalMatrixWorkspace_sptr& secondTransmissionRun, OptionalDouble& stitchingStart, + OptionalDouble& stitchingDelta, OptionalDouble& stitchingEnd, + OptionalDouble& stitchingStartOverlap, OptionalDouble& stitchingEndOverlap) const; + + /// Init common index inputs + void initIndexInputs(); + /// Init common wavelength inputs + void initWavelengthInputs(); + /// Init common stitching inputs + void initStitchingInputs(); + + private: + + /// Validate the transmission correction property inputs + void validateSecondTransmissionInputs(const bool firstTransmissionInWavelength) const; + + /// Validate the the first transmission workspace. + bool validateFirstTransmissionInputs() const; + + /// Convert the monitor parts of the input workspace to wavelength + API::MatrixWorkspace_sptr toLamMonitor(const API::MatrixWorkspace_sptr& toConvert, + const int monitorIndex, const MinMax& backgroundMinMax); + + }; + + + } // namespace Algorithms +} // namespace Mantid + +#endif /* MANTID_ALGORITHMS_REFLECTOMETRYWORKFLOWBASE_H_ */ diff --git a/Code/Mantid/Framework/Algorithms/src/AddSampleLog.cpp b/Code/Mantid/Framework/Algorithms/src/AddSampleLog.cpp index 72a3c7454661..4eba3d381c74 100644 --- a/Code/Mantid/Framework/Algorithms/src/AddSampleLog.cpp +++ b/Code/Mantid/Framework/Algorithms/src/AddSampleLog.cpp @@ -1,7 +1,9 @@ /*WIKI* Workspaces contain information in logs. Often these detail what happened to the sample during the experiment. This algorithm allows one named log to be entered. -The log can be either a String, a Number, or a Number Series. If you select Number Series, the current time will be used as the time of the log entry, and the number in the text used as the (only) value. +The log can be either a String, a Number, or a Number Series. If you select Number Series, the workspace start time will be used as the time of the log entry, and the number in the text used as the (only) value. + +If the LogText contains a numeric value, the created log will be of integer type if an integer is passed and floating point (double) otherwise. This applies to both the Number & Number Series options. *WIKI*/ //---------------------------------------------------------------------- // Includes @@ -66,31 +68,55 @@ void AddSampleLog::exec() // Remove any existing log if (theRun.hasProperty(propName)) + { theRun.removeLogData(propName); + } if (propType == "String") { theRun.addLogData(new PropertyWithValue(propName, propValue)); + return; } - else if (propType == "Number") + + bool valueIsInt(false); + int intVal; + double dblVal; + if ( Strings::convert(propValue, intVal) ) { - double val; - if (!Strings::convert(propValue, val)) - throw std::invalid_argument("Error interpreting string '" + propValue + "' as a number."); - theRun.addLogData(new PropertyWithValue(propName, val)); + valueIsInt = true; } - else if (propType == "Number Series") + else if ( !Strings::convert(propValue, dblVal) ) { - double val; - if (!Strings::convert(propValue, val)) - throw std::invalid_argument("Error interpreting string '" + propValue + "' as a number."); - Kernel::DateAndTime now = Kernel::DateAndTime::getCurrentTime(); - TimeSeriesProperty * tsp = new TimeSeriesProperty(propName); - tsp->addValue(now, val); - theRun.addLogData(tsp); + throw std::invalid_argument("Error interpreting string '" + propValue + "' as a number."); } - setProperty("Workspace", wSpace); + if (propType == "Number") + { + if (valueIsInt) theRun.addLogData(new PropertyWithValue(propName, intVal)); + else theRun.addLogData(new PropertyWithValue(propName, dblVal)); + } + else if (propType == "Number Series") + { + Kernel::DateAndTime startTime; + try { + startTime = theRun.startTime(); + } catch (std::runtime_error&) { + // Swallow the error - startTime will just be 0 + } + + if (valueIsInt) + { + auto tsp = new TimeSeriesProperty(propName); + tsp->addValue(startTime, intVal); + theRun.addLogData(tsp); + } + else + { + auto tsp = new TimeSeriesProperty(propName); + tsp->addValue(startTime, dblVal); + theRun.addLogData(tsp); + } + } } } // namespace Algorithms diff --git a/Code/Mantid/Framework/Algorithms/src/CalculateFlatBackground.cpp b/Code/Mantid/Framework/Algorithms/src/CalculateFlatBackground.cpp index 2c47252e0130..cf28238e6a01 100644 --- a/Code/Mantid/Framework/Algorithms/src/CalculateFlatBackground.cpp +++ b/Code/Mantid/Framework/Algorithms/src/CalculateFlatBackground.cpp @@ -352,6 +352,7 @@ double CalculateFlatBackground::Mean(const API::MatrixWorkspace_const_sptr WS, c /** * Uses linear algorithm to do the fitting. * + * @param WS The workspace to fit * @param spectrum The spectrum index to fit, using the workspace numbering of the spectra * @param startX An X value in the first bin to be included in the fit * @param endX An X value in the last bin to be included in the fit diff --git a/Code/Mantid/Framework/Algorithms/src/CalculateTransmission.cpp b/Code/Mantid/Framework/Algorithms/src/CalculateTransmission.cpp index 748a77b63b34..83fa17aa2da6 100644 --- a/Code/Mantid/Framework/Algorithms/src/CalculateTransmission.cpp +++ b/Code/Mantid/Framework/Algorithms/src/CalculateTransmission.cpp @@ -361,7 +361,7 @@ API::MatrixWorkspace_sptr CalculateTransmission::fitData(API::MatrixWorkspace_sp /** Uses Polynomial as a ChildAlgorithm to fit the log of the exponential curve expected for the transmission. * @param[in] WS The single-spectrum workspace to fit * @param[in] order The order of the polynomial from 2 to 6 - * @param[out] the coeficients of the polynomial. c[0] + c[1]x + c[2]x^2 + ... + * @param[out] coeficients of the polynomial. c[0] + c[1]x + c[2]x^2 + ... */ API::MatrixWorkspace_sptr CalculateTransmission::fitPolynomial(API::MatrixWorkspace_sptr WS, int order, std::vector & coeficients) { diff --git a/Code/Mantid/Framework/Algorithms/src/CheckWorkspacesMatch.cpp b/Code/Mantid/Framework/Algorithms/src/CheckWorkspacesMatch.cpp index db83f1e91bc8..760975d1338e 100644 --- a/Code/Mantid/Framework/Algorithms/src/CheckWorkspacesMatch.cpp +++ b/Code/Mantid/Framework/Algorithms/src/CheckWorkspacesMatch.cpp @@ -99,7 +99,8 @@ bool CheckWorkspacesMatch::processGroups() * @param groupOne :: Input group 1 * @param groupTwo :: Input group 2 */ -void CheckWorkspacesMatch::processGroups(WorkspaceGroup_const_sptr groupOne, WorkspaceGroup_const_sptr groupTwo) +void CheckWorkspacesMatch::processGroups(boost::shared_ptr groupOne, + boost::shared_ptr groupTwo) { // Check their sizes const size_t totalNum = static_cast(groupOne->getNumberOfEntries()); diff --git a/Code/Mantid/Framework/Algorithms/src/CreateTransmissionWorkspace.cpp b/Code/Mantid/Framework/Algorithms/src/CreateTransmissionWorkspace.cpp new file mode 100644 index 000000000000..d1556289a373 --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/src/CreateTransmissionWorkspace.cpp @@ -0,0 +1,246 @@ +/*WIKI* + Creates a transmission run workspace given one or more TOF workspaces and the original run Workspace. If two workspaces are provided, then + the workspaces are stitched together using [[Stitch1D]]. InputWorkspaces must be in TOF. A single output workspace is generated with x-units of Wavlength in angstroms. + *WIKI*/ + +#include "MantidAlgorithms/CreateTransmissionWorkspace.h" + +#include "MantidAPI/WorkspaceValidators.h" +#include "MantidKernel/MandatoryValidator.h" +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/EnabledWhenProperty.h" +#include "MantidKernel/RebinParamsValidator.h" +#include "MantidKernel/BoundedValidator.h" +#include +#include + +using namespace Mantid::Kernel; +using namespace Mantid::API; + +namespace Mantid +{ + namespace Algorithms + { + + // Register the algorithm into the AlgorithmFactory + DECLARE_ALGORITHM(CreateTransmissionWorkspace) + + //---------------------------------------------------------------------------------------------- + /** Constructor + */ + CreateTransmissionWorkspace::CreateTransmissionWorkspace() + { + } + + //---------------------------------------------------------------------------------------------- + /** Destructor + */ + CreateTransmissionWorkspace::~CreateTransmissionWorkspace() + { + } + + //---------------------------------------------------------------------------------------------- + /// Algorithm's name for identification. @see Algorithm::name + const std::string CreateTransmissionWorkspace::name() const + { + return "CreateTransmissionWorkspace"; + } + ; + + /// Algorithm's version for identification. @see Algorithm::version + int CreateTransmissionWorkspace::version() const + { + return 1; + } + ; + + /// Algorithm's category for identification. @see Algorithm::category + const std::string CreateTransmissionWorkspace::category() const + { + return "Reflectometry\\ISIS"; + } + + //---------------------------------------------------------------------------------------------- + /// Sets documentation strings for this algorithm + void CreateTransmissionWorkspace::initDocs() + { + this->setWikiSummary( + "Creates a transmission run workspace in Wavelength from input TOF workspaces."); + this->setOptionalMessage(this->getWikiSummary()); + } + + //---------------------------------------------------------------------------------------------- + /** Initialize the algorithm's properties. + */ + void CreateTransmissionWorkspace::init() + { + boost::shared_ptr inputValidator = boost::make_shared(); + inputValidator->add(boost::make_shared("TOF")); + + declareProperty( + new WorkspaceProperty("FirstTransmissionRun", "", Direction::Input, + PropertyMode::Mandatory, inputValidator->clone()), + "First transmission run, or the low wavelength transmision run if SecondTransmissionRun is also provided."); + + declareProperty( + new WorkspaceProperty("SecondTransmissionRun", "", Direction::Input, + PropertyMode::Optional, inputValidator->clone()), + "Second, high wavelength transmission run. Optional. Causes the InputWorkspace to be treated as the low wavelength transmission run."); + + this->initStitchingInputs(); + this->initIndexInputs(); + this->initWavelengthInputs(); + + declareProperty(new WorkspaceProperty("OutputWorkspace", "", Direction::Output), + "Output Workspace IvsQ."); + + setPropertySettings("Params", + new Kernel::EnabledWhenProperty("SecondTransmissionWorkspace", IS_NOT_DEFAULT)); + + setPropertySettings("StartOverlap", + new Kernel::EnabledWhenProperty("SecondTransmissionWorkspace", IS_NOT_DEFAULT)); + + setPropertySettings("EndOverlap", + new Kernel::EnabledWhenProperty("SecondTransmissionWorkspace", IS_NOT_DEFAULT)); + + } + + //---------------------------------------------------------------------------------------------- + /** Execute the algorithm. + */ + void CreateTransmissionWorkspace::exec() + { + OptionalMatrixWorkspace_sptr firstTransmissionRun; + OptionalMatrixWorkspace_sptr secondTransmissionRun; + OptionalDouble stitchingStart; + OptionalDouble stitchingDelta; + OptionalDouble stitchingEnd; + OptionalDouble stitchingStartOverlap; + OptionalDouble stitchingEndOverlap; + + // Get the transmission run property information. + getTransmissionRunInfo(firstTransmissionRun, secondTransmissionRun, stitchingStart, + stitchingDelta, stitchingEnd, stitchingStartOverlap, stitchingEndOverlap); + + // Get wavelength intervals. + const MinMax wavelengthInterval = this->getMinMax("WavelengthMin", "WavelengthMax"); + const double wavelengthStep = getProperty("WavelengthStep"); + const MinMax monitorBackgroundWavelengthInterval = getMinMax("MonitorBackgroundWavelengthMin", + "MonitorBackgroundWavelengthMax"); + const MinMax monitorIntegrationWavelengthInterval = getMinMax("MonitorIntegrationWavelengthMin", + "MonitorIntegrationWavelengthMax"); + + const std::string processingCommands = getWorkspaceIndexList(); + + // Get the monitor i0 index + const int i0MonitorIndex = getProperty("I0MonitorIndex"); + + // Create the transmission workspace. + MatrixWorkspace_sptr outWS = this->makeTransmissionCorrection(processingCommands, wavelengthInterval, + monitorBackgroundWavelengthInterval, monitorIntegrationWavelengthInterval, i0MonitorIndex, + firstTransmissionRun.get(), secondTransmissionRun, stitchingStart, stitchingDelta, stitchingEnd, + stitchingStartOverlap, stitchingEndOverlap, wavelengthStep); + + + setProperty("OutputWorkspace", outWS); + } + + + + /** + * Create a transmission corrections workspace utilising one or two workspaces. + * + * Input workspaces are in TOF. These are converted to lambda, normalized and stitched together (if two given). + * + * @param processingCommands : Processing instructions. Usually a list of detector indexes to keep. + * @param wavelengthInterval : Wavelength interval for the run workspace. + * @param wavelengthMonitorBackgroundInterval : Wavelength interval for the monitor background + * @param wavelengthMonitorIntegrationInterval : Wavelength interval for the monitor integration + * @param i0MonitorIndex : Monitor index for the I0 monitor + * @param firstTransmissionRun : The first transmission run + * @param secondTransmissionRun : The second transmission run (optional) + * @param stitchingStart : Stitching start (optional but dependent on secondTransmissionRun) + * @param stitchingDelta : Stitching delta (optional but dependent on secondTransmissionRun) + * @param stitchingEnd : Stitching end (optional but dependent on secondTransmissionRun) + * @param stitchingStartOverlap : Stitching start overlap (optional but dependent on secondTransmissionRun) + * @param stitchingEndOverlap : Stitching end overlap (optional but dependent on secondTransmissionRun) + * @param wavelengthStep : Step in angstroms for rebinning for workspaces converted into wavelength. + * @return A transmission workspace in Wavelength units. + */ + MatrixWorkspace_sptr CreateTransmissionWorkspace::makeTransmissionCorrection( + const std::string& processingCommands, + const MinMax& wavelengthInterval, + const MinMax& wavelengthMonitorBackgroundInterval, + const MinMax& wavelengthMonitorIntegrationInterval, + const int& i0MonitorIndex, + MatrixWorkspace_sptr firstTransmissionRun, + OptionalMatrixWorkspace_sptr secondTransmissionRun, + const OptionalDouble& stitchingStart, + const OptionalDouble& stitchingDelta, + const OptionalDouble& stitchingEnd, + const OptionalDouble& stitchingStartOverlap, + const OptionalDouble& stitchingEndOverlap, + const double& wavelengthStep) + { + auto trans1InLam = toLam(firstTransmissionRun, processingCommands, i0MonitorIndex, wavelengthInterval, + wavelengthMonitorBackgroundInterval, wavelengthStep); + MatrixWorkspace_sptr trans1Detector = trans1InLam.get<0>(); + MatrixWorkspace_sptr trans1Monitor = trans1InLam.get<1>(); + + // Monitor integration ... can this happen inside the toLam routine? + auto integrationAlg = this->createChildAlgorithm("Integration"); + integrationAlg->initialize(); + integrationAlg->setProperty("InputWorkspace", trans1Monitor); + integrationAlg->setProperty("RangeLower", wavelengthMonitorIntegrationInterval.get<0>()); + integrationAlg->setProperty("RangeUpper", wavelengthMonitorIntegrationInterval.get<1>()); + integrationAlg->execute(); + trans1Monitor = integrationAlg->getProperty("OutputWorkspace"); + + MatrixWorkspace_sptr transmissionWS = trans1Detector / trans1Monitor; + + if (secondTransmissionRun.is_initialized()) + { + auto transRun2 = secondTransmissionRun.get(); + g_log.debug("Extracting second transmission run workspace indexes from spectra"); + + auto trans2InLam = toLam(transRun2, processingCommands, i0MonitorIndex, wavelengthInterval, + wavelengthMonitorBackgroundInterval, wavelengthStep); + + // Unpack the conversion results. + MatrixWorkspace_sptr trans2Detector = trans2InLam.get<0>(); + MatrixWorkspace_sptr trans2Monitor = trans2InLam.get<1>(); + + // Monitor integration ... can this happen inside the toLam routine? + auto integrationAlg = this->createChildAlgorithm("Integration"); + integrationAlg->initialize(); + integrationAlg->setProperty("InputWorkspace", trans2Monitor); + integrationAlg->setProperty("RangeLower", wavelengthMonitorIntegrationInterval.get<0>()); + integrationAlg->setProperty("RangeUpper", wavelengthMonitorIntegrationInterval.get<1>()); + integrationAlg->execute(); + trans2Monitor = integrationAlg->getProperty("OutputWorkspace"); + + MatrixWorkspace_sptr normalizedTrans2 = trans2Detector / trans2Monitor; + + // Stitch the results. + auto stitch1DAlg = this->createChildAlgorithm("Stitch1D"); + stitch1DAlg->initialize(); + AnalysisDataService::Instance().addOrReplace("transmissionWS", transmissionWS); + AnalysisDataService::Instance().addOrReplace("normalizedTrans2", normalizedTrans2); + stitch1DAlg->setProperty("LHSWorkspace", transmissionWS); + stitch1DAlg->setProperty("RHSWorkspace", normalizedTrans2); + stitch1DAlg->setProperty("StartOverlap", stitchingStartOverlap.get()); + stitch1DAlg->setProperty("EndOverlap", stitchingEndOverlap.get()); + const std::vector params = boost::assign::list_of(stitchingStart.get())( + stitchingDelta.get())(stitchingEnd.get()).convert_to_container >(); + stitch1DAlg->setProperty("Params", params); + stitch1DAlg->execute(); + transmissionWS = stitch1DAlg->getProperty("OutputWorkspace"); + AnalysisDataService::Instance().remove("transmissionWS"); + AnalysisDataService::Instance().remove("normalizedTrans2"); + } + + return transmissionWS; + } + + } // namespace Algorithms +} // namespace Mantid diff --git a/Code/Mantid/Framework/Algorithms/src/ExtractMaskToTable.cpp b/Code/Mantid/Framework/Algorithms/src/ExtractMaskToTable.cpp index 3af6ea82e041..b6153af5e228 100644 --- a/Code/Mantid/Framework/Algorithms/src/ExtractMaskToTable.cpp +++ b/Code/Mantid/Framework/Algorithms/src/ExtractMaskToTable.cpp @@ -143,6 +143,7 @@ namespace Algorithms //---------------------------------------------------------------------------------------------- /** Parse input TableWorkspace to get a list of detectors IDs of which detector are already masked * @param masktablews :: TableWorkspace containing masking information + * @param maskeddetectorids :: List for holding masked detector IDs */ void ExtractMaskToTable::parseMaskTable(DataObjects::TableWorkspace_sptr masktablews, std::vector& maskeddetectorids) { @@ -323,6 +324,7 @@ namespace Algorithms * @param maskeddetids :: vector of detector IDs of which detectors masked * @param xmin :: minumim x * @param xmax :: maximum x + * @param prevmaskedids :: vector of previous masked detector IDs */ void ExtractMaskToTable::addToTableWorkspace(TableWorkspace_sptr outws, vector maskeddetids, double xmin, double xmax, vector prevmaskedids) diff --git a/Code/Mantid/Framework/Algorithms/src/FilterEvents.cpp b/Code/Mantid/Framework/Algorithms/src/FilterEvents.cpp index ace2cdf10644..542405cb33f2 100644 --- a/Code/Mantid/Framework/Algorithms/src/FilterEvents.cpp +++ b/Code/Mantid/Framework/Algorithms/src/FilterEvents.cpp @@ -18,6 +18,17 @@ Some events are not inside any splitters. They are put to a workspace name ende If input property 'OutputWorkspaceIndexedFrom1' is set to True, then this workspace shall not be outputed. +==== Difference from FilterByLogValue ==== +In FilterByLogValue(), EventList.splitByTime() is used. + +In FilterEvents(), if FilterByPulse is selected true, EventList.SplitByTime is called; +otherwise, EventList.SplitByFullTime() is called instead. + +The difference between splitByTime and splitByFullTime is that splitByTime filters events by pulse time, +and splitByFullTime considers both pulse time and TOF. + +Therefore, FilterByLogValue is not suitable for fast log filtering. + *WIKI*/ #include "MantidAlgorithms/FilterEvents.h" @@ -379,10 +390,11 @@ namespace Algorithms //---------------------------------------------------------------------------------------------- /** Parse TOF-correction table workspace to vectors + * Default correction value is equal to 1. */ void FilterEvents::importDetectorTOFCalibration() { - // 1. Prepare output + // Prepare output (class variables) m_detectorIDs.clear(); m_detTofOffsets.clear(); @@ -390,7 +402,7 @@ namespace Algorithms m_detectorIDs.resize(numhist, 0); m_detTofOffsets.resize(numhist, 1.0); - // 2. Set the detector IDs + // Set up the detector IDs to m_detectorIDs[] for (size_t i = 0; i < numhist; ++i) { // FIXME - current implementation assumes one detector per spectra @@ -411,19 +423,19 @@ namespace Algorithms m_detectorIDs[i] = detid; } - // 3. Apply the correction table if it applies + // Calculate TOF correction value for all detectors if (m_detCorrectWorkspace) { - // If a detector calibration workspace is present + // Obtain correction from detector calibration workspace - // a) Check input workspace + // Check input workspace vector colnames = m_detCorrectWorkspace->getColumnNames(); if (colnames.size() < 2) throw runtime_error("Input table workspace is not valide."); else if (colnames[0].compare("DetectorID") || colnames[1].compare("Correction")) throw runtime_error("Input table workspace has wrong column definition."); - // b) Parse detector to a map + // Parse detector and its TOF offset (i.e., correction) to a map map correctmap; size_t numrows = m_detCorrectWorkspace->rowCount(); for (size_t i = 0; i < numrows; ++i) @@ -437,7 +449,7 @@ namespace Algorithms correctmap.insert(make_pair(detid, offset)); } - // c) Map correction map to list + // Check size of TOF correction map if (correctmap.size() > numhist) { g_log.warning() << "Input correction table workspace has more detectors (" << correctmap.size() @@ -454,6 +466,7 @@ namespace Algorithms throw runtime_error(errss.str()); } + // Map correction map to list map::iterator fiter; for (size_t i = 0; i < numhist; ++i) { @@ -509,7 +522,11 @@ namespace Algorithms const DataObjects::EventList& input_el = m_eventWS->getEventList(iws); // Perform the filtering (using the splitting function and just one output) - if (m_doTOFCorrection) + if (mFilterByPulseTime) + { + input_el.splitByPulseTime(m_splitters, outputs); + } + else if (m_doTOFCorrection) { input_el.splitByFullTime(m_splitters, outputs, m_detTofOffsets[iws], m_doTOFCorrection); } @@ -524,8 +541,9 @@ namespace Algorithms // FIXME - Turn on parallel // PARALLEL_END_INTERUPT_REGION } // END FOR i = 0 - // FIXME - Turn on parallel // PARALLEL_CHECK_INTERUPT_REGION + // FIXME - Turn on parallel + // Finish (1) adding events and splitting the sample logs in each target workspace. progress(0.1+progressamount, "Splitting logs"); @@ -573,8 +591,8 @@ namespace Algorithms } - /* - * Generate splitters for specified workspace index + //---------------------------------------------------------------------------------------------- + /** Generate splitters for specified workspace index as a subset of m_splitters */ void FilterEvents::generateSplitters(int wsindex, Kernel::TimeSplitterType& splitters) { diff --git a/Code/Mantid/Framework/Algorithms/src/FindPeaks.cpp b/Code/Mantid/Framework/Algorithms/src/FindPeaks.cpp index 2003f1574f91..9245be3563cb 100644 --- a/Code/Mantid/Framework/Algorithms/src/FindPeaks.cpp +++ b/Code/Mantid/Framework/Algorithms/src/FindPeaks.cpp @@ -792,8 +792,8 @@ namespace Algorithms //---------------------------------------------------------------------------------------------- /** Find the index of a value (or nearest) in a given sorted vector (vector of x axis) - * @param X :: vector - * @param centre :: value to search + * @param vecX :: vector + * @param x :: value to search */ int FindPeaks::getVectorIndex(const MantidVec &vecX, double x) { @@ -1179,6 +1179,8 @@ namespace Algorithms * @param in_bg0: guessed value of a0 (output) * @param in_bg1: guessed value of a1 (output) * @param in_bg2: guessed value of a2 (output) + * @param i_peakmin :: index for minimum boundary of peak + * @param i_peakmax :: index for maximum boundary of peak */ void FindPeaks::fitPeakHighBackground(const API::MatrixWorkspace_sptr &input, const size_t spectrum, int i_centre, int i_min, int i_max, @@ -1333,7 +1335,7 @@ namespace Algorithms * @param peak :: peak function to fit * @param in_centre :: starting value of peak centre * @param in_height :: starting value of peak height - * @param in_fhwm :: starting value of peak width + * @param in_fwhms :: starting value of peak width * @param peakleftboundary :: left boundary of peak * @param peakrightboundary :: right boundary of peak * @param user_centre :: peak centre input by user @@ -1751,7 +1753,6 @@ namespace Algorithms * @param centre :: estimated peak centre (maximum position) * @param height :: maximum * @param fwhm :: 2 fwhm - * @param error :: reason for estimating peak parameters error. */ std::string FindPeaks::estimatePeakParameters(const MantidVec& vecX, const MantidVec& vecY, size_t i_min, size_t i_max, double& centre, double& height, double& fwhm) @@ -2103,7 +2104,7 @@ namespace Algorithms /** Check the results of the fit algorithm to see if they make sense and update the best parameters. * @param fitAlg :: algorithm object * @param bestEffparams :: vector of double for best effective parameters - * @param bestRarparams :: vector double for raw function parameters + * @param bestRawparams :: vector double for raw function parameters * @param mincost :: chi square * @param expPeakPos :: double as expected peak position * @param expPeakHeight :: double as expected peak height diff --git a/Code/Mantid/Framework/Algorithms/src/FitPeak.cpp b/Code/Mantid/Framework/Algorithms/src/FitPeak.cpp index 3845c085903e..4ab7a2934829 100644 --- a/Code/Mantid/Framework/Algorithms/src/FitPeak.cpp +++ b/Code/Mantid/Framework/Algorithms/src/FitPeak.cpp @@ -107,8 +107,7 @@ namespace Algorithms mustBeNonNegative->setLower(0); declareProperty("WorkspaceIndex", 0, mustBeNonNegative, "Workspace index "); - std::vector peakNames = FunctionFactory::Instance().getFunctionNames(); - vector peakFullNames = addFunctionParameterNames(peakNames); + static vector peakFullNames = addFunctionParameterNames(FunctionFactory::Instance().getFunctionNames()); declareProperty("PeakFunctionType", "", boost::make_shared(peakFullNames), "Peak function type. "); diff --git a/Code/Mantid/Framework/Algorithms/src/GenerateEventsFilter.cpp b/Code/Mantid/Framework/Algorithms/src/GenerateEventsFilter.cpp index 9ea8de389598..18af7891a8aa 100644 --- a/Code/Mantid/Framework/Algorithms/src/GenerateEventsFilter.cpp +++ b/Code/Mantid/Framework/Algorithms/src/GenerateEventsFilter.cpp @@ -36,6 +36,12 @@ SNS DAS records log values upon its changing. The frequency of log sampling is The option to do interpolation is not supported at this moment. +== Comparison to FilterByLogValue == +1. If the first log value is within the specified range and the first log time is after run star time, +FilterByLogValue assumes that the log value before the first recorded log time is also within range, and thus +the first splitter starts from the run star time, while GenerateEventFilter tends to be more conservative, +and thus the first splitter will start from the first log time. + *WIKI*/ //---------------------------------------------------------------------- @@ -190,9 +196,8 @@ namespace Algorithms } Kernel::DateAndTime runstart = m_dataWS->run().startTime(); - // (m_dataWS->run().getProperty("run_start")->value()); g_log.debug() << "DB9441 Run Start = " << runstart << " / " << runstart.totalNanoseconds() - << std::endl; + << "\n"; std::string title = getProperty("TitleOfSplitters"); if (title.size() == 0) @@ -844,7 +849,14 @@ namespace Algorithms bool centre, bool filterIncrease, bool filterDecrease, DateAndTime startTime, DateAndTime stopTime) { - // 0. Set up + // Return if the log is empty. + if (m_dblLog->size() == 0) + { + g_log.warning() << "There is no entry in this property " << m_dblLog->name() << std::endl; + return; + } + + // Set up double timetolerance = 0.0; if (centre) { @@ -852,49 +864,41 @@ namespace Algorithms } time_duration tol = DateAndTime::durationFromSeconds( timetolerance ); - // 1. Do nothing if the log is empty. - if (m_dblLog->size() == 0) - { - g_log.warning() << "There is no entry in this property " << m_dblLog->name() << std::endl; - return; - } - - // 2. Go through the whole log to set up time intervals - Kernel::DateAndTime ZeroTime(0); + // Go through the whole log to set up time intervals + const Kernel::DateAndTime ZeroTime(0); int lastindex = -1; int currindex = -1; - DateAndTime lastTime, currTime; + DateAndTime lastTime; + DateAndTime currTime = ZeroTime; DateAndTime start, stop; size_t progslot = 0; int logsize = m_dblLog->size(); - for (int i = 0; i < logsize; i ++) { - // a) Initialize status flags and new entry - lastTime = currTime; // for loop i, currTime is not defined. + // Initialize status flags and new entry bool breakloop = false; - bool completehalf = false; - bool newsplitter = false; + bool createsplitter = false; + lastTime = currTime; currTime = m_dblLog->nthTime(i); double currValue = m_dblLog->nthValue(i); - // b) Filter out by time and direction (optional) + // Filter out by time and direction (optional) bool intime = false; if (currTime < startTime) { // case i. Too early, do nothing - completehalf = false; + createsplitter = false; } else if (currTime > stopTime) { - // case ii. Too later. Put to splitter if half of splitter is done. But still within range + // case ii. Too late. Put to splitter if half of splitter is done. But still within range breakloop = true; stop = currTime; if (start.totalNanoseconds() > 0) { - completehalf = true; + createsplitter = true; } } else @@ -903,12 +907,13 @@ namespace Algorithms intime = true; } - // c) Filter in time + // Check log within given time range + bool newsplitter = false; // Flag to start a new split in this loop + if (intime) { - // c1) Determine direction + // Determine direction bool correctdir = true; - if (filterIncrease && filterDecrease) { // Both direction is fine @@ -933,11 +938,11 @@ namespace Algorithms if (!correctdir && start.totalNanoseconds() > 0) { stop = currTime; - completehalf = true; + createsplitter = true; } } // END-IF-ELSE: Direction - // c2) See whether this value falls into any range + // Check this value whether it falls into any range if (correctdir) { size_t index = searchValue(logvalueranges, currValue); @@ -967,7 +972,7 @@ namespace Algorithms { // ii. Time to close a region and new a region stop = currTime; - completehalf = true; + createsplitter = true; newsplitter = true; } else if (currindex == lastindex && start.totalNanoseconds() > 0) @@ -977,7 +982,7 @@ namespace Algorithms { // Last entry in the log. Need to flag to close the pair stop = currTime; - completehalf = true; + createsplitter = true; newsplitter = false; } else @@ -1009,7 +1014,7 @@ namespace Algorithms { // Close the interval pair if it has been started. stop = currTime; - completehalf = true; + createsplitter = true; } } // [In-bound: Between interval] else if (!valuewithin2boundaries) @@ -1020,7 +1025,7 @@ namespace Algorithms { // End situation stop = currTime; - completehalf = true; + createsplitter = true; } else { @@ -1039,7 +1044,7 @@ namespace Algorithms if (start.totalNanoseconds() > 0) { stop = currTime; - completehalf = true; + createsplitter = true; g_log.debug() << "DBOP Log Index [2] " << i << " falls Out b/c value range... " << ".\n"; } } @@ -1058,7 +1063,7 @@ namespace Algorithms } // d) Create Splitter - if (completehalf) + if (createsplitter) { if (centre) { @@ -1091,7 +1096,6 @@ namespace Algorithms lastindex = currindex; // f) Progress - // Progress bar.. size_t tmpslot = i*90/m_dblLog->size(); if (tmpslot > progslot) { @@ -1109,10 +1113,12 @@ namespace Algorithms } //----------------------------------------------------------------------------------------------- /** Generate filters for an integer log + * @param splitters :: splitting interval array * @param minvalue :: minimum allowed log value * @param maxvalue :: maximum allowed log value * @param filterIncrease :: include log value increasing period; * @param filterDecrease :: include log value decreasing period + * @param runend :: end of run date and time */ void GenerateEventsFilter::processIntegerValueFilter(TimeSplitterType& splitters, int minvalue, int maxvalue, bool filterIncrease, bool filterDecrease, DateAndTime runend) @@ -1298,12 +1304,10 @@ namespace Algorithms // a) Found if (value == sorteddata[stop]) { - // std::cout << "DB450 Found @ A " << dataranges[stop] << " Index = " << stop << std::endl; return stop; } else { - // std::cout << "DB450 Found @ B " << dataranges[start] << " Index = " << start << std::endl; return start; } } diff --git a/Code/Mantid/Framework/Algorithms/src/GeneratePeaks.cpp b/Code/Mantid/Framework/Algorithms/src/GeneratePeaks.cpp index 2cf455c08846..d9f4b566b35b 100644 --- a/Code/Mantid/Framework/Algorithms/src/GeneratePeaks.cpp +++ b/Code/Mantid/Framework/Algorithms/src/GeneratePeaks.cpp @@ -86,8 +86,7 @@ namespace Algorithms this->declareProperty(new API::WorkspaceProperty("PeakParametersWorkspace", "", Direction::Input), "Input TableWorkspace for peak's parameters."); - std::vector peakNames = FunctionFactory::Instance().getFunctionNames(); - this->declareProperty("PeakFunction", "Gaussian", boost::make_shared(peakNames), + this->declareProperty("PeakFunction", "Gaussian", boost::make_shared(FunctionFactory::Instance().getFunctionNames()), "Peak function to calculate."); this->declareProperty(new API::WorkspaceProperty("InputWorkspace", "", Direction::Input, PropertyMode::Optional), diff --git a/Code/Mantid/Framework/Algorithms/src/GeneratePythonScript.cpp b/Code/Mantid/Framework/Algorithms/src/GeneratePythonScript.cpp index eec03cd04c85..5a9a8b2852bb 100644 --- a/Code/Mantid/Framework/Algorithms/src/GeneratePythonScript.cpp +++ b/Code/Mantid/Framework/Algorithms/src/GeneratePythonScript.cpp @@ -58,8 +58,9 @@ void GeneratePythonScript::init() std::vector exts; exts.push_back(".py"); - declareProperty(new API::FileProperty("Filename","", API::FileProperty::Save, exts), + declareProperty(new API::FileProperty("Filename","", API::FileProperty::OptionalSave, exts), "The file into which the Python script will be generated."); + declareProperty("ScriptText", "",Direction::Output); } //---------------------------------------------------------------------------------------------- @@ -68,15 +69,6 @@ void GeneratePythonScript::init() void GeneratePythonScript::exec() { const Workspace_const_sptr ws = getProperty("InputWorkspace"); - const std::string filename = getPropertyValue("Filename"); - std::ofstream file(filename.c_str(), std::ofstream::trunc); - - - if (NULL == file) - { - g_log.error("Unable to create file: " + filename); - throw Exception::FileError("Unable to create file: " , filename); - } // Get the algorithm histories of the workspace. const WorkspaceHistory wsHistory = ws->getHistory(); @@ -104,9 +96,18 @@ void GeneratePythonScript::exec() generatedScript += *m3_pIter + "\n"; } - file << generatedScript; - file.flush(); - file.close(); + setPropertyValue("ScriptText", generatedScript); + + const std::string filename = getPropertyValue("Filename"); + + if (!filename.empty()) + { + std::ofstream file(filename.c_str(), std::ofstream::trunc); + file << generatedScript; + file.flush(); + file.close(); + } + } //---------------------------------------------------------------------------------------------- /** Generate the line of script corresponding to the given AlgorithmHistory @@ -123,33 +124,46 @@ std::string GeneratePythonScript::genAlgString(const API::AlgorithmHistory &algH const int version = algHist.version(); // Create an unmanaged version of the algorithm, with witch we can compare the parameters later. - const IAlgorithm_sptr ialg_Sptr = AlgorithmManager::Instance().createUnmanaged(name,version); - if(ialg_Sptr) + try { - ialg_Sptr->initialize(); - } + const IAlgorithm_sptr ialg_Sptr = AlgorithmManager::Instance().createUnmanaged(name,version); - // Get the properties of this algorithm history, loop through them, and generate - // a string with the appropriate parameters. - std::vector props = algHist.getProperties(); - std::vector::iterator propsIter = props.begin(); + if(ialg_Sptr) + { + ialg_Sptr->initialize(); + } - for( ; propsIter != props.end(); ++propsIter) - { - std::string paramString = genParamString(*propsIter, ialg_Sptr, name); + // Get the properties of this algorithm history, loop through them, and generate + // a string with the appropriate parameters. + std::vector props = algHist.getProperties(); + std::vector::iterator propsIter = props.begin(); - // Miss out parameters that are empty. - if(paramString.length() != 0) + for( ; propsIter != props.end(); ++propsIter) { - if(algString.length() != 0) + std::string paramString = genParamString(*propsIter, ialg_Sptr, name); + + // Miss out parameters that are empty. + if(paramString.length() != 0) { - algString += ","; + if(algString.length() != 0) + { + algString += ","; + } + algString += paramString; } - algString += paramString; } + return name + "(" + algString + ")"; + } + catch (std::runtime_error &) + { + std::ostringstream os; + algHist.printSelf(os,4); + algString = "ERROR: MISSING ALGORITHM: "+name+ " with parameters" + os.str(); + return algString; } - return name + "(" + algString + ")"; + + } //---------------------------------------------------------------------------------------------- /** Generate the parameter string (of format "[name]='[value]'") for the given PropertyHistory. diff --git a/Code/Mantid/Framework/Algorithms/src/GetDetectorOffsets.cpp b/Code/Mantid/Framework/Algorithms/src/GetDetectorOffsets.cpp index 79e75a49edb8..d28e6bf67d35 100644 --- a/Code/Mantid/Framework/Algorithms/src/GetDetectorOffsets.cpp +++ b/Code/Mantid/Framework/Algorithms/src/GetDetectorOffsets.cpp @@ -87,8 +87,7 @@ namespace Mantid declareProperty(new WorkspaceProperty<>("MaskWorkspace","Mask",Direction::Output), "An output workspace containing the mask."); // Only keep peaks - std::vector peakNames = FunctionFactory::Instance().getFunctionNames(); - declareProperty("PeakFunction", "Gaussian", boost::make_shared(peakNames)); + declareProperty("PeakFunction", "Gaussian", boost::make_shared(FunctionFactory::Instance().getFunctionNames())); declareProperty("MaxOffset", 1.0, "Maximum absolute value of offsets; default is 1"); } diff --git a/Code/Mantid/Framework/Algorithms/src/MaskBinsFromTable.cpp b/Code/Mantid/Framework/Algorithms/src/MaskBinsFromTable.cpp index 4e4102b6d8db..7e9adb92f52b 100644 --- a/Code/Mantid/Framework/Algorithms/src/MaskBinsFromTable.cpp +++ b/Code/Mantid/Framework/Algorithms/src/MaskBinsFromTable.cpp @@ -91,7 +91,6 @@ namespace Algorithms //---------------------------------------------------------------------------------------------- /** Call MaskBins * @param dataws :: MatrixWorkspace to mask bins for - * @param maskws :: TableWorkspace containing the mask bin information */ void MaskBinsFromTable::maskBins(API::MatrixWorkspace_sptr dataws) { diff --git a/Code/Mantid/Framework/Algorithms/src/MultipleScatteringCylinderAbsorption.cpp b/Code/Mantid/Framework/Algorithms/src/MultipleScatteringCylinderAbsorption.cpp index afef6e3d9f8a..fec0868f9835 100644 --- a/Code/Mantid/Framework/Algorithms/src/MultipleScatteringCylinderAbsorption.cpp +++ b/Code/Mantid/Framework/Algorithms/src/MultipleScatteringCylinderAbsorption.cpp @@ -306,10 +306,22 @@ inline double MultipleScatteringCylinderAbsorption::wavelength( double path_leng /** - * Alter the values in the y_vals[] to account for multiple scattering. - * Parameter total_path is in meters, and the sample radius is in cm. + * This method will change the values in the y_val array to correct for + * multiple scattering absorption. Parameter total_path is in meters, and + * the sample radius is in cm. + * + * @param total_path :: The total flight path in meters + * @param angle_deg :: The scattering angle (two theta) in degrees + * @param radius :: The sample rod radius in cm + * @param coeff1 :: The absorption cross section / 1.81 + * @param coeff2 :: The density + * @param coeff3 :: The total scattering cross section + * @param tof :: Array of times-of-flight at bin boundaries + * (or bin centers) for the spectrum, in microseconds + * @param y_val :: The spectrum values + * @param errors :: The spectrum errors */ -void MultipleScatteringCylinderAbsorption::apply_msa_correction(double total_path, double angle_rad, double radius, +void MultipleScatteringCylinderAbsorption::apply_msa_correction(double total_path, double angle_deg, double radius, double coeff1, double coeff2, double coeff3, vector& tof, vector& y_val, std::vector &errors) { @@ -323,7 +335,7 @@ void MultipleScatteringCylinderAbsorption::apply_msa_correction(double total_pat is_histogram = false; vector Z(Z_initial, Z_initial+Z_size); // initialize Z array for this angle - ZSet(angle_rad, Z); + ZSet(angle_deg, Z); double Q2 = coeff1 * coeff2; double sigsct = coeff2 * coeff3; diff --git a/Code/Mantid/Framework/Algorithms/src/MuonGroupDetectors.cpp b/Code/Mantid/Framework/Algorithms/src/MuonGroupDetectors.cpp new file mode 100644 index 000000000000..f860e6e63fb5 --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/src/MuonGroupDetectors.cpp @@ -0,0 +1,152 @@ +/*WIKI* +Applies detector grouping to a workspace. (Muon version). + +Expect the DetectorGroupingTable to contain one column only. It should be of type vector_int (std::vector). Every row corresponds to a group, and the values in the only column are IDs (not indices!) of the detectors which spectra should be contained in the group. Name of the column is not used. + +One detector might be in more than one group. Empty groups are ignored. std::invalid_argument exceptions are thrown if table format is not correct, there are no non-empty groups or one of the detector IDs does not exist in the workspace. +*WIKI*/ + +#include "MantidAlgorithms/MuonGroupDetectors.h" +#include "MantidAPI/WorkspaceFactory.h" +#include "MantidDataObjects/TableWorkspace.h" + + +namespace Mantid +{ +namespace Algorithms +{ + + using namespace Kernel; + using namespace API; + using namespace DataObjects; + + // Register the algorithm into the AlgorithmFactory + DECLARE_ALGORITHM(MuonGroupDetectors) + + //---------------------------------------------------------------------------------------------- + /** Constructor + */ + MuonGroupDetectors::MuonGroupDetectors() + { + } + + //---------------------------------------------------------------------------------------------- + /** Destructor + */ + MuonGroupDetectors::~MuonGroupDetectors() + { + } + + + //---------------------------------------------------------------------------------------------- + /// Algorithm's name for identification. @see Algorithm::name + const std::string MuonGroupDetectors::name() const { return "MuonGroupDetectors";}; + + /// Algorithm's version for identification. @see Algorithm::version + int MuonGroupDetectors::version() const { return 1;}; + + /// Algorithm's category for identification. @see Algorithm::category + const std::string MuonGroupDetectors::category() const { return "Muon"; } + + //---------------------------------------------------------------------------------------------- + /// Sets documentation strings for this algorithm + void MuonGroupDetectors::initDocs() + { + this->setWikiSummary("Applies detector grouping to a workspace. (Muon version)."); + this->setOptionalMessage("Applies detector grouping to a workspace. (Muon version)."); + } + + //---------------------------------------------------------------------------------------------- + /** Initialize the algorithm's properties. + */ + void MuonGroupDetectors::init() + { + declareProperty(new WorkspaceProperty("InputWorkspace","",Direction::Input), + "Workspace to apply grouping to."); + + declareProperty(new WorkspaceProperty("DetectorGroupingTable","",Direction::Input), + "Table with detector grouping information. Check wiki page for table format expected."); + + declareProperty(new WorkspaceProperty("OutputWorkspace","",Direction::Output), + "Workspace with detectors grouped."); + } + + //---------------------------------------------------------------------------------------------- + /** Execute the algorithm. + */ + void MuonGroupDetectors::exec() + { + TableWorkspace_sptr table = getProperty("DetectorGroupingTable"); + + // Check that table does have expected format + if ( table->columnCount() != 1 ) + throw std::invalid_argument("Grouping table should have one column only"); + + if ( table->getColumn(0)->type() != "vector_int" ) + throw std::invalid_argument("Column should be of integer vector type"); + + std::vector nonEmptyRows; // Rows with non-empty groups + nonEmptyRows.reserve(table->rowCount()); // Most of rows will be non-empty + + // First pass to determine how many non-empty groups we have + for ( size_t row = 0; row < table->rowCount(); ++row ) + { + if ( table->cell< std::vector >(row, 0).size() != 0 ) + nonEmptyRows.push_back(row); + } + + if ( nonEmptyRows.size() == 0 ) + throw std::invalid_argument("Detector Grouping Table doesn't contain any non-empty groups"); + + MatrixWorkspace_sptr inWS = getProperty("InputWorkspace"); + + // Create output workspace with all the same parameters as an input one except number of histograms + MatrixWorkspace_sptr outWS = WorkspaceFactory::Instance().create( inWS, nonEmptyRows.size() ); + + // Compile the groups + for ( auto rowIt = nonEmptyRows.begin(); rowIt != nonEmptyRows.end(); ++rowIt ) + { + // Group index in the output workspace + size_t groupIndex = static_cast( std::distance(nonEmptyRows.begin(), rowIt) ); + + std::vector& detectorIDs = table->cell< std::vector >(*rowIt, 0); + + // Recieve detector IDs, but need workspace indices to group, so convert + std::vector wsIndices; + inWS->getIndicesFromDetectorIDs(detectorIDs, wsIndices); + + if ( wsIndices.size() != detectorIDs.size() ) + throw std::invalid_argument("Some of the detector IDs were not found"); + + // We will be setting them anew + outWS->getSpectrum(groupIndex)->clearDetectorIDs(); + + for(auto detIt = wsIndices.begin(); detIt != wsIndices.end(); detIt++) + { + for( size_t i = 0; i < inWS->blocksize(); ++i ) + { + // Sum the y values + outWS->dataY(groupIndex)[i] += inWS->dataY(*detIt)[i]; + + // Sum the errors in quadrature + outWS->dataE(groupIndex)[i] = + sqrt(pow(outWS->dataE(groupIndex)[i], 2) + pow(inWS->dataE(*detIt)[i], 2)); + } + + // Detectors list of the group should contain all the detectors of it's elements + outWS->getSpectrum(groupIndex)->addDetectorIDs( inWS->getSpectrum(*detIt)->getDetectorIDs() ); + } + + // Using the first detector X values + outWS->dataX(groupIndex) = inWS->dataX(wsIndices.front()); + + outWS->getSpectrum(groupIndex)->setSpectrumNo( static_cast(groupIndex + 1) ); + } + + setProperty("OutputWorkspace", outWS); + } + + + +} // namespace Algorithms +} // namespace Mantid diff --git a/Code/Mantid/Framework/Algorithms/src/PerformIndexOperations.cpp b/Code/Mantid/Framework/Algorithms/src/PerformIndexOperations.cpp new file mode 100644 index 000000000000..2fad1bfc8947 --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/src/PerformIndexOperations.cpp @@ -0,0 +1,529 @@ +/*WIKI* + + Performs index operations on a workspace which involve cropping out spectra and summing spectra together. See [[MultiFileLoading]] for similar syntax used during loading, though operations + using step sizes are not supported here. + + == Basic Instructions == + + {| class="wikitable" + !rowspan="2" |Name + !width=200 rowspan="2" |Usage + !rowspan="2" |Description + !colspan="2" |Example + |- + !Input + !Result + |- + !align="center" |List + |align="center" |, + |Used to list workspace_indexes to keep. + |0,1,2 + |Keeps spectrum with workspace indexes 0,1,2 others are cropped out. + |- + !align="center" |Plus + |align="center" |+ + |Used to specify which spectrum are to be summed together via [[SumSpectra]]. For summing ranges use ''Added Range'' + |1+2 + |Sum spectrum with workspace indexes 1, 2 together. + |- + !align="center" |Range + |align="center" |: + |Used to specify a range of N spectrum to keep. + |0:4 + |Keeps spectrum with workspace indexes between 0 and 4. + |- + !align="center" |Added Range + |align="center" |- + |Used to specify a range of spectrum that are to be summed together via [[SumSpectra]]. This is an alternative to the binary ''Plus'' operator. + |1-4 + |Sum spectra corresponding to the workspace indexes between 1 and 4. + |- + |} + + == Complex Instructions == + + The basic instructions listed above can be used in combination because ''', can be used to separate out sets of instructions as well as indexes to keep'''. For example, ''0-2, 3:6'' will add + spectrum with workspace indexes 0-2 together into a single spectrum and then append spectra that correspond to workspace indexes 3-6 from the original workspace (you will have 4 spectra + in the output workspace). + + == Limitations == + + * The ''+'' operator is binary only and works like 0+1, but cannot be used like 0+1+2. Use the Add Range operator ''-'' in these scenarios. + + == Order of operations == + The spectra will appear in the output workspace in the same order that they are defined in the instructions. For example ''1+2, 0'' will have the results of 1+2 as workspace index 0 in the output + workspace and index 0 in the original workspace will be mapped to workspace index 1 in the output workspace. + + + *WIKI*/ + +#include "MantidAlgorithms/PerformIndexOperations.h" +#include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/AlgorithmManager.h" +#include "MantidKernel/Strings.h" +#include +#include +#include + +using namespace Mantid::Kernel; +using namespace Mantid::API; + +namespace +{ + /** + * Command class for executing algorithms on workspaces and appending resulting workspace together. + */ + class Command + { + public: + + virtual bool isValid() const + { + return true; + } + + virtual MatrixWorkspace_sptr execute(MatrixWorkspace_sptr input) const = 0; + + virtual MatrixWorkspace_sptr executeAndAppend(MatrixWorkspace_sptr inputWS, + MatrixWorkspace_sptr toAppend) const + { + if (!this->isValid()) + { + return toAppend; + } + else + { + MatrixWorkspace_sptr current = this->execute(inputWS); + Mantid::API::AlgorithmManagerImpl& factory = Mantid::API::AlgorithmManager::Instance(); + auto conjoinWorkspaceAlg = factory.create("ConjoinWorkspaces"); + conjoinWorkspaceAlg->setChild(true); + conjoinWorkspaceAlg->initialize(); + conjoinWorkspaceAlg->setProperty("InputWorkspace1", toAppend); + conjoinWorkspaceAlg->setProperty("InputWorkspace2", current); + conjoinWorkspaceAlg->execute(); + MatrixWorkspace_sptr outWS = conjoinWorkspaceAlg->getProperty("InputWorkspace1"); + return outWS; + } + } + + virtual ~Command() + { + } + }; + + /// Helper typedef + typedef std::vector > VecCommands; + + /** + * Command yielding no result. + */ + class NullCommand: public Command + { + virtual bool isValid() const + { + return false; + } + virtual MatrixWorkspace_sptr execute(MatrixWorkspace_sptr) const + { + throw std::runtime_error("Should not be attempting ::execute on a NullCommand"); + } + virtual ~NullCommand() + { + } + }; + + /** + * Addition command for summing spectra together. + */ + class AdditionCommand: public Command + { + private: + std::vector m_indexes; + public: + AdditionCommand(const std::vector& indexes) : + m_indexes(indexes) + { + } + + virtual MatrixWorkspace_sptr execute(MatrixWorkspace_sptr inputWS) const + { + MatrixWorkspace_sptr outWS; + if (m_indexes.size() > 0) + { + Mantid::API::AlgorithmManagerImpl& factory = Mantid::API::AlgorithmManager::Instance(); + auto sumSpectraAlg = factory.create("SumSpectra"); + sumSpectraAlg->setChild(true); + sumSpectraAlg->initialize(); + sumSpectraAlg->setProperty("InputWorkspace", inputWS); + sumSpectraAlg->setProperty("ListOfWorkspaceIndices", m_indexes); + sumSpectraAlg->setPropertyValue("OutputWorkspace", "outWS"); + sumSpectraAlg->execute(); + outWS = sumSpectraAlg->getProperty("OutputWorkspace"); + } + return outWS; + } + + virtual ~AdditionCommand() + { + } + }; + + /** + * Command for cropping spectra out of a workspace as a new workspace + */ + class CropCommand: public Command + { + private: + std::vector m_indexes; + public: + CropCommand(const std::vector& indexes) : + m_indexes(indexes) + { + } + + MatrixWorkspace_sptr execute(MatrixWorkspace_sptr inputWS) const + { + + MatrixWorkspace_sptr outWS; + for (size_t i = 0; i < m_indexes.size(); ++i) + { + Mantid::API::AlgorithmManagerImpl& factory = Mantid::API::AlgorithmManager::Instance(); + auto cropWorkspaceAlg = factory.create("CropWorkspace"); + cropWorkspaceAlg->setChild(true); + cropWorkspaceAlg->initialize(); + cropWorkspaceAlg->setProperty("InputWorkspace", inputWS); + cropWorkspaceAlg->setProperty("StartWorkspaceIndex", m_indexes[i]); + cropWorkspaceAlg->setProperty("EndWorkspaceIndex", m_indexes[i]); + cropWorkspaceAlg->setPropertyValue("OutputWorkspace", "outWS"); + cropWorkspaceAlg->execute(); + MatrixWorkspace_sptr subRange = cropWorkspaceAlg->getProperty("OutputWorkspace"); + if (i == 0) + { + outWS = subRange; + } + else + { + auto conjoinWorkspaceAlg = factory.create("ConjoinWorkspaces"); + conjoinWorkspaceAlg->setChild(true); + conjoinWorkspaceAlg->initialize(); + conjoinWorkspaceAlg->setProperty("InputWorkspace1", outWS); + conjoinWorkspaceAlg->setProperty("InputWorkspace2", subRange); + conjoinWorkspaceAlg->execute(); + outWS = conjoinWorkspaceAlg->getProperty("InputWorkspace1"); + } + } + return outWS; + } + virtual ~CropCommand() + { + } + }; + + /** + * Abstract type. Command parsing interface. + */ + class CommandParser + { + public: + virtual Command* interpret(const std::string& instruction) const = 0; + + virtual ~CommandParser() + { + } + }; + + /// Helper typedef for vector of command parsers + typedef std::vector > VecCommandParsers; + + /** + * Command parser base class for common concrete command parser types. + */ + template + class CommandParserBase: public CommandParser + { + public: + virtual Command* interpret(const std::string& instruction) const + { + Command* command = NULL; + boost::regex ex = getRegex(); + if (boost::regex_match(instruction, ex)) + { + auto indexes = Mantid::Kernel::Strings::parseRange(instruction, ",", getSeparator()); + command = new ProductType(indexes); + } + else + { + command = new NullCommand; + } + return command; + } + virtual ~CommandParserBase() + { + } + private: + virtual std::string getSeparator() const = 0; + virtual boost::regex getRegex() const = 0; + }; + + /** + * Parser to interpret Range Addition instructions. + */ + class AdditionParserRange: public CommandParserBase + { + public: + + virtual ~AdditionParserRange() + { + } + + private: + boost::regex getRegex() const + { + return boost::regex("^\\s*[0-9]+\\s*\\-\\s*[0-9]+\\s*$"); + } + std::string getSeparator() const + { + return "-"; + } + }; + + /** + * Parser to interpret Addition instructions. + */ + class AdditionParser: public CommandParser + { + public: + + virtual ~AdditionParser() + { + } + + virtual Command* interpret(const std::string& instruction) const + { + Command* command = NULL; + boost::regex ex("^\\s*[0-9]+\\s*\\+\\s*[0-9]+\\s*$"); + if (boost::regex_match(instruction, ex)) + { + std::vector arguments; + boost::split(arguments, instruction, boost::is_any_of("+")); + int minIndex = -1; + int maxIndex = -1; + Mantid::Kernel::Strings::convert(arguments.front(), minIndex); + Mantid::Kernel::Strings::convert(arguments.back(), maxIndex); + std::vector indexes; + indexes.push_back(minIndex); + indexes.push_back(maxIndex); + command = new AdditionCommand(indexes); + } + else + { + command = new NullCommand; + } + return command; + } + }; + + /** + * Parser to interpret Crop Range instructions. + */ + class CropParserRange: public CommandParserBase + { + public: + + virtual ~CropParserRange() + { + } + private: + boost::regex getRegex() const + { + return boost::regex("^\\s*[0-9]+\\s*:\\s*[0-9]+\\s*$"); + } + std::string getSeparator() const + { + return ":"; + } + }; + + /** + * Parser to interpret single index cropping instructions + */ + class CropParserIndex: public CommandParser + { + public: + + virtual ~CropParserIndex() + { + } + + virtual Command* interpret(const std::string& instruction) const + { + Command* command = NULL; + boost::regex ex("^\\s*[0-9]+\\s*$"); + if (boost::regex_match(instruction, ex)) + { + int index = -1; + Mantid::Kernel::Strings::convert(instruction, index); + std::vector indexes(1, index); + command = new CropCommand(indexes); + } + else + { + command = new NullCommand; + } + return command; + } + + }; + +} + +namespace Mantid +{ + namespace Algorithms + { + + // Register the algorithm into the AlgorithmFactory + DECLARE_ALGORITHM(PerformIndexOperations) + + //---------------------------------------------------------------------------------------------- + /** Constructor + */ + PerformIndexOperations::PerformIndexOperations() + { + } + + //---------------------------------------------------------------------------------------------- + /** Destructor + */ + PerformIndexOperations::~PerformIndexOperations() + { + } + + //---------------------------------------------------------------------------------------------- + /// Algorithm's name for identification. @see Algorithm::name + const std::string PerformIndexOperations::name() const + { + return "PerformIndexOperations"; + } + ; + + /// Algorithm's version for identification. @see Algorithm::version + int PerformIndexOperations::version() const + { + return 1; + } + ; + + /// Algorithm's category for identification. @see Algorithm::category + const std::string PerformIndexOperations::category() const + { + return "Algorithms;Transforms;Splitting"; + } + + //---------------------------------------------------------------------------------------------- + /// Sets documentation strings for this algorithm + void PerformIndexOperations::initDocs() + { + this->setWikiSummary("Process the workspace according to the Index operations provided."); + this->setOptionalMessage(this->getWikiSummary()); + } + + //---------------------------------------------------------------------------------------------- + /** Initialize the algorithm's properties. + */ + void PerformIndexOperations::init() + { + declareProperty(new WorkspaceProperty("InputWorkspace", "", Direction::Input), + "Input to processes workspace."); + declareProperty(new PropertyWithValue("ProcessingInstructions", "", Direction::Input), + "Processing instructions. See full instruction list."); + declareProperty(new WorkspaceProperty("OutputWorkspace", "", Direction::Output), + "Output processed workspace"); + } + + /** + * Interpret the instructions as an ordered list of commands that can be executed later. + * + * @param processingInstructions : Instructions to process + * @return Vector of Commands. Commands wrap mantid-algorithmic steps to achieve the desired result. + */ + VecCommands interpret(const std::string& processingInstructions) + { + std::vector processingInstructionsSplit; + boost::split(processingInstructionsSplit, processingInstructions, boost::is_any_of(",")); + + VecCommandParsers commandParsers; + commandParsers.push_back(boost::make_shared()); + commandParsers.push_back(boost::make_shared()); + commandParsers.push_back(boost::make_shared()); + commandParsers.push_back(boost::make_shared()); + + VecCommands commands; + for (auto it = processingInstructionsSplit.begin(); it != processingInstructionsSplit.end(); ++it) + { + const std::string candidate = *it; + bool parserFound = false; + for (auto parserIt = commandParsers.begin(); parserIt != commandParsers.end(); ++parserIt) + { + auto commandParser = *parserIt; + Command* command = commandParser->interpret(candidate); + boost::shared_ptr commandSptr(command); + if (commandSptr->isValid()) // Do not record invalid commands. + { + parserFound = true; + commands.push_back(commandSptr); + } + } + if (!parserFound) + { + throw std::invalid_argument("Cannot interpret " + candidate); + } + } + return commands; + } + + //---------------------------------------------------------------------------------------------- + /** Execute the algorithm. + */ + void PerformIndexOperations::exec() + { + MatrixWorkspace_sptr inputWorkspace = this->getProperty("InputWorkspace"); + const std::string processingInstructions = this->getProperty("ProcessingInstructions"); + + boost::regex re("^\\s*[0-9]+\\s*$|^(\\s*,*[0-9]+(\\s*(,|:|\\+|\\-)\\s*)*[0-9]*)*$"); + if (!boost::regex_match(processingInstructions, re)) + { + throw std::invalid_argument( + "ProcessingInstructions are not well formed: " + processingInstructions); + } + + if (processingInstructions.empty()) + { + auto cloneWS = this->createChildAlgorithm("CloneWorkspace"); + cloneWS->initialize(); + cloneWS->setProperty("InputWorkspace", inputWorkspace); + cloneWS->execute(); + Workspace_sptr tmp = cloneWS->getProperty("OutputWorkspace"); + MatrixWorkspace_sptr outWS = boost::dynamic_pointer_cast(tmp); + this->setProperty("OutputWorkspace", outWS); + } + else + { + // Interpret the instructions. + VecCommands commands = interpret(processingInstructions); + + // Execute the commands. + auto command = commands[0]; + MatrixWorkspace_sptr outWS = command->execute(inputWorkspace); + for (size_t j = 1; j < commands.size(); ++j) + { + outWS = commands[j]->executeAndAppend(inputWorkspace, outWS); + } + + this->setProperty("OutputWorkspace", outWS); + } + + } + + } // namespace Algorithms +} // namespace Mantid diff --git a/Code/Mantid/Framework/Algorithms/src/PlotAsymmetryByLogValue.cpp b/Code/Mantid/Framework/Algorithms/src/PlotAsymmetryByLogValue.cpp index 80b1207b0611..eac0e5c0a88b 100644 --- a/Code/Mantid/Framework/Algorithms/src/PlotAsymmetryByLogValue.cpp +++ b/Code/Mantid/Framework/Algorithms/src/PlotAsymmetryByLogValue.cpp @@ -270,21 +270,31 @@ namespace Mantid if(m_autogroup) { - IAlgorithm_sptr applyGrouping = createChildAlgorithm("ApplyGroupingFromMuonNexus"); - applyGrouping->initialize(); - applyGrouping->setProperty("InputWorkspace", loadedWs); - applyGrouping->setPropertyValue("Filename", fn.str()); + Workspace_sptr loadedDetGrouping = load->getProperty("DetectorGroupingTable"); + + if ( ! loadedDetGrouping ) + throw std::runtime_error("No grouping info in the file.\n\nPlease specify grouping manually"); + + // Could be groups of workspaces, so need to work with ADS + ScopedWorkspace inWS(loadedWs); + ScopedWorkspace grouping(loadedDetGrouping); + ScopedWorkspace outWS; try { + IAlgorithm_sptr applyGrouping = createChildAlgorithm("MuonGroupDetectors", -1, -1, false); + applyGrouping->initialize(); + applyGrouping->setPropertyValue( "InputWorkspace", inWS.name() ); + applyGrouping->setPropertyValue( "DetectorGroupingTable", grouping.name() ); + applyGrouping->setPropertyValue( "OutputWorkspace", outWS.name() ); applyGrouping->execute(); + + loadedWs = outWS.retrieve(); } catch(...) { - throw std::runtime_error("Unable to auto-group the workspace. Please specify grouping manually"); + throw std::runtime_error("Unable to group detectors.\n\nPlease specify grouping manually."); } - - loadedWs = applyGrouping->getProperty("OutputWorkspace"); } WorkspaceGroup_sptr loadedGroup = boost::dynamic_pointer_cast(loadedWs); diff --git a/Code/Mantid/Framework/Algorithms/src/Plus.cpp b/Code/Mantid/Framework/Algorithms/src/Plus.cpp index 3b88976ea330..1abac4d3d228 100644 --- a/Code/Mantid/Framework/Algorithms/src/Plus.cpp +++ b/Code/Mantid/Framework/Algorithms/src/Plus.cpp @@ -1,8 +1,10 @@ /*WIKI* {{BinaryOperation|verb=added|prep=to|symbol=+}} -For [[EventWorkspace]]s, the Event lists at each workspace index are concatenated to create the output event list at the same workspace index. Note that in some cases, these event lists might be from different detectors; this is not checked against and the event lists will be concatenated anyway. This may or may not be your desired behavior. If you wish to merge different EventWorkspaces while matching their detectors together, use the [[MergeRuns]] algorithm. +===EventWorkspace note=== +For [[EventWorkspace]]s, the event lists at each workspace index are concatenated to create the output event list at the same workspace index. Note that in some (rare*) cases, these event lists might be from different detectors; this is not checked against and the event lists will be concatenated anyway. This may or may not be your desired behavior. If you wish to merge different EventWorkspaces while matching their detectors together, use the [[MergeRuns]] algorithm. +* This could happen, for example, if the workspace operands have not both been processed in an identical fashion and the detectors have somehow been grouped differently. *WIKI*/ /*WIKI_USAGE* '''Python''' diff --git a/Code/Mantid/Framework/Algorithms/src/RadiusSum.cpp b/Code/Mantid/Framework/Algorithms/src/RadiusSum.cpp index 977df0b80736..b7957e4cc7be 100644 --- a/Code/Mantid/Framework/Algorithms/src/RadiusSum.cpp +++ b/Code/Mantid/Framework/Algorithms/src/RadiusSum.cpp @@ -323,7 +323,7 @@ namespace Algorithms * It is important that the input workspace must be a numeric image, and not an instrument related workspace. * The function will raise exception (std::invalid_argument) if an invalid input is give. * - * @see ::inputWorkspaceHasInstrumentAssociated for reference. + * @see RadiusSum::inputWorkspaceHasInstrumentAssociated for reference. * * @param inWS reference to the workspace * @return a list of values that defines the limits of the image in this order: Xmin, Xmax, Ymin, Ymax diff --git a/Code/Mantid/Framework/Algorithms/src/ReflectometryReductionOne.cpp b/Code/Mantid/Framework/Algorithms/src/ReflectometryReductionOne.cpp index af7212bc3132..ccef0b9efc8c 100644 --- a/Code/Mantid/Framework/Algorithms/src/ReflectometryReductionOne.cpp +++ b/Code/Mantid/Framework/Algorithms/src/ReflectometryReductionOne.cpp @@ -56,19 +56,23 @@ namespace Mantid * * @param originWS : Origin workspace, which provides the original workspace index to spectrum id mapping. * @param hostWS : Workspace onto which the resulting workspace indexes will be hosted - * @return Remapped wokspace indexes applicable for the host workspace. + * @return Remapped wokspace indexes applicable for the host workspace. results as comma separated string. */ - ReflectometryReductionOne::WorkspaceIndexList createWorkspaceIndexListFromDetectorWorkspace( - MatrixWorkspace_const_sptr originWS, MatrixWorkspace_const_sptr hostWS) + std::string createWorkspaceIndexListFromDetectorWorkspace(MatrixWorkspace_const_sptr originWS, + MatrixWorkspace_const_sptr hostWS) { auto spectrumMap = originWS->getSpectrumToWorkspaceIndexMap(); - ReflectometryReductionOne::WorkspaceIndexList translatedIndexList; - for (auto it = spectrumMap.begin(); it != spectrumMap.end(); ++it) + auto it = spectrumMap.begin(); + std::stringstream result; + specid_t specId = (*it).first; + result << static_cast(hostWS->getIndexFromSpectrumNumber(specId)); + ++it; + for (; it != spectrumMap.end(); ++it) { - specid_t specId = (*it).first; - translatedIndexList.push_back(static_cast(hostWS->getIndexFromSpectrumNumber(specId))); // Could be slow to do it like this. + specId = (*it).first; + result << "," << static_cast(hostWS->getIndexFromSpectrumNumber(specId)); } - return translatedIndexList; + return result.str(); } /** @@ -81,8 +85,8 @@ namespace Mantid * @param originIndexes : Indexes in terms of the origin workspace * @return WorkspaceIndexes in terms of the host workspace */ - ReflectometryReductionOne::WorkspaceIndexList getIndexesInTermsOf(MatrixWorkspace_const_sptr hostWS, - MatrixWorkspace_sptr originWS, + ReflectometryWorkflowBase::WorkspaceIndexList getIndexesInTermsOf( + MatrixWorkspace_const_sptr hostWS, MatrixWorkspace_sptr originWS, const ReflectometryReductionOne::WorkspaceIndexList& originIndexes) { auto spectrumMap = hostWS->getSpectrumToWorkspaceIndexMap(); @@ -97,6 +101,8 @@ namespace Mantid const std::string multiDetectorAnalysis = "MultiDetectorAnalysis"; const std::string pointDetectorAnalysis = "PointDetectorAnalysis"; + const std::string tofUnitId = "TOF"; + const std::string wavelengthUnitId = "Wavelength"; } /* End of ananomous namespace */ @@ -153,7 +159,7 @@ namespace Mantid void ReflectometryReductionOne::init() { boost::shared_ptr inputValidator = boost::make_shared(); - inputValidator->add(boost::make_shared("TOF")); + inputValidator->add(boost::make_shared(tofUnitId)); declareProperty( new WorkspaceProperty("InputWorkspace", "", Direction::Input, inputValidator), @@ -172,558 +178,74 @@ namespace Mantid declareProperty(new ArrayProperty("RegionOfDirectBeam"), "Indices of the spectra a pair (lower, upper) that mark the ranges that correspond to the direct beam in multi-detector mode."); - declareProperty( - new PropertyWithValue("WavelengthMin", Mantid::EMPTY_DBL(), - boost::make_shared >(), Direction::Input), - "Wavelength minimum in angstroms"); - declareProperty( - new PropertyWithValue("WavelengthMax", Mantid::EMPTY_DBL(), - boost::make_shared >(), Direction::Input), - "Wavelength maximum in angstroms"); - - declareProperty( - new PropertyWithValue("WavelengthStep", 0.05, - boost::make_shared >(), Direction::Input), - "Wavelength rebinning step in angstroms. Defaults to 0.05. Used for rebinning intermediate workspaces converted into wavelength."); - - boost::shared_ptr mandatoryWorkspaceIndex = boost::make_shared< - CompositeValidator>(); - mandatoryWorkspaceIndex->add(boost::make_shared >()); - auto boundedIndex = boost::make_shared >(); - boundedIndex->setLower(0); - mandatoryWorkspaceIndex->add(boundedIndex); - - declareProperty( - new PropertyWithValue("I0MonitorIndex", Mantid::EMPTY_INT(), mandatoryWorkspaceIndex), - "I0 monitor index"); - - declareProperty( - new PropertyWithValue("MonitorBackgroundWavelengthMin", Mantid::EMPTY_DBL(), - boost::make_shared >(), Direction::Input), - "Wavelength minimum for monitor background in angstroms. Taken to be WavelengthMin if not provided."); - - declareProperty( - new PropertyWithValue("MonitorBackgroundWavelengthMax", Mantid::EMPTY_DBL(), - boost::make_shared >(), Direction::Input), - "Wavelength maximum for monitor background in angstroms. Taken to be WavelengthMax if not provided."); - - declareProperty( - new PropertyWithValue("MonitorIntegrationWavelengthMin", Mantid::EMPTY_DBL(), - boost::make_shared >(), Direction::Input), - "Wavelength minimum for integration in angstroms. Taken to be WavelengthMin if not provided."); - declareProperty( - new PropertyWithValue("MonitorIntegrationWavelengthMax", Mantid::EMPTY_DBL(), - boost::make_shared >(), Direction::Input), - "Wavelength maximum for integration in angstroms. Taken to be WavelengthMax if not provided."); - - declareProperty(new ArrayProperty("WorkspaceIndexList"), - "Indices of the spectra in pairs (lower, upper) that mark the ranges that correspond to detectors of interest."); + this->initIndexInputs(); + this->initWavelengthInputs(); declareProperty(new PropertyWithValue("DetectorComponentName", "", Direction::Input), "Name of the detector component i.e. point-detector. If these are not specified, the algorithm will attempt lookup using a standard naming convention."); declareProperty(new PropertyWithValue("SampleComponentName", "", Direction::Input), - "Name of the sample component i.e. some-surface-holder. If these are not specified, the algorithm will attempt lookup using a standard naming convention."); + "Name of the sample component i.e. some-surface-holder. If these are not specified, the algorithm will attempt lookup using a standard naming convention."); - declareProperty(new WorkspaceProperty<>("OutputWorkspace", "", Direction::Output), "Output Workspace IvsQ."); + declareProperty(new WorkspaceProperty<>("OutputWorkspace", "", Direction::Output), + "Output Workspace IvsQ."); - declareProperty(new WorkspaceProperty<>("OutputWorkspaceWavelength", "", Direction::Output, PropertyMode::Optional), "Output Workspace IvsLam. Intermediate workspace."); + declareProperty( + new WorkspaceProperty<>("OutputWorkspaceWavelength", "", Direction::Output, + PropertyMode::Optional), "Output Workspace IvsLam. Intermediate workspace."); declareProperty(new PropertyWithValue("ThetaIn", Mantid::EMPTY_DBL(), Direction::Input), - "Final theta value in degrees. Optional, this value will be calculated internally and provided as ThetaOut if not provided."); + "Final theta value in degrees. Optional, this value will be calculated internally and provided as ThetaOut if not provided."); - declareProperty(new PropertyWithValue("ThetaOut", Mantid::EMPTY_DBL(), Direction::Output), "Calculated final theta in degrees."); + declareProperty(new PropertyWithValue("ThetaOut", Mantid::EMPTY_DBL(), Direction::Output), + "Calculated final theta in degrees."); - declareProperty(new PropertyWithValue("CorrectDetectorPositions", true, Direction::Input), "Correct detector positions using ThetaIn (if given)"); + declareProperty(new PropertyWithValue("CorrectDetectorPositions", true, Direction::Input), + "Correct detector positions using ThetaIn (if given)"); declareProperty( new WorkspaceProperty("FirstTransmissionRun", "", Direction::Input, - PropertyMode::Optional, inputValidator->clone()), + PropertyMode::Optional), "First transmission run, or the low wavelength transmision run if SecondTransmissionRun is also provided."); declareProperty( new WorkspaceProperty("SecondTransmissionRun", "", Direction::Input, PropertyMode::Optional, inputValidator->clone()), "Second, high wavelength transmission run. Optional. Causes the FirstTransmissionRun to be treated as the low wavelength transmission run."); - declareProperty( - new ArrayProperty("Params", boost::make_shared(true)), - "A comma separated list of first bin boundary, width, last bin boundary. " - "These parameters are used for stitching together transmission runs. " - "Values are in q. This input is only needed if a SecondTransmission run is provided."); - - declareProperty( - new PropertyWithValue("StartOverlapQ", Mantid::EMPTY_DBL(), Direction::Input), - "Start Q for stitching transmission runs together"); - declareProperty( - new PropertyWithValue("EndOverlapQ", Mantid::EMPTY_DBL(), Direction::Input), - "End Q for stitching transmission runs together"); + this->initStitchingInputs(); setPropertyGroup("FirstTransmissionRun", "Transmission"); setPropertyGroup("SecondTransmissionRun", "Transmission"); setPropertyGroup("Params", "Transmission"); - setPropertyGroup("StartOverlapQ", "Transmission"); - setPropertyGroup("EndOverlapQ", "Transmission"); + setPropertyGroup("StartOverlap", "Transmission"); + setPropertyGroup("EndOverlap", "Transmission"); // Only do transmission corrections when point detector. - setPropertySettings("FirstTransmissionRun", new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); - setPropertySettings("SecondTransmissionRun", new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); - setPropertySettings("Params", new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); - setPropertySettings("StartOverlapQ", new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); - setPropertySettings("EndOverlapQ", new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); - - // Only use region of interest when in multi-detector analysis mode - setPropertySettings("RegionOfInterest", - new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "MultiDetectorAnalysis")); + setPropertySettings("FirstTransmissionRun", + new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); + setPropertySettings("SecondTransmissionRun", + new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); + setPropertySettings("Params", + new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); + setPropertySettings("StartOverlap", + new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); + setPropertySettings("EndOverlap", + new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "PointDetectorAnalysis")); // Only use region of direct beam when in multi-detector analysis mode. setPropertySettings("RegionOfDirectBeam", new Kernel::EnabledWhenProperty("AnalysisMode", IS_EQUAL_TO, "MultiDetectorAnalysis")); } - /** - * Determine if the property value is the same as the default value. - * This can be used to determine if the property has not been set. - * @param propertyName : Name of property to query - * @return: True only if the property has it's default value. - */ - bool ReflectometryReductionOne::isPropertyDefault(const std::string& propertyName) const - { - Property* property = this->getProperty(propertyName); - return property->isDefault(); - } - - /** - * Helper method used with the stl to determine whether values are negative - * @param value : Value to check - * @return : True if negative. - */ - bool checkNotPositive(const int value) - { - return value < 0; - } - - /** - * Get the workspace index list - * @return Workspace index list. - */ - ReflectometryReductionOne::WorkspaceIndexList ReflectometryReductionOne::getWorkspaceIndexList() const - { - WorkspaceIndexList indexList = getProperty("WorkspaceIndexList"); - if (indexList.size() % 2 != 0 || indexList.size() == 0) - { - throw std::invalid_argument( - "WorkspaceIndex list must be composed of pairs of min index, max index."); - } - - if (std::find_if(indexList.begin(), indexList.end(), checkNotPositive) != indexList.end()) - { - throw std::invalid_argument("WorkspaceIndexList contains negative indexes"); - } - - for (size_t i = 0; (i + 1) < indexList.size(); i += 2) - { - if (indexList[i] > indexList[i + 1]) - throw std::invalid_argument("WorkspaceIndexList pairs must be in min, max order"); - } - return indexList; - } - - /** - * Fetch min, max inputs as a vector (int) if they are non-default and set them to the optionalUpperLower object. - * Performs checks to verify that invalid indexes have not been passed in. - * @param propertyName : Property name to fetch - * @param isPointDetector : Flag indicates that the execution is in point detector mode. - * @param optionalUpperLower : Object to set min and max on. - */ - void ReflectometryReductionOne::fetchOptionalLowerUpperPropertyValue(const std::string& propertyName, - bool isPointDetector, OptionalWorkspaceIndexes& optionalUpperLower) const - { - if (!isPropertyDefault(propertyName)) - { - // Validation of property inputs. - if (isPointDetector) - { - throw std::invalid_argument( - "Cannot have a region of interest property in point detector mode."); - } - std::vector temp = this->getProperty(propertyName); - if (temp.size() != 2) - { - const std::string message = propertyName + " requires a lower and upper boundary"; - throw std::invalid_argument(message); - } - if (temp[0] > temp[1]) - { - throw std::invalid_argument("Min must be <= Max index"); - } - if (std::find_if(temp.begin(), temp.end(), checkNotPositive) != temp.end()) - { - const std::string message = propertyName + " contains negative indexes"; - throw std::invalid_argument(message); - } - // Assignment - optionalUpperLower = temp; - } - } - - /** - * Get min max pairs as a tuple. - * @param minProperty : Property name for the min property - * @param maxProperty : Property name for the max property - * @return A tuple consisting of min, max - */ - ReflectometryReductionOne::MinMax ReflectometryReductionOne::getMinMax( - const std::string& minProperty, const std::string& maxProperty) const - { - const double min = getProperty(minProperty); - const double max = getProperty(maxProperty); - if (min > max) - { - throw std::invalid_argument("Cannot have any WavelengthMin > WavelengthMax"); - } - return MinMax(min, max); - } - - /** - * Validate the transmission workspace inputs when a second transmission run is provided. - * Throws if any of the property values do not make sense. - */ - void ReflectometryReductionOne::validateTransmissionInputs() const - { - // Verify that all the required inputs for the second transmission run are now given. - if (isPropertyDefault("FirstTransmissionRun")) - { - throw std::invalid_argument( - "A SecondTransmissionRun is only valid if a FirstTransmissionRun is provided."); - } - if (isPropertyDefault("Params")) - { - throw std::invalid_argument( - "If a SecondTransmissionRun has been given, then stitching Params for the transmission runs are also required."); - } - if (isPropertyDefault("StartOverlapQ")) - { - throw std::invalid_argument( - "If a SecondTransmissionRun has been given, then a stitching StartOverlapQ for the transmission runs is also required."); - } - if (isPropertyDefault("EndOverlapQ")) - { - throw std::invalid_argument( - "If a SecondTransmissionRun has been given, then a stitching EndOverlapQ for the transmission runs is also required."); - } - const double startOverlapQ = this->getProperty("StartOverlapQ"); - const double endOverlapQ = this->getProperty("EndOverlapQ"); - if (startOverlapQ >= endOverlapQ) - { - throw std::invalid_argument("EndOverlapQ must be > StartOverlapQ"); - } - - } - - /** - * Get the transmission run information. - * - * Transmission runs are optional, but you cannot have the second without the first. Also, stitching - * parameters are required if the second is present. This getter fetches and assigns to the optional reference arguments - * - * @param firstTransmissionRun - * @param secondTransmissionRun - * @param stitchingStartQ - * @param stitchingDeltaQ - * @param stitchingEndQ - */ - void ReflectometryReductionOne::getTransmissionRunInfo( - OptionalMatrixWorkspace_sptr& firstTransmissionRun, - OptionalMatrixWorkspace_sptr& secondTransmissionRun, OptionalDouble& stitchingStartQ, - OptionalDouble& stitchingDeltaQ, OptionalDouble& stitchingEndQ, - OptionalDouble& stitchingStartOverlapQ, OptionalDouble& stitchingEndOverlapQ) const - { - if (!isPropertyDefault("FirstTransmissionRun")) - { - MatrixWorkspace_sptr temp = this->getProperty("FirstTransmissionRun"); - /* - if(temp->getNumberHistograms() > 1) - { - throw std::invalid_argument("Error with FirstTransmissionRun. Only one histogram is permitted for a transmission run."); - } - */ - firstTransmissionRun = temp; - } - - if (!isPropertyDefault("SecondTransmissionRun")) - { - // Check that the property values provided make sense together. - validateTransmissionInputs(); - - // Set the values. - { - MatrixWorkspace_sptr temp = this->getProperty("SecondTransmissionRun"); - secondTransmissionRun = temp; - } - { - std::vector params = getProperty("Params"); - stitchingStartQ = params[0]; - stitchingDeltaQ = params[1]; - stitchingEndQ = params[2]; - } - { - double temp = this->getProperty("StartOverlapQ"); - stitchingStartOverlapQ = temp; - temp = this->getProperty("EndOverlapQ"); - stitchingEndOverlapQ = temp; - } - } - - } - - - /** - * Convert the TOF workspace into a monitor workspace. Crops to the monitorIndex and applying flat background correction as part of the process. - * @param toConvert : TOF wavlength to convert. - * @param monitorIndex : Monitor index to crop to - * @param backgroundMinMax : Min and Max Lambda range for Flat background correction. - * @return The cropped and corrected monitor workspace. - */ - MatrixWorkspace_sptr ReflectometryReductionOne::toLamMonitor(const MatrixWorkspace_sptr& toConvert, const int monitorIndex, const MinMax& backgroundMinMax) - { - // Convert Units. - auto convertUnitsAlg = this->createChildAlgorithm("ConvertUnits"); - convertUnitsAlg->initialize(); - convertUnitsAlg->setProperty("InputWorkspace", toConvert); - convertUnitsAlg->setProperty("Target", "Wavelength"); - convertUnitsAlg->setProperty("AlignBins", true); - convertUnitsAlg->execute(); - - // Crop the to the monitor index. - MatrixWorkspace_sptr monitorWS = convertUnitsAlg->getProperty("OutputWorkspace"); - auto cropWorkspaceAlg = this->createChildAlgorithm("CropWorkspace"); - cropWorkspaceAlg->initialize(); - cropWorkspaceAlg->setProperty("InputWorkspace", monitorWS); - cropWorkspaceAlg->setProperty("StartWorkspaceIndex", monitorIndex); - cropWorkspaceAlg->setProperty("EndWorkspaceIndex", monitorIndex); - cropWorkspaceAlg->execute(); - monitorWS = cropWorkspaceAlg->getProperty("OutputWorkspace"); - - // Flat background correction - auto correctMonitorsAlg = this->createChildAlgorithm("CalculateFlatBackground"); - correctMonitorsAlg->initialize(); - correctMonitorsAlg->setProperty("InputWorkspace", monitorWS); - correctMonitorsAlg->setProperty("WorkspaceIndexList", - boost::assign::list_of(0).convert_to_container >()); - correctMonitorsAlg->setProperty("StartX", backgroundMinMax.get<0>()); - correctMonitorsAlg->setProperty("EndX", backgroundMinMax.get<1>()); - correctMonitorsAlg->execute(); - monitorWS = correctMonitorsAlg->getProperty("OutputWorkspace"); - - return monitorWS; - } - - /** - * Convert to a detector workspace in lambda. - * @param detectorIndexRange : Workspace index ranges to keep - * @param toConvert : TOF wavelength to convert. - * @param wavelengthMinMax : Wavelength minmax to keep. Crop out the rest. - * @param wavelengthStep : Wavelength step for rebinning - * @return Detector workspace in wavelength - */ - MatrixWorkspace_sptr ReflectometryReductionOne::toLamDetector( - const WorkspaceIndexList& detectorIndexRange, const MatrixWorkspace_sptr& toConvert, - const MinMax& wavelengthMinMax, const double& wavelengthStep) - { - // Detector Workspace Processing - MatrixWorkspace_sptr detectorWS; - - // Loop over pairs of detector index ranges. Peform the cropping and then conjoin the results into a single workspace. - for (size_t i = 0; i < detectorIndexRange.size(); i += 2) - { - auto cropWorkspaceAlg = this->createChildAlgorithm("CropWorkspace"); - cropWorkspaceAlg->initialize(); - cropWorkspaceAlg->setProperty("InputWorkspace", toConvert); - cropWorkspaceAlg->setProperty("StartWorkspaceIndex", detectorIndexRange[i]); - cropWorkspaceAlg->setProperty("EndWorkspaceIndex", detectorIndexRange[i + 1]); - cropWorkspaceAlg->execute(); - MatrixWorkspace_sptr subRange = cropWorkspaceAlg->getProperty("OutputWorkspace"); - if (i == 0) - { - detectorWS = subRange; - } - else - { - auto conjoinWorkspaceAlg = this->createChildAlgorithm("ConjoinWorkspaces"); - conjoinWorkspaceAlg->initialize(); - conjoinWorkspaceAlg->setProperty("InputWorkspace1", detectorWS); - conjoinWorkspaceAlg->setProperty("InputWorkspace2", subRange); - conjoinWorkspaceAlg->execute(); - detectorWS = conjoinWorkspaceAlg->getProperty("InputWorkspace1"); - } - } - // Now convert units. Do this after the conjoining step otherwise the x bins will not match up. - auto convertUnitsAlg = this->createChildAlgorithm("ConvertUnits"); - convertUnitsAlg->initialize(); - convertUnitsAlg->setProperty("InputWorkspace", detectorWS); - convertUnitsAlg->setProperty("Target", "Wavelength"); - convertUnitsAlg->setProperty("AlignBins", true); - convertUnitsAlg->execute(); - detectorWS = convertUnitsAlg->getProperty("OutputWorkspace"); - - // Crop out the lambda x-ranges now that the workspace is in wavelength. - auto cropWorkspaceAlg = this->createChildAlgorithm("CropWorkspace"); - cropWorkspaceAlg->initialize(); - cropWorkspaceAlg->setProperty("InputWorkspace", detectorWS); - cropWorkspaceAlg->setProperty("XMin", wavelengthMinMax.get<0>()); - cropWorkspaceAlg->setProperty("XMax", wavelengthMinMax.get<1>()); - cropWorkspaceAlg->execute(); - detectorWS = cropWorkspaceAlg->getProperty("OutputWorkspace"); - - auto rebinWorkspaceAlg = this->createChildAlgorithm("Rebin"); - rebinWorkspaceAlg->initialize(); - std::vector params = boost::assign::list_of(wavelengthStep); - rebinWorkspaceAlg->setProperty("Params", params); - rebinWorkspaceAlg->setProperty("InputWorkspace", detectorWS); - rebinWorkspaceAlg->execute(); - detectorWS = rebinWorkspaceAlg->getProperty("OutputWorkspace"); - - return detectorWS; - } - - /** - * Convert From a TOF workspace into a detector and monitor workspace both in Lambda. - * @param toConvert: TOF workspace to convert - * @param detectorIndexRange : Detector index ranges - * @param monitorIndex : Monitor index - * @param wavelengthMinMax : Wavelength min max for detector workspace - * @param backgroundMinMax : Wavelength min max for flat background correction of monitor workspace - * @param wavelengthStep : Wavlength step size for rebinning. - * @return Tuple of detector and monitor workspaces - */ - ReflectometryReductionOne::DetectorMonitorWorkspacePair ReflectometryReductionOne::toLam(MatrixWorkspace_sptr toConvert, - const WorkspaceIndexList& detectorIndexRange, const int monitorIndex, - const MinMax& wavelengthMinMax, const MinMax& backgroundMinMax, const double& wavelengthStep) - { - // Detector Workspace Processing - MatrixWorkspace_sptr detectorWS = toLamDetector(detectorIndexRange, toConvert, wavelengthMinMax, wavelengthStep); - - // Monitor Workspace Processing - MatrixWorkspace_sptr monitorWS = toLamMonitor(toConvert, monitorIndex, backgroundMinMax); - - // Rebin the Monitor Workspace to match the Detector Workspace. - auto rebinToWorkspaceAlg = this->createChildAlgorithm("RebinToWorkspace"); - rebinToWorkspaceAlg->initialize(); - rebinToWorkspaceAlg->setProperty("WorkspaceToRebin", monitorWS); - rebinToWorkspaceAlg->setProperty("WorkspaceToMatch", detectorWS); - rebinToWorkspaceAlg->execute(); - monitorWS = rebinToWorkspaceAlg->getProperty("OutputWorkspace"); - - return DetectorMonitorWorkspacePair( detectorWS, monitorWS ); - } - - /** - * Perform Transmission Corrections. - * @param IvsLam : Run workspace which is to be normalized by the results of the transmission corrections. - * @param wavelengthInterval : Wavelength interval for the run workspace. - * @param wavelengthMonitorBackgroundInterval : Wavelength interval for the monitor background - * @param wavelengthMonitorIntegrationInterval : Wavelength interval for the monitor integration - * @param i0MonitorIndex : Monitor index for the I0 monitor - * @param firstTransmissionRun : The first transmission run - * @param secondTransmissionRun : The second transmission run (optional) - * @param stitchingStartQ : Stitching start Q (optional but dependent on secondTransmissionRun) - * @param stitchingDeltaQ : Stitching delta Q (optional but dependent on secondTransmissionRun) - * @param stitchingEndQ : Stitching end Q (optional but dependent on secondTransmissionRun) - * @param stitchingStartOverlapQ : Stitching start Q overlap (optional but dependent on secondTransmissionRun) - * @param stitchingEndOverlapQ : Stitching end Q overlap (optional but dependent on secondTransmissionRun) - * @param wavelengthStep : Step in angstroms for rebinning for workspaces converted into wavelength. - * @return Normalized run workspace by the transmission workspace, which have themselves been converted to Lam, normalized by monitors and possibly stitched together. - */ - MatrixWorkspace_sptr ReflectometryReductionOne::transmissonCorrection(MatrixWorkspace_sptr IvsLam, - const MinMax& wavelengthInterval, - const MinMax& wavelengthMonitorBackgroundInterval, - const MinMax& wavelengthMonitorIntegrationInterval, - const int& i0MonitorIndex, - MatrixWorkspace_sptr firstTransmissionRun, - OptionalMatrixWorkspace_sptr secondTransmissionRun, - const OptionalDouble& stitchingStartQ, - const OptionalDouble& stitchingDeltaQ, - const OptionalDouble& stitchingEndQ, - const OptionalDouble& stitchingStartOverlapQ, - const OptionalDouble& stitchingEndOverlapQ, - const double& wavelengthStep - ) - { - g_log.debug("Extracting first transmission run workspace indexes from spectra"); - const WorkspaceIndexList detectorIndexes = createWorkspaceIndexListFromDetectorWorkspace(IvsLam, firstTransmissionRun); - auto trans1InLam = toLam(firstTransmissionRun, detectorIndexes, i0MonitorIndex, wavelengthInterval, wavelengthMonitorBackgroundInterval, wavelengthStep); - MatrixWorkspace_sptr trans1Detector = trans1InLam.get<0>(); - MatrixWorkspace_sptr trans1Monitor = trans1InLam.get<1>(); - - // Monitor integration ... can this happen inside the toLam routine? - auto integrationAlg = this->createChildAlgorithm("Integration"); - integrationAlg->initialize(); - integrationAlg->setProperty("InputWorkspace", trans1Monitor); - integrationAlg->setProperty("RangeLower", wavelengthMonitorIntegrationInterval.get<0>()); - integrationAlg->setProperty("RangeUpper", wavelengthMonitorIntegrationInterval.get<1>()); - integrationAlg->execute(); - trans1Monitor = integrationAlg->getProperty("OutputWorkspace"); - - MatrixWorkspace_sptr denominator = trans1Detector / trans1Monitor; - - if (secondTransmissionRun.is_initialized()) - { - auto transRun2 = secondTransmissionRun.get(); - g_log.debug("Extracting second transmission run workspace indexes from spectra"); - const WorkspaceIndexList detectorIndexes = createWorkspaceIndexListFromDetectorWorkspace(IvsLam, transRun2); - - auto trans2InLam = toLam(transRun2, detectorIndexes, i0MonitorIndex, wavelengthInterval, - wavelengthMonitorBackgroundInterval, wavelengthStep); - - // Unpack the conversion results. - MatrixWorkspace_sptr trans2Detector = trans2InLam.get<0>(); - MatrixWorkspace_sptr trans2Monitor = trans2InLam.get<1>(); - - // Monitor integration ... can this happen inside the toLam routine? - auto integrationAlg = this->createChildAlgorithm("Integration"); - integrationAlg->initialize(); - integrationAlg->setProperty("InputWorkspace", trans2Monitor); - integrationAlg->setProperty("RangeLower", wavelengthMonitorIntegrationInterval.get<0>()); - integrationAlg->setProperty("RangeUpper", wavelengthMonitorIntegrationInterval.get<1>()); - integrationAlg->execute(); - trans2Monitor = integrationAlg->getProperty("OutputWorkspace"); - - MatrixWorkspace_sptr normalizedTrans2 = trans2Detector / trans2Monitor; - - // Stitch the results. - auto stitch1DAlg = this->createChildAlgorithm("Stitch1D"); - stitch1DAlg->initialize(); - AnalysisDataService::Instance().addOrReplace("denominator", denominator); - AnalysisDataService::Instance().addOrReplace("normalizedTrans2", normalizedTrans2); - stitch1DAlg->setProperty("LHSWorkspace", denominator); - stitch1DAlg->setProperty("RHSWorkspace", normalizedTrans2); - stitch1DAlg->setProperty("StartOverlap", stitchingStartOverlapQ.get() ); - stitch1DAlg->setProperty("EndOverlap", stitchingEndOverlapQ.get() ); - const std::vector params = boost::assign::list_of(stitchingStartQ.get())(stitchingDeltaQ.get())(stitchingEndQ.get()).convert_to_container >(); - stitch1DAlg->setProperty("Params", params); - stitch1DAlg->execute(); - denominator = stitch1DAlg->getProperty("OutputWorkspace"); - AnalysisDataService::Instance().remove("denominator"); - AnalysisDataService::Instance().remove("normalizedTrans2"); - } - - auto rebinToWorkspaceAlg = this->createChildAlgorithm("RebinToWorkspace"); - rebinToWorkspaceAlg->initialize(); - rebinToWorkspaceAlg->setProperty("WorkspaceToMatch", IvsLam); - rebinToWorkspaceAlg->setProperty("WorkspaceToRebin", denominator); - rebinToWorkspaceAlg->execute(); - denominator = rebinToWorkspaceAlg->getProperty("OutputWorkspace"); - - MatrixWorkspace_sptr normalizedIvsLam = IvsLam / denominator; - return normalizedIvsLam; - } - /** * Correct the position of the detectors based on the input theta value. * @param toCorrect : Workspace to correct detector posisitions on. * @param thetaInDeg : Theta in degrees to use in correction calculations. + * @param sample : Pointer to the sample + * @param detector : Pointer to a given detector */ - void ReflectometryReductionOne::correctPosition(API::MatrixWorkspace_sptr toCorrect, const double& thetaInDeg, IComponent_const_sptr sample, IComponent_const_sptr detector) + void ReflectometryReductionOne::correctPosition(API::MatrixWorkspace_sptr toCorrect, + const double& thetaInDeg, IComponent_const_sptr sample, IComponent_const_sptr detector) { auto instrument = toCorrect->getInstrument(); @@ -736,15 +258,16 @@ namespace Mantid auto referenceFrame = instrument->getReferenceFrame(); - const double sampleToDetectorAlongBeam = sampleToDetector.scalar_prod( referenceFrame->vecPointingAlongBeam() ) ; + const double sampleToDetectorAlongBeam = sampleToDetector.scalar_prod( + referenceFrame->vecPointingAlongBeam()); - const double thetaInRad = thetaInDeg * ( M_PI / 180.0 ); + const double thetaInRad = thetaInDeg * (M_PI / 180.0); - double acrossOffset = 0; + double acrossOffset = 0; - double beamOffset = detectorPosition.scalar_prod( referenceFrame->vecPointingAlongBeam() ); + double beamOffset = detectorPosition.scalar_prod(referenceFrame->vecPointingAlongBeam()); - double upOffset = sampleToDetectorAlongBeam * std::sin( 2.0 * thetaInRad ); + double upOffset = sampleToDetectorAlongBeam * std::sin(2.0 * thetaInRad); auto moveComponentAlg = this->createChildAlgorithm("MoveInstrumentComponent"); moveComponentAlg->initialize(); @@ -765,14 +288,12 @@ namespace Mantid * * @param toConvert : Workspace to convert * @param bCorrectPosition : Flag to indicate that detector positions should be corrected based on the input theta values. - * @param isPointDetector : Flag to indicate that this is a point detector reduction run. * @param thetaInDeg : Theta in Degrees. Used for correction. - * @param sample : Sample component - * @param detector : Detector component + * @param isPointDetector: Is point detector analysis * @return */ - Mantid::API::MatrixWorkspace_sptr ReflectometryReductionOne::toIvsQ(API::MatrixWorkspace_sptr toConvert, const bool bCorrectPosition, - const bool isPointDetector, OptionalDouble& thetaInDeg, Geometry::IComponent_const_sptr sample, Geometry::IComponent_const_sptr detector) + Mantid::API::MatrixWorkspace_sptr ReflectometryReductionOne::toIvsQ( + API::MatrixWorkspace_sptr toConvert, const bool bCorrectPosition, OptionalDouble& thetaInDeg, const bool isPointDetector) { /* * Can either calculate a missing theta value for the purposes of reporting, or correct positions based on a theta value, @@ -785,19 +306,27 @@ namespace Mantid Instrument_const_sptr instrument = toConvert->getInstrument(); + IComponent_const_sptr detector = this->getDetectorComponent(instrument, isPointDetector); + IComponent_const_sptr sample = this->getSurfaceSampleComponent(instrument); + const V3D sampleToDetectorPos = detector->getPos() - sample->getPos(); const V3D sourcePos = instrument->getSource()->getPos(); const V3D beamPos = sample->getPos() - sourcePos; const V3D sampleDetVec = detector->getPos() - sample->getPos(); - const double calculatedTheta = sampleDetVec.angle(beamPos) * thetaToRad * 1 / 2; + const double calculatedTheta = sampleDetVec.angle(beamPos) * thetaToRad * 1 / 2; thetaInDeg = calculatedTheta / thetaToRad; // Assign calculated value it. } - else if( bCorrectPosition ) // This probably ought to be an automatic decision. How about making a guess about sample position holder and detector names. But also allowing the two component names (sample and detector) to be passed in. + else if (bCorrectPosition) // This probably ought to be an automatic decision. How about making a guess about sample position holder and detector names. But also allowing the two component names (sample and detector) to be passed in. { g_log.debug("Correcting detector position"); + + auto instrument = toConvert->getInstrument(); + IComponent_const_sptr detector = this->getDetectorComponent(instrument, isPointDetector); + IComponent_const_sptr sample = this->getSurfaceSampleComponent(instrument); + correctPosition(toConvert, thetaInDeg.get(), sample, detector); } @@ -818,7 +347,8 @@ namespace Mantid * @param inst : Instrument to search through * @return : The component : The component object found. */ - Mantid::Geometry::IComponent_const_sptr ReflectometryReductionOne::getSurfaceSampleComponent(Mantid::Geometry::Instrument_const_sptr inst) + Mantid::Geometry::IComponent_const_sptr ReflectometryReductionOne::getSurfaceSampleComponent( + Mantid::Geometry::Instrument_const_sptr inst) { std::string sampleComponent = "some-surface-holder"; if (!isPropertyDefault("SampleComponentName")) @@ -841,15 +371,16 @@ namespace Mantid * @param isPointDetector : True if this is a point detector. Used to guess a name. * @return The component : The component object found. */ - boost::shared_ptr ReflectometryReductionOne::getDetectorComponent(Mantid::Geometry::Instrument_const_sptr inst, const bool isPointDetector) + boost::shared_ptr ReflectometryReductionOne::getDetectorComponent( + Mantid::Geometry::Instrument_const_sptr inst, const bool isPointDetector) { std::string componentToCorrect = isPointDetector ? "point-detector" : "line-detector"; - if(!isPropertyDefault("DetectorComponentName")) + if (!isPropertyDefault("DetectorComponentName")) { componentToCorrect = this->getPropertyValue("DetectorComponentName"); } - boost::shared_ptr searchResult = inst->getComponentByName(componentToCorrect); - if(searchResult == NULL) + boost::shared_ptr searchResult = inst->getComponentByName(componentToCorrect); + if (searchResult == NULL) { throw std::invalid_argument(componentToCorrect + " does not exist. Check input properties."); } @@ -863,7 +394,8 @@ namespace Mantid * @param endIndex * @return Workspace with spectra summed over the specified range. */ - MatrixWorkspace_sptr ReflectometryReductionOne::sumSpectraOverRange(MatrixWorkspace_sptr inWS, const int startIndex, const int endIndex) + MatrixWorkspace_sptr ReflectometryReductionOne::sumSpectraOverRange(MatrixWorkspace_sptr inWS, + const int startIndex, const int endIndex) { auto sumSpectra = this->createChildAlgorithm("SumSpectra"); sumSpectra->initialize(); @@ -884,13 +416,14 @@ namespace Mantid OptionalMatrixWorkspace_sptr firstTransmissionRun; OptionalMatrixWorkspace_sptr secondTransmissionRun; - OptionalDouble stitchingStartQ; - OptionalDouble stitchingDeltaQ; - OptionalDouble stitchingEndQ; - OptionalDouble stitchingStartOverlapQ; - OptionalDouble stitchingEndOverlapQ; + OptionalDouble stitchingStart; + OptionalDouble stitchingDelta; + OptionalDouble stitchingEnd; + OptionalDouble stitchingStartOverlap; + OptionalDouble stitchingEndOverlap; - getTransmissionRunInfo(firstTransmissionRun, secondTransmissionRun, stitchingStartQ, stitchingDeltaQ, stitchingEndQ, stitchingStartOverlapQ, stitchingEndOverlapQ); + getTransmissionRunInfo(firstTransmissionRun, secondTransmissionRun, stitchingStart, stitchingDelta, + stitchingEnd, stitchingStartOverlap, stitchingEndOverlap); OptionalDouble theta; if (!isPropertyDefault("ThetaIn")) @@ -901,16 +434,16 @@ namespace Mantid const std::string strAnalysisMode = getProperty("AnalysisMode"); const bool isPointDetector = (pointDetectorAnalysis.compare(strAnalysisMode) == 0); + const bool isMultiDetector = (multiDetectorAnalysis.compare(strAnalysisMode) == 0); - const MinMax wavelengthInterval = this->getMinMax("WavelengthMin","WavelengthMax"); + const MinMax wavelengthInterval = this->getMinMax("WavelengthMin", "WavelengthMax"); const double wavelengthStep = getProperty("WavelengthStep"); - const MinMax monitorBackgroundWavelengthInterval = getMinMax("MonitorBackgroundWavelengthMin", "MonitorBackgroundWavelengthMax"); - const MinMax monitorIntegrationWavelengthInterval = getMinMax("MonitorIntegrationWavelengthMin", "MonitorIntegrationWavelengthMax"); - - const WorkspaceIndexList indexList = getWorkspaceIndexList(); + const MinMax monitorBackgroundWavelengthInterval = getMinMax("MonitorBackgroundWavelengthMin", + "MonitorBackgroundWavelengthMax"); + const MinMax monitorIntegrationWavelengthInterval = getMinMax("MonitorIntegrationWavelengthMin", + "MonitorIntegrationWavelengthMax"); - OptionalWorkspaceIndexes regionOfInterest; - fetchOptionalLowerUpperPropertyValue("RegionOfInterest", isPointDetector, regionOfInterest); + const std::string processingCommands = getWorkspaceIndexList(); OptionalWorkspaceIndexes directBeam; fetchOptionalLowerUpperPropertyValue("RegionOfDirectBeam", isPointDetector, directBeam); @@ -919,61 +452,61 @@ namespace Mantid const bool correctDetctorPositions = getProperty("CorrectDetectorPositions"); - auto instrument = runWS->getInstrument(); - IComponent_const_sptr detector = this->getDetectorComponent(instrument, isPointDetector); - IComponent_const_sptr sample = this->getSurfaceSampleComponent(instrument); - - DetectorMonitorWorkspacePair inLam = toLam(runWS, indexList, i0MonitorIndex, wavelengthInterval, monitorBackgroundWavelengthInterval, wavelengthStep); + DetectorMonitorWorkspacePair inLam = toLam(runWS, processingCommands, i0MonitorIndex, + wavelengthInterval, monitorBackgroundWavelengthInterval, wavelengthStep); auto detectorWS = inLam.get<0>(); auto monitorWS = inLam.get<1>(); MatrixWorkspace_sptr IvsLam; // Output workspace MatrixWorkspace_sptr IvsQ; // Output workspace - if(isPointDetector) + if (isMultiDetector) { - auto integrationAlg = this->createChildAlgorithm("Integration"); - integrationAlg->initialize(); - integrationAlg->setProperty("InputWorkspace", monitorWS); - integrationAlg->setProperty("RangeLower", monitorIntegrationWavelengthInterval.get<0>()); - integrationAlg->setProperty("RangeUpper", monitorIntegrationWavelengthInterval.get<1>()); - integrationAlg->execute(); - MatrixWorkspace_sptr integratedMonitor = integrationAlg->getProperty("OutputWorkspace"); - - IvsLam = detectorWS / integratedMonitor; // Normalize by the integrated monitor counts. - - if(firstTransmissionRun.is_initialized()) + if (directBeam.is_initialized()) { + // Sum over the direct beam. + WorkspaceIndexList db = directBeam.get(); + std::stringstream buffer; + buffer << db.front() << "-" << db.back(); + MatrixWorkspace_sptr regionOfDirectBeamWS = this->toLamDetector(buffer.str(), runWS, + wavelengthInterval, wavelengthStep); + + // Rebin to the detector workspace + auto rebinToWorkspaceAlg = this->createChildAlgorithm("RebinToWorkspace"); + rebinToWorkspaceAlg->initialize(); + rebinToWorkspaceAlg->setProperty("WorkspaceToRebin", regionOfDirectBeamWS); + rebinToWorkspaceAlg->setProperty("WorkspaceToMatch", detectorWS); + rebinToWorkspaceAlg->execute(); + regionOfDirectBeamWS = rebinToWorkspaceAlg->getProperty("OutputWorkspace"); + + // Normalize by the direct beam. + detectorWS = detectorWS / regionOfDirectBeamWS; + } + } + auto integrationAlg = this->createChildAlgorithm("Integration"); + integrationAlg->initialize(); + integrationAlg->setProperty("InputWorkspace", monitorWS); + integrationAlg->setProperty("RangeLower", monitorIntegrationWavelengthInterval.get<0>()); + integrationAlg->setProperty("RangeUpper", monitorIntegrationWavelengthInterval.get<1>()); + integrationAlg->execute(); + MatrixWorkspace_sptr integratedMonitor = integrationAlg->getProperty("OutputWorkspace"); - // Perform transmission correction. - IvsLam = this->transmissonCorrection(IvsLam, wavelengthInterval, monitorBackgroundWavelengthInterval, monitorIntegrationWavelengthInterval, - i0MonitorIndex, firstTransmissionRun.get(), secondTransmissionRun, stitchingStartQ, stitchingDeltaQ, stitchingEndQ, stitchingStartOverlapQ, stitchingEndOverlapQ, wavelengthStep); + IvsLam = detectorWS / integratedMonitor; // Normalize by the integrated monitor counts. - } - else - { - g_log.warning("No transmission correction will be applied."); - } + if (firstTransmissionRun.is_initialized()) + { + + // Perform transmission correction. + IvsLam = this->transmissonCorrection(IvsLam, wavelengthInterval, + monitorBackgroundWavelengthInterval, monitorIntegrationWavelengthInterval, i0MonitorIndex, + firstTransmissionRun.get(), secondTransmissionRun, stitchingStart, stitchingDelta, + stitchingEnd, stitchingStartOverlap, stitchingEndOverlap, wavelengthStep); } else { - if(!regionOfInterest.is_initialized()) - { - throw std::invalid_argument("RegionOfInterest must be provided for a multi-detector run."); - } - if(!directBeam.is_initialized()) - { - throw std::invalid_argument("RegionOfDirectBeam must be provided for a multi-detector run."); - } - const WorkspaceIndexList roi = getIndexesInTermsOf(detectorWS, runWS, regionOfInterest.get()); - const WorkspaceIndexList db = getIndexesInTermsOf(detectorWS, runWS, directBeam.get()); - - MatrixWorkspace_sptr regionOfInterestWS = this->sumSpectraOverRange(detectorWS, roi.front(), roi.back()); - MatrixWorkspace_sptr regionOfDirectBeamWS = this->sumSpectraOverRange(detectorWS, db.front(), db.back()); - - IvsLam = regionOfInterestWS / regionOfDirectBeamWS; // TODO. This needs checking. + g_log.warning("No transmission correction will be applied."); } - IvsQ = this->toIvsQ(IvsLam, correctDetctorPositions, isPointDetector, theta, sample, detector); + IvsQ = this->toIvsQ(IvsLam, correctDetctorPositions, theta, isPointDetector); setProperty("ThetaOut", theta.get()); setProperty("OutputWorkspaceWavelength", IvsLam); @@ -981,5 +514,79 @@ namespace Mantid } + /** + * Perform Transmission Corrections. + * @param IvsLam : Run workspace which is to be normalized by the results of the transmission corrections. + * @param wavelengthInterval : Wavelength interval for the run workspace. + * @param wavelengthMonitorBackgroundInterval : Wavelength interval for the monitor background + * @param wavelengthMonitorIntegrationInterval : Wavelength interval for the monitor integration + * @param i0MonitorIndex : Monitor index for the I0 monitor + * @param firstTransmissionRun : The first transmission run + * @param secondTransmissionRun : The second transmission run (optional) + * @param stitchingStart : Stitching start in wavelength (optional but dependent on secondTransmissionRun) + * @param stitchingDelta : Stitching delta in wavelength (optional but dependent on secondTransmissionRun) + * @param stitchingEnd : Stitching end in wavelength (optional but dependent on secondTransmissionRun) + * @param stitchingStartOverlap : Stitching start wavelength overlap (optional but dependent on secondTransmissionRun) + * @param stitchingEndOverlap : Stitching end wavelength overlap (optional but dependent on secondTransmissionRun) + * @param wavelengthStep : Step in angstroms for rebinning for workspaces converted into wavelength. + * @return Normalized run workspace by the transmission workspace, which have themselves been converted to Lam, normalized by monitors and possibly stitched together. + */ + MatrixWorkspace_sptr ReflectometryReductionOne::transmissonCorrection(MatrixWorkspace_sptr IvsLam, + const MinMax& wavelengthInterval, const MinMax& wavelengthMonitorBackgroundInterval, + const MinMax& wavelengthMonitorIntegrationInterval, const int& i0MonitorIndex, + MatrixWorkspace_sptr firstTransmissionRun, OptionalMatrixWorkspace_sptr secondTransmissionRun, + const OptionalDouble& stitchingStart, const OptionalDouble& stitchingDelta, + const OptionalDouble& stitchingEnd, const OptionalDouble& stitchingStartOverlap, + const OptionalDouble& stitchingEndOverlap, const double& wavelengthStep) + { + g_log.debug("Extracting first transmission run workspace indexes from spectra"); + const std::string detectorIndexes = createWorkspaceIndexListFromDetectorWorkspace(IvsLam, + firstTransmissionRun); + + MatrixWorkspace_sptr denominator = firstTransmissionRun; + Unit_const_sptr xUnit = firstTransmissionRun->getAxis(0)->unit(); + if (xUnit->unitID() == tofUnitId) + { + // Make the transmission run. + auto alg = this->createChildAlgorithm("CreateTransmissionWorkspace"); + alg->initialize(); + alg->setProperty("FirstTransmissionRun", firstTransmissionRun); + if (secondTransmissionRun.is_initialized()) + { + alg->setProperty("SecondTransmissionRun", secondTransmissionRun.get()); + const std::vector params = boost::assign::list_of(stitchingStart.get())( + stitchingDelta.get())(stitchingEnd.get()).convert_to_container >(); + alg->setProperty("Params", params); + alg->setProperty("StartOverlap", stitchingStartOverlap.get()); + alg->setProperty("EndOverlap", stitchingEndOverlap.get()); + } + alg->setProperty("ProcessingInstructions", detectorIndexes); + alg->setProperty("I0MonitorIndex", i0MonitorIndex); + alg->setProperty("WavelengthMin", wavelengthInterval.get<0>()); + alg->setProperty("WavelengthMax", wavelengthInterval.get<1>()); + alg->setProperty("WavelengthStep", wavelengthStep); + alg->setProperty("MonitorBackgroundWavelengthMin", wavelengthMonitorBackgroundInterval.get<0>()); + alg->setProperty("MonitorBackgroundWavelengthMax", wavelengthMonitorBackgroundInterval.get<1>()); + alg->setProperty("MonitorIntegrationWavelengthMin", + wavelengthMonitorIntegrationInterval.get<0>()); + alg->setProperty("MonitorIntegrationWavelengthMax", + wavelengthMonitorIntegrationInterval.get<1>()); + alg->execute(); + denominator = alg->getProperty("OutputWorkspace"); + } + + // Rebin the transmission run to be the same as the input. + auto rebinToWorkspaceAlg = this->createChildAlgorithm("RebinToWorkspace"); + rebinToWorkspaceAlg->initialize(); + rebinToWorkspaceAlg->setProperty("WorkspaceToMatch", IvsLam); + rebinToWorkspaceAlg->setProperty("WorkspaceToRebin", denominator); + rebinToWorkspaceAlg->execute(); + denominator = rebinToWorkspaceAlg->getProperty("OutputWorkspace"); + + // Do normalization. + MatrixWorkspace_sptr normalizedIvsLam = IvsLam / denominator; + return normalizedIvsLam; + } + } // namespace Algorithms } // namespace Mantid diff --git a/Code/Mantid/Framework/Algorithms/src/ReflectometryWorkflowBase.cpp b/Code/Mantid/Framework/Algorithms/src/ReflectometryWorkflowBase.cpp new file mode 100644 index 000000000000..13069ee81da7 --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/src/ReflectometryWorkflowBase.cpp @@ -0,0 +1,464 @@ +#include "MantidAPI/WorkspaceValidators.h" +#include "MantidKernel/MandatoryValidator.h" +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/RebinParamsValidator.h" +#include "MantidKernel/BoundedValidator.h" +#include "MantidAlgorithms/ReflectometryWorkflowBase.h" + +#include + +using namespace Mantid::API; +using namespace Mantid::Kernel; + +namespace Mantid +{ + namespace Algorithms + { + namespace + { + + /** + * Helper method used with the stl to determine whether values are negative + * @param value : Value to check + * @return : True if negative. + */ + bool checkNotPositive(const int value) + { + return value < 0; + } + } + + //---------------------------------------------------------------------------------------------- + /** Constructor + */ + ReflectometryWorkflowBase::ReflectometryWorkflowBase() + { + } + + //---------------------------------------------------------------------------------------------- + /** Destructor + */ + ReflectometryWorkflowBase::~ReflectometryWorkflowBase() + { + } + + /** + * Init index properties. + */ + void ReflectometryWorkflowBase::initIndexInputs() + { + + boost::shared_ptr mandatoryWorkspaceIndex = boost::make_shared< + CompositeValidator>(); + mandatoryWorkspaceIndex->add(boost::make_shared >()); + auto boundedIndex = boost::make_shared >(); + boundedIndex->setLower(0); + mandatoryWorkspaceIndex->add(boundedIndex); + + declareProperty( + new PropertyWithValue("I0MonitorIndex", Mantid::EMPTY_INT(), mandatoryWorkspaceIndex), + "I0 monitor index"); + + declareProperty(new PropertyWithValue("ProcessingInstructions", "", + boost::make_shared >(), Direction::Input), + "Processing instructions on workspace indexes to yield only the detectors of interest. See [[PerformIndexOperations]] for details."); + } + + /** + * Init common wavelength inputs. + */ + void ReflectometryWorkflowBase::initWavelengthInputs() + { + declareProperty( + new PropertyWithValue("WavelengthMin", Mantid::EMPTY_DBL(), + boost::make_shared >(), Direction::Input), + "Wavelength minimum in angstroms"); + + declareProperty( + new PropertyWithValue("WavelengthMax", Mantid::EMPTY_DBL(), + boost::make_shared >(), Direction::Input), + "Wavelength maximum in angstroms"); + + declareProperty( + new PropertyWithValue("WavelengthStep", 0.05, + boost::make_shared >(), Direction::Input), + "Wavelength rebinning step in angstroms. Defaults to 0.05. Used for rebinning intermediate workspaces converted into wavelength."); + + declareProperty( + new PropertyWithValue("MonitorBackgroundWavelengthMin", Mantid::EMPTY_DBL(), + boost::make_shared >(), Direction::Input), + "Wavelength minimum for monitor background in angstroms."); + + declareProperty( + new PropertyWithValue("MonitorBackgroundWavelengthMax", Mantid::EMPTY_DBL(), + boost::make_shared >(), Direction::Input), + "Wavelength maximum for monitor background in angstroms."); + + declareProperty( + new PropertyWithValue("MonitorIntegrationWavelengthMin", Mantid::EMPTY_DBL(), + boost::make_shared >(), Direction::Input), + "Wavelength minimum for integration in angstroms."); + declareProperty( + new PropertyWithValue("MonitorIntegrationWavelengthMax", Mantid::EMPTY_DBL(), + boost::make_shared >(), Direction::Input), + "Wavelength maximum for integration in angstroms."); + } + + /** + * Init stitching inputs + */ + void ReflectometryWorkflowBase::initStitchingInputs() + { + declareProperty( + new ArrayProperty("Params", boost::make_shared(true)), + "A comma separated list of first bin boundary, width, last bin boundary. " + "These parameters are used for stitching together transmission runs. " + "Values are in wavelength (angstroms). This input is only needed if a SecondTransmission run is provided."); + + declareProperty( + new PropertyWithValue("StartOverlap", Mantid::EMPTY_DBL(), Direction::Input), + "Start wavelength for stitching transmission runs together"); + + declareProperty( + new PropertyWithValue("EndOverlap", Mantid::EMPTY_DBL(), Direction::Input), + "End wavelength (angstroms) for stitching transmission runs together"); + + } + + /** + * Determine if the property value is the same as the default value. + * This can be used to determine if the property has not been set. + * @param propertyName : Name of property to query + * @return: True only if the property has it's default value. + */ + bool ReflectometryWorkflowBase::isPropertyDefault(const std::string& propertyName) const + { + Property* property = this->getProperty(propertyName); + return property->isDefault(); + } + + /** + * @return The processing instructions. + */ + const std::string ReflectometryWorkflowBase::getWorkspaceIndexList() const + { + const std::string instructions = getProperty("ProcessingInstructions"); + return instructions; + } + + /** + * Fetch min, max inputs as a vector (int) if they are non-default and set them to the optionalUpperLower object. + * Performs checks to verify that invalid indexes have not been passed in. + * @param propertyName : Property name to fetch + * @param isPointDetector : Flag indicates that the execution is in point detector mode. + * @param optionalUpperLower : Object to set min and max on. + */ + void ReflectometryWorkflowBase::fetchOptionalLowerUpperPropertyValue(const std::string& propertyName, + bool isPointDetector, OptionalWorkspaceIndexes& optionalUpperLower) const + { + if (!isPropertyDefault(propertyName)) + { + // Validation of property inputs. + if (isPointDetector) + { + throw std::invalid_argument( + "Cannot have a region of interest property in point detector mode."); + } + std::vector temp = this->getProperty(propertyName); + if (temp.size() != 2) + { + const std::string message = propertyName + " requires a lower and upper boundary"; + throw std::invalid_argument(message); + } + if (temp[0] > temp[1]) + { + throw std::invalid_argument("Min must be <= Max index"); + } + if (std::find_if(temp.begin(), temp.end(), checkNotPositive) != temp.end()) + { + const std::string message = propertyName + " contains negative indexes"; + throw std::invalid_argument(message); + } + // Assignment + optionalUpperLower = temp; + } + } + + /** + * Get min max pairs as a tuple. + * @param minProperty : Property name for the min property + * @param maxProperty : Property name for the max property + * @return A tuple consisting of min, max + */ + ReflectometryWorkflowBase::MinMax ReflectometryWorkflowBase::getMinMax( + const std::string& minProperty, const std::string& maxProperty) const + { + const double min = getProperty(minProperty); + const double max = getProperty(maxProperty); + if (min > max) + { + throw std::invalid_argument("Cannot have any WavelengthMin > WavelengthMax"); + } + return MinMax(min, max); + } + + /** + * Check the first transmission run units. + * + * @return True only if the units of the first transmission run are in wavelength. + * + */ + bool ReflectometryWorkflowBase::validateFirstTransmissionInputs() const + { + WorkspaceUnitValidator tofValidator("TOF"); + WorkspaceUnitValidator wavelengthValidator("Wavelength"); + MatrixWorkspace_sptr firstTransmissionRun = this->getProperty("FirstTransmissionRun"); + if (!tofValidator.isValid(firstTransmissionRun).empty() + && !wavelengthValidator.isValid(firstTransmissionRun).empty()) + { + throw std::invalid_argument("FirstTransmissionRun must be either in TOF or Wavelength"); + } + + const bool bInWavelength = (!wavelengthValidator.isValid(firstTransmissionRun).empty()); + return bInWavelength; + } + + /** + * Validate the transmission workspace inputs when a second transmission run is provided. + * Throws if any of the property values do not make sense. + * @param firstTransmissionInWavelength: Indicates that the first transmission run is in units of wavlength. + */ + void ReflectometryWorkflowBase::validateSecondTransmissionInputs(const bool firstTransmissionInWavelength) const + { + // Verify that all the required inputs for the second transmission run are now given. + if (isPropertyDefault("FirstTransmissionRun")) + { + throw std::invalid_argument( + "A SecondTransmissionRun is only valid if a FirstTransmissionRun is provided."); + if (firstTransmissionInWavelength) + { + this->g_log.warning( + "The first transmission run is in wavelength so is assumed to be correctly stitched in wavelength. " + "The second transmission run and associated inputs will be ignored." + "Run CreateTransmissionWorkspace to create a transmission workspace from TOF runs."); + return; + } + } + if (isPropertyDefault("Params")) + { + throw std::invalid_argument( + "If a SecondTransmissionRun has been given, then stitching Params for the transmission runs are also required."); + } + if (isPropertyDefault("StartOverlap")) + { + throw std::invalid_argument( + "If a SecondTransmissionRun has been given, then a stitching StartOverlap for the transmission runs is also required."); + } + if (isPropertyDefault("EndOverlap")) + { + throw std::invalid_argument( + "If a SecondTransmissionRun has been given, then a stitching EndOverlap for the transmission runs is also required."); + } + const double startOverlap = this->getProperty("StartOverlap"); + const double endOverlap = this->getProperty("EndOverlap"); + if (startOverlap >= endOverlap) + { + throw std::invalid_argument("EndOverlap must be > StartOverlap"); + } + + if( !isPropertyDefault("SecondTransmissionRun") ) + { + MatrixWorkspace_sptr trans1 = this->getProperty("FirstTransmissionRun"); + MatrixWorkspace_sptr trans2 = this->getProperty("SecondTransmissionRun"); + + auto firstMap = trans1->getSpectrumToWorkspaceIndexMap(); + auto secondMap = trans2->getSpectrumToWorkspaceIndexMap(); + if(firstMap != secondMap) + { + throw std::invalid_argument("Spectrum maps differ between the transmission runs. They must be the same."); + } + } + + } + + /** + * Get the transmission run information. + * + * Transmission runs are optional, but you cannot have the second without the first. Also, stitching + * parameters are required if the second is present. This getter fetches and assigns to the optional reference arguments + * + * @param firstTransmissionRun + * @param secondTransmissionRun + * @param stitchingStart + * @param stitchingDelta + * @param stitchingEnd + * @param stitchingStartOverlap + * @param stitchingEndOverlap + */ + void ReflectometryWorkflowBase::getTransmissionRunInfo( + OptionalMatrixWorkspace_sptr& firstTransmissionRun, + OptionalMatrixWorkspace_sptr& secondTransmissionRun, OptionalDouble& stitchingStart, + OptionalDouble& stitchingDelta, OptionalDouble& stitchingEnd, + OptionalDouble& stitchingStartOverlap, OptionalDouble& stitchingEndOverlap) const + { + bool bFirstTransInWavelength = false; + if (!isPropertyDefault("FirstTransmissionRun")) + { + bFirstTransInWavelength = validateFirstTransmissionInputs(); + + MatrixWorkspace_sptr temp = this->getProperty("FirstTransmissionRun"); + firstTransmissionRun = temp; + } + + if (!isPropertyDefault("SecondTransmissionRun")) + { + // Check that the property values provided make sense together. + validateSecondTransmissionInputs(bFirstTransInWavelength); + + // Set the values. + { + MatrixWorkspace_sptr temp = this->getProperty("SecondTransmissionRun"); + secondTransmissionRun = temp; + } + { + std::vector params = getProperty("Params"); + stitchingStart = params[0]; + stitchingDelta = params[1]; + stitchingEnd = params[2]; + } + { + double temp = this->getProperty("StartOverlap"); + stitchingStartOverlap = temp; + temp = this->getProperty("EndOverlap"); + stitchingEndOverlap = temp; + } + } + + } + + /** + * Convert the TOF workspace into a monitor workspace. Crops to the monitorIndex and applying flat background correction as part of the process. + * @param toConvert : TOF wavlength to convert. + * @param monitorIndex : Monitor index to crop to + * @param backgroundMinMax : Min and Max Lambda range for Flat background correction. + * @return The cropped and corrected monitor workspace. + */ + MatrixWorkspace_sptr ReflectometryWorkflowBase::toLamMonitor(const MatrixWorkspace_sptr& toConvert, + const int monitorIndex, const MinMax& backgroundMinMax) + { + // Convert Units. + auto convertUnitsAlg = this->createChildAlgorithm("ConvertUnits"); + convertUnitsAlg->initialize(); + convertUnitsAlg->setProperty("InputWorkspace", toConvert); + convertUnitsAlg->setProperty("Target", "Wavelength"); + convertUnitsAlg->setProperty("AlignBins", true); + convertUnitsAlg->execute(); + + // Crop the to the monitor index. + MatrixWorkspace_sptr monitorWS = convertUnitsAlg->getProperty("OutputWorkspace"); + auto cropWorkspaceAlg = this->createChildAlgorithm("CropWorkspace"); + cropWorkspaceAlg->initialize(); + cropWorkspaceAlg->setProperty("InputWorkspace", monitorWS); + cropWorkspaceAlg->setProperty("StartWorkspaceIndex", monitorIndex); + cropWorkspaceAlg->setProperty("EndWorkspaceIndex", monitorIndex); + cropWorkspaceAlg->execute(); + monitorWS = cropWorkspaceAlg->getProperty("OutputWorkspace"); + + // Flat background correction + auto correctMonitorsAlg = this->createChildAlgorithm("CalculateFlatBackground"); + correctMonitorsAlg->initialize(); + correctMonitorsAlg->setProperty("InputWorkspace", monitorWS); + correctMonitorsAlg->setProperty("WorkspaceIndexList", + boost::assign::list_of(0).convert_to_container >()); + correctMonitorsAlg->setProperty("StartX", backgroundMinMax.get<0>()); + correctMonitorsAlg->setProperty("EndX", backgroundMinMax.get<1>()); + correctMonitorsAlg->execute(); + monitorWS = correctMonitorsAlg->getProperty("OutputWorkspace"); + + return monitorWS; + } + + /** + * Convert to a detector workspace in lambda. + * @param processingCommands : Commands to apply to crop and add spectra of the toConvert workspace. + * @param toConvert : TOF wavelength to convert. + * @param wavelengthMinMax : Wavelength minmax to keep. Crop out the rest. + * @param wavelengthStep : Wavelength step for rebinning + * @return Detector workspace in wavelength + */ + MatrixWorkspace_sptr ReflectometryWorkflowBase::toLamDetector( + const std::string& processingCommands, const MatrixWorkspace_sptr& toConvert, + const MinMax& wavelengthMinMax, const double& wavelengthStep) + { + // Process the input workspace according to the processingCommands to get a detector workspace + auto performIndexAlg = this->createChildAlgorithm("PerformIndexOperations"); + performIndexAlg->initialize(); + performIndexAlg->setProperty("ProcessingInstructions", processingCommands); + performIndexAlg->setProperty("InputWorkspace", toConvert); + performIndexAlg->execute(); + MatrixWorkspace_sptr detectorWS = performIndexAlg->getProperty("OutputWorkspace"); + + // Now convert units. Do this after the conjoining step otherwise the x bins will not match up. + auto convertUnitsAlg = this->createChildAlgorithm("ConvertUnits"); + convertUnitsAlg->initialize(); + convertUnitsAlg->setProperty("InputWorkspace", detectorWS); + convertUnitsAlg->setProperty("Target", "Wavelength"); + convertUnitsAlg->setProperty("AlignBins", true); + convertUnitsAlg->execute(); + detectorWS = convertUnitsAlg->getProperty("OutputWorkspace"); + + // Crop out the lambda x-ranges now that the workspace is in wavelength. + auto cropWorkspaceAlg = this->createChildAlgorithm("CropWorkspace"); + cropWorkspaceAlg->initialize(); + cropWorkspaceAlg->setProperty("InputWorkspace", detectorWS); + cropWorkspaceAlg->setProperty("XMin", wavelengthMinMax.get<0>()); + cropWorkspaceAlg->setProperty("XMax", wavelengthMinMax.get<1>()); + cropWorkspaceAlg->execute(); + detectorWS = cropWorkspaceAlg->getProperty("OutputWorkspace"); + + auto rebinWorkspaceAlg = this->createChildAlgorithm("Rebin"); + rebinWorkspaceAlg->initialize(); + std::vector params = boost::assign::list_of(wavelengthStep); + rebinWorkspaceAlg->setProperty("Params", params); + rebinWorkspaceAlg->setProperty("InputWorkspace", detectorWS); + rebinWorkspaceAlg->execute(); + detectorWS = rebinWorkspaceAlg->getProperty("OutputWorkspace"); + + return detectorWS; + } + + /** + * Convert From a TOF workspace into a detector and monitor workspace both in Lambda. + * @param toConvert: TOF workspace to convert + * @param processingCommands : Detector index ranges + * @param monitorIndex : Monitor index + * @param wavelengthMinMax : Wavelength min max for detector workspace + * @param backgroundMinMax : Wavelength min max for flat background correction of monitor workspace + * @param wavelengthStep : Wavlength step size for rebinning. + * @return Tuple of detector and monitor workspaces + */ + ReflectometryWorkflowBase::DetectorMonitorWorkspacePair ReflectometryWorkflowBase::toLam( + MatrixWorkspace_sptr toConvert, const std::string& processingCommands, + const int monitorIndex, const MinMax& wavelengthMinMax, const MinMax& backgroundMinMax, + const double& wavelengthStep) + { + // Detector Workspace Processing + MatrixWorkspace_sptr detectorWS = toLamDetector(processingCommands, toConvert, wavelengthMinMax, + wavelengthStep); + + // Monitor Workspace Processing + MatrixWorkspace_sptr monitorWS = toLamMonitor(toConvert, monitorIndex, backgroundMinMax); + + // Rebin the Monitor Workspace to match the Detector Workspace. + auto rebinToWorkspaceAlg = this->createChildAlgorithm("RebinToWorkspace"); + rebinToWorkspaceAlg->initialize(); + rebinToWorkspaceAlg->setProperty("WorkspaceToRebin", monitorWS); + rebinToWorkspaceAlg->setProperty("WorkspaceToMatch", detectorWS); + rebinToWorkspaceAlg->execute(); + monitorWS = rebinToWorkspaceAlg->getProperty("OutputWorkspace"); + + return DetectorMonitorWorkspacePair(detectorWS, monitorWS); + } + + } // namespace Algorithms +} // namespace Mantid diff --git a/Code/Mantid/Framework/Algorithms/src/SaveGSASInstrumentFile.cpp b/Code/Mantid/Framework/Algorithms/src/SaveGSASInstrumentFile.cpp index 51f32931f8e5..c82a79222160 100644 --- a/Code/Mantid/Framework/Algorithms/src/SaveGSASInstrumentFile.cpp +++ b/Code/Mantid/Framework/Algorithms/src/SaveGSASInstrumentFile.cpp @@ -542,7 +542,7 @@ ChopperConfiguration::ChopperConfiguration(const int freq, const std::string& ba //---------------------------------------------------------------------------------------------- /** Set up some constant by default * Output--> m_configuration - * @param chopperfrequency :: chopper frequency of the profile for. + * @param profmap :: map of parameters */ void SaveGSASInstrumentFile::initConstants(const map >& profmap) { diff --git a/Code/Mantid/Framework/Algorithms/src/SumEventsByLogValue.cpp b/Code/Mantid/Framework/Algorithms/src/SumEventsByLogValue.cpp index a11ad9e05818..0d8e20fa7a74 100644 --- a/Code/Mantid/Framework/Algorithms/src/SumEventsByLogValue.cpp +++ b/Code/Mantid/Framework/Algorithms/src/SumEventsByLogValue.cpp @@ -331,17 +331,22 @@ namespace Algorithms // Loop over the spectra - there will be one per monitor for ( std::size_t spec = 0; spec < monitorWorkspace->getNumberHistograms(); ++spec ) { - // Create a column for this monitor - const std::string monitorName = monitorWorkspace->getDetector(spec)->getName(); - auto monitorCounts = outputWorkspace->addColumn("int",monitorName); - const IEventList & eventList = monitorWorkspace->getEventList(spec); - // Accumulate things in a local vector before transferring to the table workspace - std::vector Y(xLength); - filterEventList(eventList, minVal, maxVal, log, Y); - // Transfer the results to the table - for ( int i = 0; i < xLength; ++i ) - { - monitorCounts->cell(i) = Y[i]; + try { + // Create a column for this monitor + const std::string monitorName = monitorWorkspace->getDetector(spec)->getName(); + auto monitorCounts = outputWorkspace->addColumn("int",monitorName); + const IEventList & eventList = monitorWorkspace->getEventList(spec); + // Accumulate things in a local vector before transferring to the table workspace + std::vector Y(xLength); + filterEventList(eventList, minVal, maxVal, log, Y); + // Transfer the results to the table + for ( int i = 0; i < xLength; ++i ) + { + monitorCounts->cell(i) = Y[i]; + } + } catch (Exception::NotFoundError&) { + // ADARA-generated nexus files have sometimes been seen to contain 'phantom' monitors that aren't in the IDF. + // This handles that by ignoring those spectra. } } } diff --git a/Code/Mantid/Framework/Algorithms/test/AddSampleLogTest.h b/Code/Mantid/Framework/Algorithms/test/AddSampleLogTest.h index 33b33a2c45bc..59b1298df8a2 100644 --- a/Code/Mantid/Framework/Algorithms/test/AddSampleLogTest.h +++ b/Code/Mantid/Framework/Algorithms/test/AddSampleLogTest.h @@ -42,6 +42,9 @@ class AddSampleLogTest : public CxxTest::TestSuite MatrixWorkspace_sptr ws = WorkspaceCreationHelper::Create2DWorkspace(10,10); ExecuteAlgorithm(ws, "My Name", "Number", "1.234", 1.234); ExecuteAlgorithm(ws, "My Name", "Number", "2.456", 2.456); + + ExecuteAlgorithm(ws, "My Name", "Number", "-987654321", -987654321); + ExecuteAlgorithm(ws, "My Name", "Number", "963", 963); } void test_BadNumber() @@ -59,12 +62,18 @@ class AddSampleLogTest : public CxxTest::TestSuite void test_NumberSeries() { MatrixWorkspace_sptr ws = WorkspaceCreationHelper::Create2DWorkspace(10,10); + ws->mutableRun().setStartAndEndTime(DateAndTime("2013-12-18T13:40:00"),DateAndTime("2013-12-18T13:42:00")); ExecuteAlgorithm(ws, "My Name", "Number Series", "1.234", 1.234); ExecuteAlgorithm(ws, "My Name", "Number Series", "2.456", 2.456); + + ExecuteAlgorithm(ws, "My Name", "Number Series", "-1", -1); + ExecuteAlgorithm(ws, "Another Name", "Number Series", "0", 0); + ExecuteAlgorithm(ws, "Another Name", "Number Series", "123456789", 123456789); } + template void ExecuteAlgorithm(MatrixWorkspace_sptr testWS, std::string LogName, std::string LogType, std::string LogText, - double expectedValue, bool fails=false) + T expectedValue, bool fails=false) { //add the workspace to the ADS AnalysisDataService::Instance().addOrReplace("AddSampleLogTest_Temporary", testWS); @@ -103,14 +112,15 @@ class AddSampleLogTest : public CxxTest::TestSuite } else if (LogType == "Number") { - PropertyWithValue *testProp = dynamic_cast*>(prop); + auto testProp = dynamic_cast*>(prop); TS_ASSERT(testProp); TS_ASSERT_DELTA((*testProp)(), expectedValue, 1e-5); } else if (LogType == "Number Series") { - TimeSeriesProperty *testProp = dynamic_cast*>(prop); + auto testProp = dynamic_cast*>(prop); TS_ASSERT(testProp); + TS_ASSERT_EQUALS(testProp->firstTime(),DateAndTime("2013-12-18T13:40:00")); TS_ASSERT_DELTA(testProp->firstValue(), expectedValue, 1e-5); } diff --git a/Code/Mantid/Framework/Algorithms/test/FindPeaksTest.h b/Code/Mantid/Framework/Algorithms/test/FindPeaksTest.h index e20c5160d407..f7624a3cb520 100644 --- a/Code/Mantid/Framework/Algorithms/test/FindPeaksTest.h +++ b/Code/Mantid/Framework/Algorithms/test/FindPeaksTest.h @@ -6,6 +6,7 @@ #include "MantidAlgorithms/FindPeaks.h" +#include "MantidAPI/FrameworkManager.h" #include "MantidAPI/MatrixWorkspace.h" #include "MantidDataHandling/LoadNexusProcessed.h" #include "MantidDataHandling/LoadInstrument.h" @@ -20,6 +21,14 @@ using namespace Mantid::API; class FindPeaksTest : public CxxTest::TestSuite { public: + static FindPeaksTest *createSuite() { return new FindPeaksTest(); } + static void destroySuite( FindPeaksTest *suite ) { delete suite; } + + FindPeaksTest() + { + FrameworkManager::Instance(); + } + void testTheBasics() { FindPeaks finder; diff --git a/Code/Mantid/Framework/Algorithms/test/GeneratePythonScriptTest.h b/Code/Mantid/Framework/Algorithms/test/GeneratePythonScriptTest.h index a7e49bfdbcf7..2ba2962bc205 100644 --- a/Code/Mantid/Framework/Algorithms/test/GeneratePythonScriptTest.h +++ b/Code/Mantid/Framework/Algorithms/test/GeneratePythonScriptTest.h @@ -13,12 +13,39 @@ #include "MantidAlgorithms/CropWorkspace.h" #include "MantidAlgorithms/Power.h" #include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/FrameworkManager.h" #include using namespace Mantid; using namespace Mantid::Algorithms; using namespace Mantid::API; +class NonExistingAlgorithm : public Algorithm +{ + +public: + virtual const std::string name() const { return "NonExistingAlgorithm";} + /// Algorithm's version for identification overriding a virtual method + virtual int version() const { return 1;} + /// Algorithm's category for identification overriding a virtual method + virtual const std::string category() const { return "Rubbish";} + + + void init() + { + declareProperty( new WorkspaceProperty("InputWorkspace","",Kernel::Direction::Input), + "A workspace with units of TOF" ); + declareProperty( new WorkspaceProperty("OutputWorkspace","",Kernel::Direction::Output), + "The name to use for the output workspace" ); + declareProperty("MissingProperty","rubbish",Kernel::Direction::Input); + + }; + void exec() + { + + }; +}; + class GeneratePythonScriptTest : public CxxTest::TestSuite { public: @@ -39,34 +66,43 @@ class GeneratePythonScriptTest : public CxxTest::TestSuite "######################################################################", "#Python Script Generated by GeneratePythonScript Algorithm", "######################################################################", + "ERROR: MISSING ALGORITHM: NonExistingAlgorithm with parameters Algorithm: NonExistingAlgorithm v1", + " Parameters:", + " Name: InputWorkspace, Value: , Default?: Yes, Direction: Input", + " Name: OutputWorkspace, Value: , Default?: Yes, Direction: Output", + " Name: MissingProperty, Value: rubbish, Default?: Yes, Direction: Input", + "", "CreateWorkspace(OutputWorkspace='testGeneratePython',DataX='1,2,3,5,6',DataY='7,9,16,4,3',DataE='2,3,4,2,1',WorkspaceTitle='Test Workspace')", "CropWorkspace(InputWorkspace='testGeneratePython',OutputWorkspace='testGeneratePython',XMin='2',XMax='5')", "Power(InputWorkspace='testGeneratePython',OutputWorkspace='testGeneratePython',Exponent='1.5')", "" }; - - // Set up and execute the algorithm. GeneratePythonScript alg; TS_ASSERT_THROWS_NOTHING( alg.initialize() ); TS_ASSERT( alg.isInitialized() ); TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("InputWorkspace", workspaceName) ); TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", "GeneratePythonScriptTest.py") ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("ScriptText", "") ); TS_ASSERT_THROWS_NOTHING( alg.execute(); ); TS_ASSERT( alg.isExecuted() ); - // Compare the contents of the file to the expected result line-by-line. std::string filename = alg.getProperty("Filename"); std::ifstream file(filename.c_str(), std::ifstream::in); std::string scriptLine; int lineCount(0); + while(std::getline(file, scriptLine)) { TS_ASSERT_EQUALS(scriptLine,result[lineCount]); lineCount++; } + // Verify that if we set the content of ScriptText that it is set correctly. + alg.setPropertyValue("ScriptText", result[10]); + TS_ASSERT_EQUALS(alg.getPropertyValue("ScriptText"), "CropWorkspace(InputWorkspace='testGeneratePython',OutputWorkspace='testGeneratePython',XMin='2',XMax='5')"); + file.close(); if (Poco::File(filename).exists()) Poco::File(filename).remove(); } @@ -77,6 +113,7 @@ class GeneratePythonScriptTest : public CxxTest::TestSuite Mantid::Algorithms::CropWorkspace cropper; Mantid::Algorithms::Power powerer; + // Set up and execute creation of the workspace creator.initialize(); creator.setPropertyValue("OutputWorkspace",wsName); @@ -107,6 +144,16 @@ class GeneratePythonScriptTest : public CxxTest::TestSuite TS_ASSERT_THROWS_NOTHING(powerer.execute()); TS_ASSERT_EQUALS(powerer.isExecuted(), true); + // set up history for the algorithn which is presumably removed from Mantid + auto ws = API::FrameworkManager::Instance().getWorkspace(wsName); + API::WorkspaceHistory &history = ws->history(); + auto pAlg = std::auto_ptr(new NonExistingAlgorithm()); + pAlg->initialize(); + history.addHistory(API::AlgorithmHistory(pAlg.get())); + + pAlg.reset(NULL); + + } }; diff --git a/Code/Mantid/Framework/Algorithms/test/MuonGroupDetectorsTest.h b/Code/Mantid/Framework/Algorithms/test/MuonGroupDetectorsTest.h new file mode 100644 index 000000000000..2603c3905712 --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/test/MuonGroupDetectorsTest.h @@ -0,0 +1,113 @@ +#ifndef MANTID_ALGORITHMS_MUONGROUPDETECTORSTEST_H_ +#define MANTID_ALGORITHMS_MUONGROUPDETECTORSTEST_H_ + +#include + +#include "MantidAPI/TableRow.h" +#include "MantidAlgorithms/MuonGroupDetectors.h" +#include "MantidDataObjects/TableWorkspace.h" +#include "MantidTestHelpers/WorkspaceCreationHelper.h" + +using Mantid::Algorithms::MuonGroupDetectors; + +using namespace Mantid; +using namespace Mantid::Kernel; +using namespace Mantid::API; +using namespace Mantid::DataObjects; + +class MuonGroupDetectorsTest : public CxxTest::TestSuite +{ +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static MuonGroupDetectorsTest *createSuite() { return new MuonGroupDetectorsTest(); } + static void destroySuite( MuonGroupDetectorsTest *suite ) { delete suite; } + + + void test_Init() + { + MuonGroupDetectors alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + } + + void test_exec() + { + // Name of the output workspace. + const std::string outWSName("MuonGroupDetectorsTest_OutputWS"); + + MatrixWorkspace_sptr inWS = WorkspaceCreationHelper::Create2DWorkspace123(5,3); + + for ( size_t i = 0; i < inWS->getNumberHistograms(); ++i ) + inWS->getSpectrum(i)->setDetectorID( static_cast(i + 1) ); // To be consistent with how LoadMuonNexus works + + TableWorkspace_sptr grouping = createDetectorGroupingTable(); + + MuonGroupDetectors alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + TS_ASSERT_THROWS_NOTHING( alg.setProperty("InputWorkspace", inWS) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("DetectorGroupingTable", grouping) ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWSName) ); + TS_ASSERT_THROWS_NOTHING( alg.execute(); ); + TS_ASSERT( alg.isExecuted() ); + + MatrixWorkspace_sptr ws; + TS_ASSERT_THROWS_NOTHING( ws = AnalysisDataService::Instance().retrieveWS(outWSName) ); + TS_ASSERT(ws); + + if ( ws ) + { + TS_ASSERT_EQUALS( ws->getNumberHistograms(), 2); + TS_ASSERT_EQUALS( ws->blocksize(), 3 ); + + TS_ASSERT_EQUALS( ws->readY(0)[0], 4 ); + TS_ASSERT_EQUALS( ws->readY(1)[0], 6 ); + + TS_ASSERT_EQUALS( ws->readX(0)[1], 1 ); + TS_ASSERT_EQUALS( ws->readX(1)[1], 1 ); + + TS_ASSERT_DELTA( ws->readE(0)[2], 4.243, 0.001); + TS_ASSERT_DELTA( ws->readE(1)[2], 5.196, 0.001); + + TS_ASSERT_EQUALS( ws->getSpectrum(0)->getSpectrumNo(), 1); + TS_ASSERT_EQUALS( ws->getSpectrum(1)->getSpectrumNo(), 2); + + std::set d1; + d1.insert(1); d1.insert(2); + TS_ASSERT_EQUALS( ws->getSpectrum(0)->getDetectorIDs(), d1 ); + + std::set d2; + d2.insert(3); d2.insert(4); d2.insert(5); + TS_ASSERT_EQUALS( ws->getSpectrum(1)->getDetectorIDs(), d2 ); + } + + // Remove workspace from the data service. + AnalysisDataService::Instance().remove(outWSName); + } + +private: + + TableWorkspace_sptr createDetectorGroupingTable() + { + auto t = boost::make_shared(); + + t->addColumn("vector_int", "Detectors"); + + std::vector group1; + group1.push_back(1); group1.push_back(2); + TableRow row1 = t->appendRow(); + row1 << group1; + + std::vector group2; + group2.push_back(3); group2.push_back(4); group2.push_back(5); + TableRow row2 = t->appendRow(); + row2 << group2; + + return t; + } + +}; + + +#endif /* MANTID_ALGORITHMS_MUONGROUPDETECTORSTEST_H_ */ diff --git a/Code/Mantid/Framework/Algorithms/test/PerformIndexOperationsTest.h b/Code/Mantid/Framework/Algorithms/test/PerformIndexOperationsTest.h new file mode 100644 index 000000000000..2aaa9c38208a --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/test/PerformIndexOperationsTest.h @@ -0,0 +1,155 @@ +#ifndef MANTID_ALGORITHMS_PERFORMINDEXOPERATIONSTEST_H_ +#define MANTID_ALGORITHMS_PERFORMINDEXOPERATIONSTEST_H_ + +#include +#include "MantidAlgorithms/PerformIndexOperations.h" +#include "MantidAPI/AlgorithmManager.h" + +using Mantid::Algorithms::PerformIndexOperations; +using namespace Mantid::API; + +MatrixWorkspace_const_sptr doExecute(MatrixWorkspace_sptr inWS, + const std::string& processingInstructions) +{ + // Name of the output workspace. + std::string outWSName("PerformIndexOperationsTest_OutputWS"); + + PerformIndexOperations alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize()) + alg.setRethrows(true); + TS_ASSERT( alg.isInitialized()) + TS_ASSERT_THROWS_NOTHING( alg.setProperty("InputWorkspace", inWS)); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("ProcessingInstructions", processingInstructions)); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWSName)); + alg.execute(); + + MatrixWorkspace_sptr ws; + TS_ASSERT_THROWS_NOTHING( ws = AnalysisDataService::Instance().retrieveWS(outWSName)); + TS_ASSERT(ws); + return ws; +} + +class PerformIndexOperationsTest: public CxxTest::TestSuite +{ +private: + + MatrixWorkspace_sptr m_testWS; + +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static PerformIndexOperationsTest *createSuite() + { + return new PerformIndexOperationsTest(); + } + static void destroySuite(PerformIndexOperationsTest *suite) + { + delete suite; + } + + PerformIndexOperationsTest() + { + auto createAlg = AlgorithmManager::Instance().create("CreateWorkspace"); + createAlg->setChild(true); + createAlg->initialize(); + createAlg->setPropertyValue("DataY", "1.0, 1.1, 1.2, 1.3, 1.4"); + createAlg->setPropertyValue("DataX", "0, 1"); + createAlg->setProperty("NSpec", 5); + createAlg->setPropertyValue("OutputWorkspace", "out_ws"); + createAlg->execute(); + m_testWS = createAlg->getProperty("OutputWorkspace"); + } + + void test_Init() + { + PerformIndexOperations alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize()) + TS_ASSERT( alg.isInitialized()) + } + + void test_do_nothing() + { + auto outWS = doExecute(m_testWS, ""); + TS_ASSERT_EQUALS(outWS->getNumberHistograms(), m_testWS->getNumberHistograms()); + } + + void test_throw_if_bad_regex() + { + TSM_ASSERT_THROWS("Not a workspace index", doExecute(m_testWS, "x"), std::invalid_argument&); + TSM_ASSERT_THROWS("Not a positive index", doExecute(m_testWS, "-1"), std::invalid_argument&); + TSM_ASSERT_THROWS("One negative, one positive index", doExecute(m_testWS, "-1,1"), std::invalid_argument&); + TSM_ASSERT_THROWS("Invalid separator", doExecute(m_testWS, "1@2"), std::invalid_argument&); + TSM_ASSERT_THROWS("Dangling end separator", doExecute(m_testWS, "1,2,"), std::invalid_argument&); + TSM_ASSERT_THROWS("Test non-integer index", doExecute(m_testWS, "1.0"), std::invalid_argument&); + } + + + void test_simple_crop() + { + auto outWS = doExecute(m_testWS, "0:2"); // Crop off the last spectra + TS_ASSERT_EQUALS(3, outWS->getNumberHistograms()); + + TS_ASSERT_EQUALS(1.0, outWS->readY(0)[0]) + TS_ASSERT_EQUALS(1.1, outWS->readY(1)[0]) + TS_ASSERT_EQUALS(1.2, outWS->readY(2)[0]) + } + + void test_split_crop() // Crop out workspace index 2 + { + auto outWS = doExecute(m_testWS, "0:1,3:4"); // Crop off the middle spectra only + TS_ASSERT_EQUALS(4, outWS->getNumberHistograms()); + + TS_ASSERT_EQUALS(1.0, outWS->readY(0)[0]) + TS_ASSERT_EQUALS(1.1, outWS->readY(1)[0]) + TS_ASSERT_EQUALS(1.3, outWS->readY(2)[0]) + TS_ASSERT_EQUALS(1.4, outWS->readY(3)[0]) + } + + void test_add_spectra() + { + auto outWS = doExecute(m_testWS, "0+1"); + TS_ASSERT_EQUALS(1, outWS->getNumberHistograms()); + TS_ASSERT_EQUALS(1.0 + 1.1, outWS->readY(0)[0]) + } + + void test_add_spectra_that_are_not_neighbours() + { + auto outWS = doExecute(m_testWS, "0+4"); + TS_ASSERT_EQUALS(1, outWS->getNumberHistograms()); + TS_ASSERT_EQUALS(1.0 + 1.4, outWS->readY(0)[0]) + } + + void test_add_spectra_range() + { + auto outWS = doExecute(m_testWS, "0-2"); // Sum first and second spectra. Remove the rest. + TS_ASSERT_EQUALS(1, outWS->getNumberHistograms()); + + TS_ASSERT_EQUALS(1.0 + 1.1 + 1.2, outWS->readY(0)[0]) + } + + void test_combine_and_crop_ranges() + { + auto outWS = doExecute(m_testWS, "0-1,2,3,4"); // + TS_ASSERT_EQUALS(4, outWS->getNumberHistograms()); + + TS_ASSERT_EQUALS(1.0 + 1.1, outWS->readY(0)[0]) + TS_ASSERT_EQUALS(1.2, outWS->readY(1)[0]) + TS_ASSERT_EQUALS(1.3, outWS->readY(2)[0]) + TS_ASSERT_EQUALS(1.4, outWS->readY(3)[0]) + } + + void test_complex_schenario() + { + auto outWS = doExecute(m_testWS, "0:1,2-3"); // + TS_ASSERT_EQUALS(3, outWS->getNumberHistograms()); + + TS_ASSERT_EQUALS(1.0, outWS->readY(0)[0]) + TS_ASSERT_EQUALS(1.1, outWS->readY(1)[0]) + TS_ASSERT_EQUALS(1.2+1.3, outWS->readY(2)[0]) + } + + + +}; + +#endif /* MANTID_ALGORITHMS_PERFORMINDEXOPERATIONSTEST_H_ */ diff --git a/Code/Mantid/Framework/Algorithms/test/ReflectometryReductionOneTest.h b/Code/Mantid/Framework/Algorithms/test/ReflectometryReductionOneTest.h index 6e52ae734da0..83c9f6e4c995 100644 --- a/Code/Mantid/Framework/Algorithms/test/ReflectometryReductionOneTest.h +++ b/Code/Mantid/Framework/Algorithms/test/ReflectometryReductionOneTest.h @@ -9,7 +9,7 @@ using namespace Mantid; using namespace Mantid::Kernel; using namespace Mantid::API; -using Mantid::Algorithms::ReflectometryReductionOne; +using namespace Mantid::Algorithms; class ReflectometryReductionOneTest: public CxxTest::TestSuite { @@ -35,10 +35,12 @@ class ReflectometryReductionOneTest: public CxxTest::TestSuite // Define one spectra to keep detectorIndexRange.push_back(static_cast(workspaceIndexToKeep1)); - detectorIndexRange.push_back(static_cast(workspaceIndexToKeep1)); // Define another spectra to keep detectorIndexRange.push_back(static_cast(workspaceIndexToKeep2)); - detectorIndexRange.push_back(static_cast(workspaceIndexToKeep2)); + std::stringstream buffer; + buffer << workspaceIndexToKeep1 << "," << workspaceIndexToKeep2; + const std::string detectorIndexRangesStr = buffer.str(); + // Define a wavelength range for the detector workspace const double wavelengthMin = 1.0; const double wavelengthMax = 15; @@ -49,8 +51,8 @@ class ReflectometryReductionOneTest: public CxxTest::TestSuite ReflectometryReductionOne alg; // Run the conversion. - ReflectometryReductionOne::DetectorMonitorWorkspacePair inLam = alg.toLam(toConvert, - detectorIndexRange, monitorIndex, boost::tuple(wavelengthMin, wavelengthMax), + ReflectometryWorkflowBase::DetectorMonitorWorkspacePair inLam = alg.toLam(toConvert, + detectorIndexRangesStr, monitorIndex, boost::tuple(wavelengthMin, wavelengthMax), boost::tuple(backgroundWavelengthMin, backgroundWavelengthMax), wavelengthStep); // Unpack the results @@ -90,31 +92,6 @@ class ReflectometryReductionOneTest: public CxxTest::TestSuite AnalysisDataService::Instance().remove(toConvert->getName()); } - void testIvsQ() - { - auto loadAlg = AlgorithmManager::Instance().create("Load"); - loadAlg->initialize(); - loadAlg->setProperty("Filename", "INTER13460_IvsLam.nxs"); - loadAlg->setPropertyValue("OutputWorkspace", "demo"); - loadAlg->execute(); - - MatrixWorkspace_sptr toConvert = AnalysisDataService::Instance().retrieveWS("demo"); - - ReflectometryReductionOne alg; - - auto instrument = toConvert->getInstrument(); - auto detector = boost::dynamic_pointer_cast( instrument->getComponentByName("point-detector") ); - auto sample = instrument->getComponentByName("some-surface-holder"); - - boost::optional theta = 0.7; - - MatrixWorkspace_const_sptr inQ = alg.toIvsQ(toConvert, true /*correct position*/, - true /*is point detector*/, theta, sample, detector); - - TS_ASSERT_EQUALS("MomentumTransfer", inQ->getAxis(0)->unit()->unitID()); - - AnalysisDataService::Instance().remove(toConvert->getName()); - } }; diff --git a/Code/Mantid/Framework/Crystal/CMakeLists.txt b/Code/Mantid/Framework/Crystal/CMakeLists.txt index 29cb8bcb91f2..063ce847ef0b 100644 --- a/Code/Mantid/Framework/Crystal/CMakeLists.txt +++ b/Code/Mantid/Framework/Crystal/CMakeLists.txt @@ -18,6 +18,7 @@ set ( SRC_FILES src/IndexPeaks.cpp src/IndexSXPeaks.cpp src/IntegratePeakTimeSlices.cpp + src/LatticeErrors.cpp src/LoadHKL.cpp src/LoadIsawPeaks.cpp src/LoadIsawUB.cpp @@ -74,6 +75,7 @@ set ( INC_FILES inc/MantidCrystal/IndexPeaks.h inc/MantidCrystal/IndexSXPeaks.h inc/MantidCrystal/IntegratePeakTimeSlices.h + inc/MantidCrystal/LatticeErrors.h inc/MantidCrystal/LoadHKL.h inc/MantidCrystal/LoadIsawPeaks.h inc/MantidCrystal/LoadIsawUB.h diff --git a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/LatticeErrors.h b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/LatticeErrors.h new file mode 100644 index 000000000000..8c95b7db0972 --- /dev/null +++ b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/LatticeErrors.h @@ -0,0 +1,97 @@ +/* + * LatticeErrors.h + * + * Created on: Jan 26, 2013 + * Author: ruth + */ + +#ifndef PEAKHKLERRORS_H_ +#define PEAKHKLERRORS_H_ +#include "MantidKernel/System.h" + +#include "MantidAPI/IFunction.h" +#include "MantidGeometry/Instrument.h" +#include "MantidAPI/ParamFunction.h" +#include "MantidAPI/IFunction1D.h" +#include "MantidDataObjects/PeaksWorkspace.h" +#include "MantidAPI/IFunction.h" +#include "MantidGeometry/Crystal/OrientedLattice.h" +#include "MantidKernel/Matrix.h" +using Mantid::API::IFunction; +using Mantid::Geometry::Instrument; +using Mantid::DataObjects::PeaksWorkspace_sptr; + +namespace Mantid +{ + namespace Crystal + { + + /** + + @author Ruth Mikkelson, SNS,ORNL + @date 01/26/2013 + Copyright © 2012 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: + Code Documentation is available at: + */ + class DLLExport LatticeErrors: public API::ParamFunction, public API::IFunction1D + { + public: + LatticeErrors(); + virtual ~LatticeErrors(); + + std::string name() const + { + return std::string("LatticeErrors"); + } + ; + + virtual int version() const + { + return 1; + } + ; + + const std::string category() const + { + return "Crystal"; + } + ; + + void function1D(double *out, const double *xValues, const size_t nData) const; + + //void functionDeriv1D(Mantid::API::Jacobian* out, const double *xValues, const size_t nData); + + void functionDerivLocal(API::Jacobian* , const double* , const size_t ); + void functionDeriv(const API::FunctionDomain& domain, API::Jacobian& jacobian); + + + void init(); + + + + private: + + static Kernel::Logger& g_log ; + + }; + } +} + +#endif /* PEAKHKLERRORS_H_ */ diff --git a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/LoadIsawPeaks.h b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/LoadIsawPeaks.h index e7100e0ede94..79a4db870cf5 100644 --- a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/LoadIsawPeaks.h +++ b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/LoadIsawPeaks.h @@ -31,6 +31,7 @@ namespace Crystal /// Returns a confidence value that this algorithm can load a file virtual int confidence(Kernel::FileDescriptor & descriptor) const; + int findPixelID(Geometry::Instrument_const_sptr inst, std::string bankName, int col, int row); private: /// Sets documentation strings for this algorithm diff --git a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/OptimizeLatticeForCellType.h b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/OptimizeLatticeForCellType.h index 4f91ad5f4263..4231d49d0115 100644 --- a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/OptimizeLatticeForCellType.h +++ b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/OptimizeLatticeForCellType.h @@ -54,7 +54,8 @@ class DLLExport OptimizeLatticeForCellType: public API::Algorithm /// Algorithm's category for identification overriding a virtual method virtual const std::string category() const { return "Crystal"; } /// Call TOFLattice as a Child Algorithm to get statistics of the peaks - double optLattice(std::string inname, std::string cell_type, std::vector & params); + double optLatticeSum(std::string inname, std::string cell_type, std::vector & params); + void optLattice(std::string inname, std::vector & params, double *out); private: /// Sets documentation strings for this algorithm @@ -64,7 +65,12 @@ class DLLExport OptimizeLatticeForCellType: public API::Algorithm void exec(); /// Static reference to the logger class static Kernel::Logger& g_log; - + /// Function to find peaks near detector edge + bool edgePixel(DataObjects::PeaksWorkspace_sptr ws, std::string bankName, int col, int row, int Edge); + Kernel::DblMatrix aMatrix( std::vector lattice ); + void calculateErrors(size_t npeaks, std::string inname, std::string cell_type, + std::vector & Params, + std::vector & sigabc, double chisq); }; } // namespace Algorithm diff --git a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SCDCalibratePanels.h b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SCDCalibratePanels.h index dfbfbdd71c5e..b19226ffe85a 100644 --- a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SCDCalibratePanels.h +++ b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SCDCalibratePanels.h @@ -281,7 +281,7 @@ namespace Crystal * @param ws The workspace with the predicted qx,qy, and qz values for each * peak * - * @param nGroups The number of Groups-Sets of panels + * @param NGroups The number of Groups-Sets of panels * @param names The names of the variables that have been fit * * @param params The values of the variables that have been fit @@ -297,7 +297,7 @@ namespace Crystal * @param nData The number of xVals and out values */ void CreateFxnGetValues(DataObjects::Workspace2D_sptr const ws, - int const nGroups, std::vector const names, + int const NGroups, std::vector const names, std::vector const params, std::string const BankNameString, double *out, const double *xVals,const size_t nData) const; diff --git a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SaveHKL.h b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SaveHKL.h index 1e53922dd093..bf68c723f5a7 100644 --- a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SaveHKL.h +++ b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SaveHKL.h @@ -3,6 +3,7 @@ #include "MantidKernel/System.h" #include "MantidAPI/Algorithm.h" +#include "MantidDataObjects/PeaksWorkspace.h" namespace Mantid { @@ -59,7 +60,8 @@ namespace Crystal double radius; // in cm double power_th; double spectrumCalc(double TOF, int iSpec,std::vector > time, std::vector > spectra, size_t id); - + DataObjects::PeaksWorkspace_sptr ws; + void sizeBanks(std::string bankName, int& nCols, int& nRows); }; diff --git a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SaveIsawPeaks.h b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SaveIsawPeaks.h index ebf119811442..a591ab07cba2 100644 --- a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SaveIsawPeaks.h +++ b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/SaveIsawPeaks.h @@ -34,7 +34,10 @@ namespace Crystal void init(); /// Run the algorithm void exec(); - + /// find position for rectangular and non-rectangular + Kernel::V3D findPixelPos(std::string bankName, int col, int row); + void sizeBanks(std::string bankName, int& NCOLS, int& NROWS, double& xsize, double& ysize); + Geometry::Instrument_const_sptr inst; }; diff --git a/Code/Mantid/Framework/Crystal/src/IntegratePeakTimeSlices.cpp b/Code/Mantid/Framework/Crystal/src/IntegratePeakTimeSlices.cpp index 6178c48f73ac..5ec16550e800 100644 --- a/Code/Mantid/Framework/Crystal/src/IntegratePeakTimeSlices.cpp +++ b/Code/Mantid/Framework/Crystal/src/IntegratePeakTimeSlices.cpp @@ -801,10 +801,10 @@ namespace Mantid /** * Checks and updates if needed the list of NeighborIDs * @param comp Component with the neighboring pixels - * @CentPos new Center + * @param CentPos new Center * @param oldCenter old Center * @param NewRadius new Radius - * ¶m neighborRadius old the new neighborhood radius + * @param neighborRadius old the new neighborhood radius */ bool IntegratePeakTimeSlices::updateNeighbors( boost::shared_ptr< Geometry::IComponent> &comp, V3D CentPos, V3D oldCenter,double NewRadius, double &neighborRadius) @@ -1549,15 +1549,17 @@ namespace Mantid } /** * Initial phase at converting Detector data to workspace data that will be sent to the Fit Function, BivariateNormal. - * @param Data workspace that will be sent to the Fit Function, BivariateNormal + * @param Data -workspace that will be sent to the Fit Function, BivariateNormal * @param inpWkSpace -The MatrixWorkspace with all the experimental data - * @param comp -The parent of all the pixels that are neighbors of the peak being considered + * @param comp -The parent of all the pixels that are neighbors of the peak being considered * @param chanMin -The minimum channel to use - * @param chanMax-The maximum channel to be collapsed to one channel - * @param CentNghbr- The center of the current and next(if changed) neighbor pixels - * @param neighborRadius- The radius of the current neighbor pixels - * @param Radius The current starting Radius to use for neighbors. - * @param spec_idList-The list of spectral id's that are neighbors + * @param chanMax -The maximum channel to be collapsed to one channel + * @param CentX -The current peak row + * @param CentY -The current peak column + * @param CentNghbr -The center of the current and next(if changed) neighbor pixels + * @param neighborRadius -The radius of the current neighbor pixels + * @param Radius -The current starting Radius to use for neighbors. + * @param spec_idList -The list of spectral id's that are neighbors * */ void IntegratePeakTimeSlices::SetUpData( MatrixWorkspace_sptr & Data, @@ -1987,12 +1989,12 @@ namespace Mantid * Utility method to calculate variances from data given background and means * * @param background - the background to use - * @param meanx- the mean x( col) value to use - * @param meany- the mean y( row) value to use - * @param Varxx-The resultant variance in the x values around above mean with background removed - * @param Varyy-The resultant variance in the y values around above mean with background removed - * @param Varxy-The resultant covariance in the x and values around above mean with background removed - * @param StatBase- The "data". + * @param meanx - the mean x( col) value to use + * @param meany - the mean y( row) value to use + * @param Varxx - The resultant variance in the x values around above mean with background removed + * @param Varyy - The resultant variance in the y values around above mean with background removed + * @param Varxy - The resultant covariance in the x and values around above mean with background removed + * @param StatBase - The "data". */ void DataModeHandler::CalcVariancesFromData( double background, double meanx, double meany, double &Varxx, double &Varxy, double &Varyy, @@ -2026,6 +2028,7 @@ namespace Mantid /** * Calculates the string form of the constraints to be sent to the Fit Algorithm and also saves it in a vector form * @param Bounds The vector form for the constraints + * @param CalcVariances The to trigger variance calculation */ std::string DataModeHandler::CalcConstraints( std::vector< std::pair > & Bounds, bool CalcVariances) @@ -2127,7 +2130,7 @@ namespace Mantid * @param errs The parameter errorfrom the Fit Algorithm * @param lastRow The previous row( for log info only) * @param lastCol The previous col( for log info only) - * @param neighborhoodRadius The neighborhood radius( for log info only) + * @param neighborRadius The neighborhood radius( for log info only) */ void IntegratePeakTimeSlices::Fit(MatrixWorkspace_sptr &Data,double &chisqOverDOF, bool &done, std::vector&names, std::vector¶ms, @@ -2253,7 +2256,7 @@ namespace Mantid * @param errs The parameter errorfrom the Fit Algorithm * @param lastRow The previous row( for log info only) * @param lastCol The previous col( for log info only) - * @param neighborhoodRadius The neighborhood radius( for log info only) + * @param neighborRadius The neighborhood radius( for log info only) */ void IntegratePeakTimeSlices::PreFit(MatrixWorkspace_sptr &Data,double &chisqOverDOF, bool &done, std::vector&names, std::vector¶ms, @@ -2469,7 +2472,8 @@ namespace Mantid /** * Calculates if there is enough data to for there to be a peak * - * @param true if there is enough data, otherwise false + * @param ParameterValues pointer to the parameter values + * @return true if there is enough data, otherwise false */ bool DataModeHandler::IsEnoughData( const double *ParameterValues, Kernel::Logger& ) { @@ -2515,10 +2519,10 @@ namespace Mantid * Calculates the error in integration closest to the latest ISAW calculations * * @param background The background - * @param backError The error in this background value - * @param ChiSqOverDOF The fitting error - * @param TotVariance The Total square of the intensity errors in all the cells considered - * @param ncell The number of cells + * @param backError The error in this background value + * @param ChiSqOverDOF The fitting error + * @param TotVariance The Total square of the intensity errors in all the cells considered + * @param ncells The number of cells * * @return The error in the integrated intensity */ @@ -2728,7 +2732,7 @@ namespace Mantid /** * Calculates the Intensity designed for Edge Peaks * - * @param The Fitted parameters + * @param params The Fitted parameters * * @return this calculated intensity * @@ -2752,7 +2756,7 @@ namespace Mantid /** * Calculates the Error in the Intensity designed for Edge Peaks * - * @param The fitted parameter values + * @param params The fitted parameter values * @param errs The error in these fitted parameters * @param chiSqOvDOF The fitting error * diff --git a/Code/Mantid/Framework/Crystal/src/LatticeErrors.cpp b/Code/Mantid/Framework/Crystal/src/LatticeErrors.cpp new file mode 100644 index 000000000000..6d0510b30fb2 --- /dev/null +++ b/Code/Mantid/Framework/Crystal/src/LatticeErrors.cpp @@ -0,0 +1,111 @@ +/*WIKI* + + +*WIKI*/ + +/* + * LatticeErrors.cpp + * + + */ +#include "MantidAPI/FunctionFactory.h" +#include "MantidAPI/IConstraint.h" +#include "MantidAPI/IFunction1D.h" +#include "MantidAPI/IPeak.h" +#include "MantidAPI/ParamFunction.h" +#include "MantidCrystal/LatticeErrors.h" +#include "MantidCrystal/SCDPanelErrors.h" +#include "MantidDataObjects/Peak.h" +#include "MantidDataObjects/PeaksWorkspace.h" +#include "MantidGeometry/IComponent.h" +#include "MantidGeometry/Instrument/CompAssembly.h" +#include "MantidGeometry/Instrument/Goniometer.h" +#include "MantidGeometry/Instrument/ParameterMap.h" +#include "MantidGeometry/IObjComponent.h" +#include "MantidCrystal/OptimizeLatticeForCellType.h" +#include "MantidKernel/Matrix.h" +#include "MantidKernel/Quat.h" +#include "MantidKernel/V3D.h" +#include +#include + +using namespace Mantid::DataObjects; +using namespace Mantid::API; +using namespace Mantid::Kernel; +using Mantid::Geometry::CompAssembly; +using Mantid::Geometry::IObjComponent_const_sptr; + +namespace Mantid +{ + + namespace Crystal + { + + Kernel::Logger& LatticeErrors::g_log = Kernel::Logger::get("LatticeErrors"); + + DECLARE_FUNCTION( LatticeErrors ) + + LatticeErrors::LatticeErrors():ParamFunction(), IFunction1D() + { + } + + + LatticeErrors:: ~LatticeErrors() + { + + } + + void LatticeErrors::init() + { + declareParameter( "p0", 0.0, "a lattice param" ); + declareParameter( "p1", 0.0, "b lattice param" ); + declareParameter( "p2", 0.0, "c lattice param" ); + declareParameter( "p3",0.0,"alpha lattice param"); + declareParameter( "p4",0.0,"beta lattice param"); + declareParameter( "p5",0.0,"gamma lattice param"); + + } + + + /** + * Calculates the h,k, and l offsets from an integer for (some of )the peaks, given the parameter values. + * + * @param out For each peak element in this array. + * @param xValues xValues give the index in the PeaksWorkspace for the peak. For each peak considered there are + * three consecutive entries all with the same index + * @param nData The size of the xValues and out arrays + */ + void LatticeErrors::function1D ( double *out, const double *xValues, const size_t nData )const + { + UNUSED_ARG( xValues); + std::string PeakWorkspaceName = "_peaks"; + std::vectorParams; + Params.push_back( getParameter("p0")); + Params.push_back( getParameter("p1")); + Params.push_back( getParameter("p2")); + Params.push_back( getParameter("p3")); + Params.push_back( getParameter("p4")); + Params.push_back( getParameter("p5")); + + Mantid::Crystal::OptimizeLatticeForCellType u; + u.optLattice(PeakWorkspaceName, Params, out); + double ChiSqTot=0; + for( size_t i = 0; i(bankNum); - - std::string bankName = "bank"+SbankNum; + std::string bankName = "bank"; + if (instr->getName() == "WISH") bankName = "WISHpanel0"; + bankName += SbankNum; boost::shared_ptr bank =instr_old->getComponentByName( bankName ); if( !bank) @@ -262,6 +263,8 @@ namespace Crystal boost::shared_ptr< const Geometry::RectangularDetector>bankR= boost::dynamic_pointer_cast ( bank); + if (!bankR)return startChar; + double DetWScale = 1, DetHtScale = 1; if( bank) { @@ -431,15 +434,11 @@ namespace Crystal // Find the detector ID from row/col Instrument_const_sptr inst = outWS->getInstrument(); if (!inst) throw std::runtime_error("No instrument in PeaksWorkspace!"); - IComponent_const_sptr bank = inst->getComponentByName(bankName); - if (!bank) throw std::runtime_error("Bank named " + bankName + " not found!"); - RectangularDetector_const_sptr rect = boost::dynamic_pointer_cast(bank); - if (!rect) throw std::runtime_error("Bank named " + bankName + " is not a RectangularDetector!"); - IDetector_sptr det = rect->getAtXY(int(col), int(row)); - if (!det) throw std::runtime_error("Detector not found on " + bankName + "!"); + LoadIsawPeaks u; + int pixelID = u.findPixelID(inst, bankName, static_cast(col), static_cast(row)); //Create the peak object - Peak peak(outWS->getInstrument(), det->getID(), wl); + Peak peak(outWS->getInstrument(), pixelID, wl); // HKL's are flipped by -1 because of the internal Q convention peak.setHKL(-h,-k,-l); peak.setIntensity(Inti); @@ -449,6 +448,31 @@ namespace Crystal return peak; } + int LoadIsawPeaks::findPixelID(Instrument_const_sptr inst, std::string bankName, int col, int row) + { + boost::shared_ptr parent = inst->getComponentByName(bankName); + if (parent->type().compare("RectangularDetector") == 0) + { + boost::shared_ptr RDet = boost::dynamic_pointer_cast< + const RectangularDetector>(parent); + + boost::shared_ptr pixel = RDet->getAtXY(col, row); + return pixel->getID(); + } + else + { + std::vector children; + boost::shared_ptr asmb = boost::dynamic_pointer_cast(parent); + asmb->getChildren(children, false); + int col0 = (col%2==0 ? col/2+75 : (col-1)/2); + boost::shared_ptr asmb2 = boost::dynamic_pointer_cast(children[col0]); + std::vector grandchildren; + asmb2->getChildren(grandchildren,false); + Geometry::IComponent_const_sptr first = grandchildren[row-1]; + Geometry::IDetector_const_sptr det = boost::dynamic_pointer_cast(first); + return det->getID(); + } + } //----------------------------------------------------------------------------------------------- @@ -548,7 +572,9 @@ namespace Crystal std::ostringstream oss; - oss << "bank" << bankNum; + std::string bankString = "bank"; + if (outWS->getInstrument()->getName() == "WISH") bankString = "WISHpanel0"; + oss << bankString << bankNum; std::string bankName = oss.str(); int seqNum = -1; diff --git a/Code/Mantid/Framework/Crystal/src/OptimizeLatticeForCellType.cpp b/Code/Mantid/Framework/Crystal/src/OptimizeLatticeForCellType.cpp index 36a3bcff36e5..b86d87bfae2d 100644 --- a/Code/Mantid/Framework/Crystal/src/OptimizeLatticeForCellType.cpp +++ b/Code/Mantid/Framework/Crystal/src/OptimizeLatticeForCellType.cpp @@ -1,6 +1,6 @@ /*WIKI* - - +This does a least squares fit between indexed peaks and Q values +for a set of runs producing an overall leastSquare orientation matrix. *WIKI*/ #include "MantidCrystal/OptimizeLatticeForCellType.h" #include "MantidCrystal/GSLFunctions.h" @@ -14,6 +14,7 @@ #include "MantidKernel/BoundedValidator.h" #include "MantidGeometry/Crystal/OrientedLattice.h" #include "MantidGeometry/Crystal/IndexingUtils.h" +#include "MantidGeometry/Instrument/RectangularDetector.h" #include #include #include @@ -53,16 +54,7 @@ namespace Mantid OptimizeLatticeForCellType::~OptimizeLatticeForCellType() {} - static double gsl_costFunction(const gsl_vector *v, void *params) - { - std::string *p = (std::string *)params; - std::string inname = p[0]; - std::string cell_type = p[1]; - std::vectorParams; - for ( size_t i = 0; i < v->size; i++ )Params.push_back(gsl_vector_get(v,i)); - Mantid::Crystal::OptimizeLatticeForCellType u; - return u.optLattice(inname, cell_type, Params); - } + //----------------------------------------------------------------------------------------- /** Initialisation method. Declares properties to be used in algorithm. @@ -76,6 +68,7 @@ namespace Mantid cellTypes.push_back("Cubic" ); cellTypes.push_back("Tetragonal" ); cellTypes.push_back("Orthorhombic"); + cellTypes.push_back("Hexagonal"); cellTypes.push_back("Rhombohedral"); cellTypes.push_back("Monoclinic ( a unique )"); cellTypes.push_back("Monoclinic ( b unique )"); @@ -85,6 +78,7 @@ namespace Mantid "Select the cell type."); declareProperty( "Apply", false, "Re-index the peaks"); declareProperty( "Tolerance", 0.12, "Indexing Tolerance"); + declareProperty("EdgePixels",0, "Remove peaks that are at pixels this close to edge. " ); declareProperty("OutputChi2", 0.0,Direction::Output); //Disable default gsl error handler (which is to call abort!) @@ -100,98 +94,175 @@ namespace Mantid { bool apply = this->getProperty("Apply"); double tolerance = this->getProperty("Tolerance"); - std::string par[6]; + int edge = this->getProperty("EdgePixels"); std::string inname = getProperty("PeaksWorkspace"); - par[0] = inname; - std::string type = getProperty("CellType"); - par[1] = type; - PeaksWorkspace_sptr ws = getProperty("PeaksWorkspace"); - - const gsl_multimin_fminimizer_type *T = - gsl_multimin_fminimizer_nmsimplex; - gsl_multimin_fminimizer *s = NULL; - gsl_vector *ss, *x; - gsl_multimin_function minex_func; - + std::string cell_type = getProperty("CellType"); + DataObjects::PeaksWorkspace_sptr ws = getProperty("PeaksWorkspace"); + DataObjects::PeaksWorkspace_sptr peakWS (new PeaksWorkspace()); + peakWS = ws->clone(); + + for (int i= int(peakWS->getNumberPeaks())-1; i>=0; --i) + { + const std::vector &peaks = peakWS->getPeaks(); + if (edgePixel(peakWS, peaks[i].getBankName(), peaks[i].getCol(), peaks[i].getRow(), edge)) + { + peakWS->removePeak(i); + } + } + AnalysisDataService::Instance().addOrReplace("_peaks",peakWS); + // finally do the optimization - size_t nopt = 1; - if(type.compare(0,2,"Te")==0)nopt = 2; - else if(type.compare(0,2,"Or")==0)nopt = 3; - else if(type.compare(0,2,"Rh")==0)nopt = 2; - else if(type.compare(0,2,"He")==0)nopt = 2; - else if(type.compare(0,2,"Mo")==0)nopt = 4; - else if(type.compare(0,2,"Tr")==0)nopt = 6; - size_t iter = 0; - int status = 0; - - /* Starting point */ - x = gsl_vector_alloc (nopt); const DblMatrix UB = ws->sample().getOrientedLattice().getUB(); std::vectorlat(6); IndexingUtils::GetLatticeParameters( UB, lat); // initialize parameters for optimization - gsl_vector_set (x, 0, lat[0]); - if(type.compare(0,2,"Te")==0)gsl_vector_set (x, 1, lat[2]); - else if(type.compare(0,2,"Or")==0) + size_t n_peaks = peakWS->getNumberPeaks(); + MatrixWorkspace_sptr data = WorkspaceFactory::Instance().create( + std::string("Workspace2D"), 1, n_peaks, n_peaks); + for(size_t i = 0; i < data->blocksize(); i++) { - gsl_vector_set (x, 1, lat[1]); - gsl_vector_set (x, 2, lat[2]); + data->dataX(0)[i] = static_cast(i); + data->dataY(0)[i] = 0.0; + data->dataE(0)[i] = 1.0; } - else if(type.compare(0,2,"Rh")==0)gsl_vector_set (x, 1, lat[3]); - else if(type.compare(0,2,"He")==0)gsl_vector_set (x, 1, lat[2]); - else if(type.compare(0,2,"Mo")==0) + + std::ostringstream fun_str; + fun_str << "name=LatticeErrors"; + for ( size_t i = 0; i < 6; i++ )fun_str << ",p" << i <<"="<setPropertyValue("Function", fun_str.str()); + if (cell_type == "Cubic") { - iter++; - status = gsl_multimin_fminimizer_iterate(s); - if (status) - break; - - double size = gsl_multimin_fminimizer_size (s); - status = gsl_multimin_test_size (size, 1e-4); - + std::ostringstream tie_str; + tie_str << "p1=p0,p2=p0,p3=90,p4=90,p5=90"; + fit_alg->setProperty("Ties", tie_str.str()); } - while (status == GSL_CONTINUE && iter < 5000); - - // Output summary to log file - std::string report = gsl_strerror(status); - g_log.notice() << - " Method used = " << " Simplex" << - " Iteration = " << iter << - " Status = " << report << - " Chisq = " << s->fval ; - std::vectorParams; - for ( size_t i = 0; i < s->x->size; i++ )Params.push_back(gsl_vector_get(s->x,i)); - optLattice(inname, type, Params); - gsl_vector_free(x); - gsl_vector_free(ss); - gsl_multimin_fminimizer_free (s); - setProperty("OutputChi2", s->fval); + else if (cell_type == "Tetragonal") + { + std::ostringstream tie_str; + tie_str << "p1=p0,p3=90,p4=90,p5=90"; + fit_alg->setProperty("Ties", tie_str.str()); + } + else if (cell_type == "Orthorhombic") + { + std::ostringstream tie_str; + tie_str << "p3=90,p4=90,p5=90"; + fit_alg->setProperty("Ties", tie_str.str()); + } + else if (cell_type == "Rhombohedral") + { + std::ostringstream tie_str; + tie_str << "p1=p0,p2=p0,p4=p3,p5=p3"; + fit_alg->setProperty("Ties", tie_str.str()); + } + else if (cell_type == "Hexagonal") + { + std::ostringstream tie_str; + tie_str << "p1=p0,p3=90,p4=90,p5=120"; + fit_alg->setProperty("Ties", tie_str.str()); + } + else if (cell_type == "Monoclinic ( a unique )") + { + std::ostringstream tie_str; + tie_str << "p4=90,p5=90"; + fit_alg->setProperty("Ties", tie_str.str()); + } + else if (cell_type == "Monoclinic ( b unique )") + { + std::ostringstream tie_str; + tie_str << "p3=90,p5=90"; + fit_alg->setProperty("Ties", tie_str.str()); + } + else if (cell_type == "Monoclinic ( c unique )") + { + std::ostringstream tie_str; + tie_str << "p3=90,p4=90"; + fit_alg->setProperty("Ties", tie_str.str()); + } + fit_alg->setProperty("InputWorkspace", data); + fit_alg->setProperty("WorkspaceIndex", 0); + fit_alg->setProperty("MaxIterations", 5000); + fit_alg->setProperty("CreateOutput", true); + fit_alg->setProperty("Output", "fit"); + fit_alg->executeAsChildAlg(); + MatrixWorkspace_sptr fitWS = fit_alg->getProperty("OutputWorkspace"); + + double chisq = fit_alg->getProperty("OutputChi2overDoF"); + std::vector Params; + IFunction_sptr out = fit_alg->getProperty("Function"); + + Params.push_back(out->getParameter("p0")); + Params.push_back(out->getParameter("p1")); + Params.push_back(out->getParameter("p2")); + Params.push_back(out->getParameter("p3")); + Params.push_back(out->getParameter("p4")); + Params.push_back(out->getParameter("p5")); + + + std::vector sigabc(Params.size()); + OrientedLattice latt=peakWS->mutableSample().getOrientedLattice(); + DblMatrix UBnew = latt.getUB(); + + calculateErrors(n_peaks, inname, cell_type, Params, sigabc, chisq); + OrientedLattice o_lattice; + o_lattice.setUB( UBnew ); + + if (cell_type == "Cubic") + { + o_lattice.setError(sigabc[0],sigabc[0],sigabc[0],0,0,0); + } + else if (cell_type == "Tetragonal") + { + o_lattice.setError(sigabc[0],sigabc[0],sigabc[1],0,0,0); + } + else if (cell_type == "Orthorhombic") + { + o_lattice.setError(sigabc[0],sigabc[1],sigabc[2],0,0,0); + } + else if (cell_type == "Rhombohedral") + { + o_lattice.setError(sigabc[0],sigabc[0],sigabc[0],sigabc[1],sigabc[1],sigabc[1]); + } + else if (cell_type == "Hexagonal") + { + o_lattice.setError(sigabc[0],sigabc[0],sigabc[1],0,0,0); + } + else if (cell_type == "Monoclinic ( a unique )") + { + o_lattice.setError(sigabc[0],sigabc[1],sigabc[2],sigabc[3],0,0); + } + else if (cell_type == "Monoclinic ( b unique )") + { + o_lattice.setError(sigabc[0],sigabc[1],sigabc[2],0,sigabc[3],0); + + } + else if (cell_type == "Monoclinic ( c unique )") + { + o_lattice.setError(sigabc[0],sigabc[1],sigabc[2],0,0,sigabc[3]); + } + else if (cell_type == "Triclinic") + { + o_lattice.setError(sigabc[0],sigabc[1],sigabc[2],sigabc[3],sigabc[4],sigabc[5]); + } + // Show the modified lattice parameters - OrientedLattice o_lattice = ws->mutableSample().getOrientedLattice(); g_log.notice() << o_lattice << "\n"; + + ws->mutableSample().setOrientedLattice( new OrientedLattice(o_lattice) ); + + setProperty("OutputChi2", chisq); + if ( apply ) { // Reindex peaks with new UB @@ -200,63 +271,45 @@ namespace Mantid alg->setProperty("Tolerance", tolerance); alg->executeAsChildAlg(); } + AnalysisDataService::Instance().remove("_peaks"); } - - - //----------------------------------------------------------------------------------------- + //----------------------------------------------------------------------------------------- /** - * Calls Gaussian1D as a child algorithm to fit the offset peak in a spectrum - * @param mosaic - * @param rcrystallite - * @param inname - * @param corrOption - * @param pointOption - * @param tofParams - * @return - */ - double OptimizeLatticeForCellType::optLattice(std::string inname, std::string cell_type, std::vector & params) + @param inname Name of workspace containing peaks + @param cell_type cell type to optimize + @param params optimized cell parameters + @return chisq of optimization + */ + double OptimizeLatticeForCellType::optLatticeSum(std::string inname, std::string cell_type, std::vector & params) { - PeaksWorkspace_sptr ws = boost::dynamic_pointer_cast - (AnalysisDataService::Instance().retrieve(inname)); - const std::vector &peaks = ws->getPeaks(); - size_t n_peaks = ws->getNumberPeaks(); - std::vector q_vector; - std::vector hkl_vector; - - for ( size_t i = 0; i < params.size(); i++ )params[i]=std::abs(params[i]); - for ( size_t i = 0; i < n_peaks; i++ ) - { - q_vector.push_back(peaks[i].getQSampleFrame()); - hkl_vector.push_back(peaks[i].getHKL()); - } std::vector lattice_parameters; lattice_parameters.assign (6,0); if (cell_type == "Cubic") { - lattice_parameters[0] = params[0]; + lattice_parameters[0] = params[0]; lattice_parameters[1] = params[0]; - lattice_parameters[2] = params[0]; - + lattice_parameters[2] = params[0]; + lattice_parameters[3] = 90; lattice_parameters[4] = 90; lattice_parameters[5] = 90; } else if (cell_type == "Tetragonal") { - lattice_parameters[0] = params[0]; + lattice_parameters[0] = params[0]; lattice_parameters[1] = params[0]; - lattice_parameters[2] = params[1]; - + lattice_parameters[2] = params[1]; + lattice_parameters[3] = 90; lattice_parameters[4] = 90; lattice_parameters[5] = 90; } else if (cell_type == "Orthorhombic") { - lattice_parameters[0] = params[0]; + lattice_parameters[0] = params[0]; lattice_parameters[1] = params[1]; - lattice_parameters[2] = params[2]; - + lattice_parameters[2] = params[2]; + lattice_parameters[3] = 90; lattice_parameters[4] = 90; lattice_parameters[5] = 90; @@ -265,18 +318,18 @@ namespace Mantid { lattice_parameters[0] = params[0]; lattice_parameters[1] = params[0]; - lattice_parameters[2] = params[0]; - + lattice_parameters[2] = params[0]; + lattice_parameters[3] = params[1]; lattice_parameters[4] = params[1]; lattice_parameters[5] = params[1]; } else if (cell_type == "Hexagonal") { - lattice_parameters[0] = params[0]; + lattice_parameters[0] = params[0]; lattice_parameters[1] = params[0]; lattice_parameters[2] = params[1]; - + lattice_parameters[3] = 90; lattice_parameters[4] = 90; lattice_parameters[5] = 120; @@ -286,7 +339,7 @@ namespace Mantid lattice_parameters[0] = params[0]; lattice_parameters[1] = params[1]; lattice_parameters[2] = params[2]; - + lattice_parameters[3] = params[3]; lattice_parameters[4] = 90; lattice_parameters[5] = 90; @@ -296,7 +349,7 @@ namespace Mantid lattice_parameters[0] = params[0]; lattice_parameters[1] = params[1]; lattice_parameters[2] = params[2]; - + lattice_parameters[3] = 90; lattice_parameters[4] = params[3]; lattice_parameters[5] = 90; @@ -306,7 +359,7 @@ namespace Mantid lattice_parameters[0] = params[0]; lattice_parameters[1] = params[1]; lattice_parameters[2] = params[2]; - + lattice_parameters[3] = 90; lattice_parameters[4] = 90; lattice_parameters[5] = params[3]; @@ -315,35 +368,264 @@ namespace Mantid { lattice_parameters[0] = params[0]; lattice_parameters[1] = params[1]; - lattice_parameters[2] = params[2]; - + lattice_parameters[2] = params[2]; + lattice_parameters[3] = params[3]; lattice_parameters[4] = params[4]; lattice_parameters[5] = params[5]; } - - + + PeaksWorkspace_sptr ws = boost::dynamic_pointer_cast + (AnalysisDataService::Instance().retrieve(inname)); + size_t n_peaks = ws->getNumberPeaks(); + double* out = new double[n_peaks]; + optLattice(inname, lattice_parameters, out); + double ChiSqTot=0; + for( size_t i = 0; i & params, double *out) + { + PeaksWorkspace_sptr ws = boost::dynamic_pointer_cast + (AnalysisDataService::Instance().retrieve(inname)); + const std::vector &peaks = ws->getPeaks(); + size_t n_peaks = ws->getNumberPeaks(); + std::vector q_vector; + std::vector hkl_vector; + + for ( size_t i = 0; i < params.size(); i++ )params[i]=std::abs(params[i]); + for ( size_t i = 0; i < n_peaks; i++ ) + { + q_vector.push_back(peaks[i].getQSampleFrame()); + hkl_vector.push_back(peaks[i].getHKL()); + } + Mantid::API::IAlgorithm_sptr alg = createChildAlgorithm("CalculateUMatrix"); alg->setPropertyValue("PeaksWorkspace", inname); - alg->setProperty("a",lattice_parameters[0]); - alg->setProperty("b",lattice_parameters[1]); - alg->setProperty("c",lattice_parameters[2]); - alg->setProperty("alpha",lattice_parameters[3]); - alg->setProperty("beta",lattice_parameters[4]); - alg->setProperty("gamma",lattice_parameters[5]); + alg->setProperty("a",params[0]); + alg->setProperty("b",params[1]); + alg->setProperty("c",params[2]); + alg->setProperty("alpha",params[3]); + alg->setProperty("beta",params[4]); + alg->setProperty("gamma",params[5]); alg->executeAsChildAlg(); ws = alg->getProperty("PeaksWorkspace"); OrientedLattice latt=ws->mutableSample().getOrientedLattice(); DblMatrix UB = latt.getUB(); - - double result = 0; + DblMatrix A = aMatrix(params); + DblMatrix Bc = A; + Bc.Invert(); + DblMatrix U1_B1 = UB * A; + OrientedLattice o_lattice; + o_lattice.setUB( U1_B1 ); + DblMatrix U1 = o_lattice.getU(); + DblMatrix U1_Bc = U1 * Bc; + for ( size_t i = 0; i < hkl_vector.size(); i++ ) { - V3D error = UB * hkl_vector[i] - q_vector[i] / (2.0 * M_PI); - result += error.norm(); + V3D error = U1_Bc * hkl_vector[i] - q_vector[i] / (2.0 * M_PI); + out[i] = error.norm2(); } + + return; + } + //----------------------------------------------------------------------------------------- + /** + @param ws Name of workspace containing peaks + @param bankName Name of bank containing peak + @param col Column number containing peak + @param row Row number containing peak + @param Edge Number of edge points for each bank + @return True if peak is on edge + */ + bool OptimizeLatticeForCellType::edgePixel(PeaksWorkspace_sptr ws, std::string bankName, int col, int row, int Edge) + { + if (bankName.compare("None") == 0) return false; + Geometry::Instrument_const_sptr Iptr = ws->getInstrument(); + boost::shared_ptr parent = Iptr->getComponentByName(bankName); + if (parent->type().compare("RectangularDetector") == 0) + { + boost::shared_ptr RDet = boost::dynamic_pointer_cast< + const RectangularDetector>(parent); + + if (col < Edge || col >= (RDet->xpixels()-Edge) || row < Edge || row >= (RDet->ypixels()-Edge)) return true; + else return false; + } + else + { + std::vector children; + boost::shared_ptr asmb = boost::dynamic_pointer_cast(parent); + asmb->getChildren(children, false); + boost::shared_ptr asmb2 = boost::dynamic_pointer_cast(children[0]); + std::vector grandchildren; + asmb2->getChildren(grandchildren,false); + int NROWS = static_cast(grandchildren.size()); + int NCOLS = static_cast(children.size()); + // Wish pixels and tubes start at 1 not 0 + if (col-1 < Edge || col-1 >= (NCOLS-Edge) || row-1 < Edge || row-1 >= (NROWS-Edge)) return true; + else return false; + } + return false; + } + //----------------------------------------------------------------------------------------- + /** + @param lattice lattice parameters + @return the A matrix calculated + */ + DblMatrix OptimizeLatticeForCellType::aMatrix( std::vector lattice ) + { + double degrees_to_radians = M_PI / 180; + double alpha = lattice[3] * degrees_to_radians; + double beta = lattice[4] * degrees_to_radians; + double gamma = lattice[5] * degrees_to_radians; + + double l1 = lattice[0]; + double l2 = 0; + double l3 = 0; + + double m1 = lattice[1]*std::cos(gamma); + double m2 = lattice[1]*std::sin(gamma); + double m3 = 0; + + double n1 = std::cos(beta); + double n2 = (std::cos(alpha)- std::cos(beta)*std::cos(gamma)) / + std::sin(gamma); + double n3 = std::sqrt( 1 - n1*n1 - n2*n2 ); + n1 *= lattice[2]; + n2 *= lattice[2]; + n3 *= lattice[2]; + + DblMatrix result(3,3); + result[0][0] = l1; + result[0][1] = l2; + result[0][2] = l3; + result[1][0] = m1; + result[1][1] = m2; + result[1][2] = m3; + result[2][0] = n1; + result[2][1] = n2; + result[2][2] = n3; + return result; + } + //----------------------------------------------------------------------------------------- + /** + + @param npeaks Number of peaks + @param inname Name of workspace containing peaks + @param cell_type cell type to optimize + @param Params optimized cell parameters + @param sigabc errors of optimized parameters + @param chisq chisq from optimization + */ + void OptimizeLatticeForCellType::calculateErrors(size_t npeaks, std::string inname, std::string cell_type, + std::vector & Params, + std::vector & sigabc, double chisq) + { + double result = chisq; + for( size_t i=0;i approx; // save list of approximations + + double delta; + size_t nopt = 1; + if(cell_type.compare(0,2,"Te")==0)nopt = 2; + else if(cell_type.compare(0,2,"Or")==0)nopt = 3; + else if(cell_type.compare(0,2,"Rh")==0)nopt = 2; + else if(cell_type.compare(0,2,"He")==0)nopt = 2; + else if(cell_type.compare(0,2,"Mo")==0)nopt = 4; + else if(cell_type.compare(0,2,"Tr")==0)nopt = 6; + + std::vector x; + x.push_back(Params[0]); + if(cell_type.compare(0,2,"Te")==0)x.push_back(Params[2]); + else if(cell_type.compare(0,2,"Or")==0) + { + x.push_back( Params[1]); + x.push_back(Params[2]); + } + else if(cell_type.compare(0,2,"Rh")==0)x.push_back( Params[3]); + else if(cell_type.compare(0,2,"He")==0)x.push_back( Params[2]); + else if(cell_type.compare(0,2,"Mo")==0) + { + x.push_back(Params[1]); + x.push_back(Params[2]); + if(cell_type.compare(13,1,"a")==0)x.push_back( Params[3]); + else if(cell_type.compare(13,1,"b")==0)x.push_back(Params[4]); + else if(cell_type.compare(13,1,"c")==0)x.push_back(Params[5]); + } + else if(cell_type.compare(0,2,"Tr")==0)for ( size_t i = 1; i < nopt; i++ )x.push_back( Params[i]); + + for ( size_t k = 0; k < nopt; k++ ) + { + double diff = 0.0; + + double x_save = x[k]; + + if ( x_save < 1.0e-8 ) // if parameter essentially 0, use + delta = 1.0e-8; // a "small" step + else + delta = START_DELTA * x_save; + + for ( int count = 0; count < MAX_STEPS; count++ ) + { + x[k] = x_save + delta; + double chi_3 = optLatticeSum(inname, cell_type, x); + + x[k] = x_save - delta; + double chi_1 = optLatticeSum(inname, cell_type, x); + + x[k] = x_save; + double chi_2 = optLatticeSum(inname, cell_type, x); + + diff = chi_1-2*chi_2+chi_3; + if ( diff > 0 ) + { + approx.push_back(std::abs(delta) * + std::sqrt(2.0/std::abs(diff))); + } + delta = delta / 10; + } + if ( approx.size() == 0 ) + sigabc[k] = Mantid::EMPTY_DBL(); // no reasonable value + + else if ( approx.size() == 1 ) + sigabc[k] = approx[0]; // only one possible value + + else // use one with smallest diff + { + double min_diff = Mantid::EMPTY_DBL(); + for ( size_t i = 0; i < approx.size()-1; i++ ) + { + diff = std::abs( (approx[i+1]-approx[i])/approx[i] ); + if ( diff < min_diff ) + { + sigabc[k] = approx[i+1]; + min_diff = diff; + } + } + } } + + + delta = result /(double)nDOF; + + for( size_t i = 0; i < std::min(7,sigabc.size()); i++ ) + sigabc[i] = sqrt( delta) * sigabc[i]; + + } } // namespace Algorithm } // namespace Mantid diff --git a/Code/Mantid/Framework/Crystal/src/PredictPeaks.cpp b/Code/Mantid/Framework/Crystal/src/PredictPeaks.cpp index 1e195549ef21..0a03cfdb7a5b 100644 --- a/Code/Mantid/Framework/Crystal/src/PredictPeaks.cpp +++ b/Code/Mantid/Framework/Crystal/src/PredictPeaks.cpp @@ -318,7 +318,7 @@ namespace Crystal { // ---------------- Determine which HKL to look for ------------------------------------- // Inverse d-spacing that is the limit to look for. - double dstar = 1.0/minD; + double Qmax = 2.*M_PI/minD; V3D hklMin(0,0,0); V3D hklMax(0,0,0); for (double qx=-1; qx < 2; qx += 2) @@ -327,7 +327,7 @@ namespace Crystal { // Build a q-vector for this corner of a cube V3D Q(qx,qy,qz); - Q *= dstar; + Q *= Qmax; V3D hkl = crystal.hklFromQ(Q); // Find the limits of each hkl for (size_t i=0; i<3; i++) diff --git a/Code/Mantid/Framework/Crystal/src/SCDCalibratePanels.cpp b/Code/Mantid/Framework/Crystal/src/SCDCalibratePanels.cpp index fe0f9fb1c42a..daed93e21c9d 100644 --- a/Code/Mantid/Framework/Crystal/src/SCDCalibratePanels.cpp +++ b/Code/Mantid/Framework/Crystal/src/SCDCalibratePanels.cpp @@ -99,6 +99,7 @@ To do so select the workspace, which you have calibrated as the InputWorkspace a #include "MantidAPI/IFunction.h" #include "MantidGeometry/Instrument/RectangularDetector.h" #include "MantidGeometry/Crystal/IndexingUtils.h" +#include "MantidGeometry/Crystal/OrientedLattice.h" using namespace Mantid::DataObjects; using namespace Mantid::API; @@ -669,7 +670,17 @@ namespace Mantid double alpha = getProperty("alpha"); double beta = getProperty("beta"); double gamma = getProperty("gamma"); - + if ((a == EMPTY_DBL() || b == EMPTY_DBL() || c == EMPTY_DBL() || alpha == EMPTY_DBL() || + beta == EMPTY_DBL() || gamma == EMPTY_DBL()) && peaksWs->sample().hasOrientedLattice()) + { + OrientedLattice latt = peaksWs->mutableSample().getOrientedLattice(); + a = latt.a(); + b = latt.b(); + c = latt.c(); + alpha = latt.alpha(); + beta = latt.beta(); + gamma = latt.gamma(); + } double tolerance = getProperty("tolerance"); if( !GoodStart( peaksWs, a,b,c,alpha,beta,gamma,tolerance)) @@ -1103,6 +1114,7 @@ namespace Mantid * @param instrument The instrument to be modified * @param AllBankName The bank names in this instrument that will be modified * @param T0 The time offset from the DetCal file + * @param L0 The length offset from the DetCal file * @param filename The DetCal file name * @param bankPrefixName The prefix to the bank names. */ @@ -1316,12 +1328,11 @@ namespace Mantid * Really this is the operator SaveIsawDetCal but only the results of the given * banks are saved. L0 and T0 are also saved. * - * @param NewInstrument -The instrument with the correct panel geometries + * @param instrument -The instrument with the correct panel geometries * and initial path length - * * @param AllBankName -the set of the NewInstrument names of the banks(panels) - * @param T0 -The time offset - * @param FileName -The name of the DetCal file to save the results to + * @param T0 -The time offset from the DetCal file + * @param filename -The name of the DetCal file to save the results to */ void SCDCalibratePanels::saveIsawDetCal( boost::shared_ptr &instrument, set &AllBankName,double T0,string filename) @@ -1371,12 +1382,12 @@ namespace Mantid auto mustBePositive = boost::make_shared >(); mustBePositive->setLower(0.0); - declareProperty("a", 0.0, mustBePositive, "Lattice Parameter a"); - declareProperty("b", 0.0, mustBePositive, "Lattice Parameter b"); - declareProperty("c", 0.0, mustBePositive, "Lattice Parameter c"); - declareProperty("alpha", 0.0, mustBePositive, "Lattice Parameter alpha in degrees"); - declareProperty("beta", 0.0, mustBePositive, "Lattice Parameter beta in degrees"); - declareProperty("gamma", 0.0, mustBePositive, "Lattice Parameter gamma in degrees"); + declareProperty("a", EMPTY_DBL(), mustBePositive, "Lattice Parameter a (Leave empty to use lattice constants in peaks workspace)"); + declareProperty("b", EMPTY_DBL(), mustBePositive, "Lattice Parameter b (Leave empty to use lattice constants in peaks workspace)"); + declareProperty("c", EMPTY_DBL(), mustBePositive, "Lattice Parameter c (Leave empty to use lattice constants in peaks workspace)"); + declareProperty("alpha", EMPTY_DBL(), mustBePositive, "Lattice Parameter alpha in degrees (Leave empty to use lattice constants in peaks workspace)"); + declareProperty("beta", EMPTY_DBL(), mustBePositive, "Lattice Parameter beta in degrees (Leave empty to use lattice constants in peaks workspace)"); + declareProperty("gamma", EMPTY_DBL(), mustBePositive, "Lattice Parameter gamma in degrees (Leave empty to use lattice constants in peaks workspace)"); declareProperty("useL0", true, "Fit the L0(source to sample) distance"); declareProperty("usetimeOffset", true, "Fit the time offset value"); diff --git a/Code/Mantid/Framework/Crystal/src/SaveHKL.cpp b/Code/Mantid/Framework/Crystal/src/SaveHKL.cpp index 1c0eadb3391e..6b3369cd07ce 100644 --- a/Code/Mantid/Framework/Crystal/src/SaveHKL.cpp +++ b/Code/Mantid/Framework/Crystal/src/SaveHKL.cpp @@ -127,6 +127,9 @@ namespace Crystal histoTypes.push_back(""); declareProperty("SortBy", histoTypes[2],boost::make_shared(histoTypes), "Sort the histograms by bank, run number or both (default)."); + declareProperty("MinIsigI", EMPTY_DBL(), mustBePositive, + "The minimum I/sig(I) ratio"); + declareProperty("WidthBorder", EMPTY_INT(), "Width of border of detectors"); } //---------------------------------------------------------------------------------------------- @@ -136,13 +139,14 @@ namespace Crystal { std::string filename = getPropertyValue("Filename"); - PeaksWorkspace_sptr ws = getProperty("InputWorkspace"); - + ws = getProperty("InputWorkspace"); double scaleFactor = getProperty("ScalePeaks"); double dMin = getProperty("MinDSpacing"); double wlMin = getProperty("MinWavelength"); double wlMax = getProperty("MaxWavelength"); std::string type = getProperty("SortBy"); + double minIsigI = getProperty("MinIsigI"); + int widthBorder = getProperty("WidthBorder"); // Sequence and run number int seqNum = 1; @@ -294,9 +298,14 @@ namespace Crystal Peak & p = peaks[wi]; if (p.getIntensity() == 0.0 || boost::math::isnan(p.getIntensity()) || boost::math::isnan(p.getSigmaIntensity())) continue; + if (minIsigI != EMPTY_DBL() && p.getIntensity() < std::abs(minIsigI * p.getSigmaIntensity())) continue; int run = p.getRunNumber(); int bank = 0; std::string bankName = p.getBankName(); + int nCols, nRows; + sizeBanks(bankName, nCols, nRows); + if (widthBorder != EMPTY_INT() && (p.getCol() < widthBorder || p.getRow() < widthBorder || p.getCol() > (nCols - widthBorder) || + p.getRow() > (nRows -widthBorder))) continue; // Take out the "bank" part of the bank name and convert to an int bankName.erase(remove_if(bankName.begin(), bankName.end(), not1(std::ptr_fun (::isdigit))), bankName.end()); Strings::convert(bankName, bank); @@ -516,7 +525,30 @@ namespace Crystal return spect; } - + void SaveHKL::sizeBanks(std::string bankName, int& nCols, int& nRows) + { + if (bankName.compare("None") == 0) return; + boost::shared_ptr parent = ws->getInstrument()->getComponentByName(bankName); + if (parent->type().compare("RectangularDetector") == 0) + { + boost::shared_ptr RDet = boost::dynamic_pointer_cast< + const RectangularDetector>(parent); + + nCols = RDet->xpixels(); + nRows = RDet->ypixels(); + } + else + { + std::vector children; + boost::shared_ptr asmb = boost::dynamic_pointer_cast(parent); + asmb->getChildren(children, false); + boost::shared_ptr asmb2 = boost::dynamic_pointer_cast(children[0]); + std::vector grandchildren; + asmb2->getChildren(grandchildren,false); + nRows = static_cast(grandchildren.size()); + nCols = static_cast(children.size()); + } + } } // namespace Mantid } // namespace Crystal diff --git a/Code/Mantid/Framework/Crystal/src/SaveIsawPeaks.cpp b/Code/Mantid/Framework/Crystal/src/SaveIsawPeaks.cpp index 60f62816606f..3ac03bcb3cae 100644 --- a/Code/Mantid/Framework/Crystal/src/SaveIsawPeaks.cpp +++ b/Code/Mantid/Framework/Crystal/src/SaveIsawPeaks.cpp @@ -117,7 +117,8 @@ namespace Crystal // Save the "bank" part once to check whether it really is a bank if( bankPart == "?") bankPart = bankName.substr(0,4); // Take out the "bank" part of the bank name and convert to an int - bankName = bankName.substr(4, bankName.size()-4); + if( bankPart == "bank")bankName = bankName.substr(4, bankName.size()-4); + else if( bankPart == "WISH")bankName = bankName.substr(9, bankName.size()-9); Strings::convert(bankName, bank); // Save in the map @@ -126,10 +127,10 @@ namespace Crystal uniqueBanks.insert(bank); } - Instrument_const_sptr inst = ws->getInstrument(); + inst = ws->getInstrument(); if (!inst) throw std::runtime_error("No instrument in PeaksWorkspace. Cannot save peaks file."); - if( bankPart != "bank" && bankPart != "?" ) { + if( bankPart != "bank" && bankPart != "WISH" && bankPart != "?" ) { std::ostringstream mess; mess << "Detector module of type " << bankPart << " not supported in ISAWPeaks. Cannot save peaks file"; throw std::runtime_error( mess.str() ); } @@ -180,10 +181,12 @@ namespace Crystal // Build up the bank name int bank = *it; std::ostringstream mess; - mess << "bank" << bank; + if( bankPart == "bank")mess << "bank" << bank; + else if( bankPart == "WISH")mess << "WISHpanel0" << bank; + std::string bankName = mess.str(); // Retrieve it - RectangularDetector_const_sptr det = boost::dynamic_pointer_cast(inst->getComponentByName(bankName)); + boost::shared_ptr det = inst->getComponentByName(bankName); if (det) { // Center of the detector @@ -192,19 +195,22 @@ namespace Crystal double detd = (center - inst->getSample()->getPos()).norm(); // Base unit vector (along the horizontal, X axis) - V3D base = det->getAtXY(det->xpixels()-1,0)->getPos() - det->getAtXY(0,0)->getPos(); + V3D base = findPixelPos(bankName,1,0) - findPixelPos(bankName,0,0); base.normalize(); // Up unit vector (along the vertical, Y axis) - V3D up = det->getAtXY(0,det->ypixels()-1)->getPos() - det->getAtXY(0,0)->getPos(); + V3D up = findPixelPos(bankName,0,1) - findPixelPos(bankName,0,0); up.normalize(); + int NCOLS, NROWS; + double xsize, ysize; + sizeBanks(bankName, NCOLS, NROWS, xsize, ysize); // Write the line out << "5 " << std::setw(6) << std::right << bank << " " - << std::setw(6) << std::right << det->xpixels() << " " - << std::setw(6) << std::right << det->ypixels() << " " - << std::setw(7) << std::right << std::fixed << std::setprecision(4) << 100.0*det->xsize() << " " - << std::setw(7) << std::right << std::fixed << std::setprecision(4) << 100.0*det->ysize() << " " + << std::setw(6) << std::right << NCOLS << " " + << std::setw(6) << std::right << NROWS << " " + << std::setw(7) << std::right << std::fixed << std::setprecision(4) << 100.0*xsize << " " + << std::setw(7) << std::right << std::fixed << std::setprecision(4) << 100.0*ysize << " " << " 0.2000 " << std::setw(6) << std::right << std::fixed << std::setprecision(2) << 100.0*detd << " " << std::setw(9) << std::right << std::fixed << std::setprecision(4) << 100.0*center.X() << " " @@ -360,24 +366,63 @@ namespace Crystal out.flush(); out.close(); -// //REMOVE: -// std::string line; -// std::ifstream myfile (filename.c_str()); -// if (myfile.is_open()) -// { -// while ( myfile.good() ) -// { -// getline (myfile,line); -// std::cout << line << std::endl; -// } -// myfile.close(); -// } - - } - - + V3D SaveIsawPeaks::findPixelPos(std::string bankName, int col, int row) + { + boost::shared_ptr parent = inst->getComponentByName(bankName); + if (parent->type().compare("RectangularDetector") == 0) + { + boost::shared_ptr RDet = boost::dynamic_pointer_cast< + const RectangularDetector>(parent); + + boost::shared_ptr pixel = RDet->getAtXY(col, row); + return pixel->getPos(); + } + else + { + std::vector children; + boost::shared_ptr asmb = boost::dynamic_pointer_cast(parent); + asmb->getChildren(children, false); + boost::shared_ptr asmb2 = boost::dynamic_pointer_cast(children[col]); + std::vector grandchildren; + asmb2->getChildren(grandchildren,false); + Geometry::IComponent_const_sptr first = grandchildren[row]; + return first->getPos(); + } + } + void SaveIsawPeaks::sizeBanks(std::string bankName, int& NCOLS, int& NROWS, double& xsize, double& ysize) + { + if (bankName.compare("None") == 0) return; + boost::shared_ptr parent = inst->getComponentByName(bankName); + if (parent->type().compare("RectangularDetector") == 0) + { + boost::shared_ptr RDet = boost::dynamic_pointer_cast< + const RectangularDetector>(parent); + + NCOLS = RDet->xpixels(); + NROWS = RDet->ypixels(); + xsize = RDet->xsize(); + ysize = RDet->ysize(); + } + else + { + std::vector children; + boost::shared_ptr asmb = boost::dynamic_pointer_cast(parent); + asmb->getChildren(children, false); + boost::shared_ptr asmb2 = boost::dynamic_pointer_cast(children[0]); + std::vector grandchildren; + asmb2->getChildren(grandchildren,false); + NROWS = static_cast(grandchildren.size()); + NCOLS = static_cast(children.size()); + Geometry::IComponent_const_sptr first = children[0]; + Geometry::IComponent_const_sptr last = children[NCOLS-1]; + xsize = first->getDistance(*last); + first = grandchildren[0]; + last = grandchildren[NROWS-1]; + ysize = first->getDistance(*last); + } + } } // namespace Mantid diff --git a/Code/Mantid/Framework/Crystal/test/OptimizeLatticeForCellTypeTest.h b/Code/Mantid/Framework/Crystal/test/OptimizeLatticeForCellTypeTest.h index 2c94b629a989..0ec0ce227e21 100644 --- a/Code/Mantid/Framework/Crystal/test/OptimizeLatticeForCellTypeTest.h +++ b/Code/Mantid/Framework/Crystal/test/OptimizeLatticeForCellTypeTest.h @@ -71,9 +71,9 @@ class OptimizeLatticeForCellTypeTest : public CxxTest::TestSuite // Check that the UB matrix is the same as in TOPAZ_3007.mat OrientedLattice latt=ws->mutableSample().getOrientedLattice(); - double correct_UB[] = { -0.0500, 0.04000, 0.0019, - -0.0053, -0.0071, 0.1290, - 0.0615, 0.0319, 0.0127 }; + double correct_UB[] = { -0.0477, 0.0413, -0.0005, + -0.0055, -0.0090, 0.1250, + 0.0610, 0.0314, 0.0110 }; std::vector UB_calculated = latt.getUB().getVector(); diff --git a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt index f234812a7133..0dc9a9bafe2c 100644 --- a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt +++ b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt @@ -23,8 +23,8 @@ set ( SRC_FILES src/DampingMinimizer.cpp src/DeltaFunction.cpp src/DerivMinimizer.cpp + src/DiffRotDiscreteCircle.cpp src/DiffSphere.cpp - src/ElasticDiffSphere.cpp src/EndErfc.cpp src/ExpDecay.cpp src/ExpDecayMuon.cpp @@ -44,7 +44,6 @@ set ( SRC_FILES src/GaussianComptonProfile.cpp src/GramCharlierComptonProfile.cpp src/IkedaCarpenterPV.cpp - src/InelasticDiffSphere.cpp src/LeBailFit.cpp src/LeBailFunction.cpp src/LevenbergMarquardtMDMinimizer.cpp @@ -121,8 +120,8 @@ set ( INC_FILES inc/MantidCurveFitting/DeltaFunction.h inc/MantidCurveFitting/DerivMinimizer.h inc/MantidCurveFitting/DiffSphere.h + inc/MantidCurveFitting/DiffRotDiscreteCircle.h inc/MantidCurveFitting/DllConfig.h - inc/MantidCurveFitting/ElasticDiffSphere.h inc/MantidCurveFitting/EmptyValues.h inc/MantidCurveFitting/EndErfc.h inc/MantidCurveFitting/ExpDecay.h @@ -145,7 +144,6 @@ set ( INC_FILES inc/MantidCurveFitting/GaussianComptonProfile.h inc/MantidCurveFitting/GramCharlierComptonProfile.h inc/MantidCurveFitting/IkedaCarpenterPV.h - inc/MantidCurveFitting/InelasticDiffSphere.h inc/MantidCurveFitting/Jacobian.h inc/MantidCurveFitting/LeBailFit.h inc/MantidCurveFitting/LeBailFunction.h @@ -197,7 +195,6 @@ set ( INC_FILES set ( TEST_FILES # ChebyshevPolynomialBackgroundTest.h # RefinePowderInstrumentParametersTest.h - #DiffSphereTest.h #SCDPanelErrorsTest.h AbragamTest.h AugmentedLagrangianOptimizerTest.h @@ -216,6 +213,8 @@ set ( TEST_FILES CubicSplineTest.h DampingMinimizerTest.h DeltaFunctionTest.h + DiffRotDiscreteCircleTest.h + DiffSphereTest.h EndErfcTest.h ExpDecayMuonTest.h ExpDecayOscTest.h diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateGammaBackground.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateGammaBackground.h index 2765d7ad0458..9bd2a19e11af 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateGammaBackground.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateGammaBackground.h @@ -81,8 +81,6 @@ namespace Mantid /// Compute the theta range for a given foil std::pair calculateThetaRange(const Geometry::IComponent_const_sptr & foilComp, const double radius, const unsigned int horizDir) const; - /// Retrieve parameter for given component - double getComponentParameter(const Geometry::IComponent & comp,const std::string &name) const; /// Input TOF data API::MatrixWorkspace_const_sptr m_inputWS; diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ComptonProfile.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ComptonProfile.h index f9fcebf7f5ec..fc076b667f8a 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ComptonProfile.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ComptonProfile.h @@ -58,6 +58,10 @@ namespace CurveFitting class MANTID_CURVEFITTING_DLL ComptonProfile : public virtual API::ParamFunction, public virtual API::IFunction1D { public: + /// Retrieve a component parameter + static double getComponentParameter(const Geometry::IComponent_const_sptr & comp, const Geometry::ParameterMap &pmap, + const std::string &name); + /// Default constructor required for factory ComptonProfile(); @@ -116,10 +120,6 @@ namespace CurveFitting /// Logger Kernel::Logger & m_log; - private: - /// Retrieve a component parameter - double getComponentParameter(const Geometry::IComponent & comp,const std::string &name) const; - /// Current workspace index, required to access instrument parameters size_t m_wsIndex; /// Store the mass values diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/DiffRotDiscreteCircle.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/DiffRotDiscreteCircle.h new file mode 100644 index 000000000000..66f16955ad6d --- /dev/null +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/DiffRotDiscreteCircle.h @@ -0,0 +1,136 @@ +#ifndef MANTID_DIFFROTDISCRETECIRCLE_H_ +#define MANTID_DIFFROTDISCRETECIRCLE_H_ + +#include "MantidAPI/ParamFunction.h" +#include "MantidAPI/IFunction1D.h" +#include "MantidAPI/FunctionDomain.h" +#include "MantidAPI/Jacobian.h" +#include "MantidAPI/ImmutableCompositeFunction.h" +#include "DeltaFunction.h" + +namespace Mantid +{ +namespace CurveFitting +{ + /** + @author Jose Borreguero, NScD + @date December 02 2013 + + Copyright © 2007-8 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: + Code Documentation is available at: + */ + +/* Class representing the elastic portion of DiffRotDiscreteCircle + * Contains a Delta Dirac. + */ +class DLLExport ElasticDiffRotDiscreteCircle : public DeltaFunction +{ +public: + + /// Constructor + ElasticDiffRotDiscreteCircle(); + + /// Destructor + virtual ~ElasticDiffRotDiscreteCircle() {}; + + /// overwrite IFunction base class methods + virtual std::string name()const{ return "ElasticDiffRotDiscreteCircle"; } + + virtual const std::string category() const { return "QENS"; } + + /// overwrite IFunction base class method, which declare function parameters + virtual void init(); + + /// A rescaling of the peak intensity + double HeightPrefactor() const; + +}; + + +/* Class representing the inelastic portion of DiffRotDiscreteCircle + * Contains a linear combination of Lorentzians. + */ +class DLLExport InelasticDiffRotDiscreteCircle : public API::ParamFunction, public API::IFunction1D +{ +public: + + /// Constructor + InelasticDiffRotDiscreteCircle(); + + /// Destructor + virtual ~InelasticDiffRotDiscreteCircle() {} + + virtual std::string name() const { return "InelasticDiffRotDiscreteCircle"; } + + virtual const std::string category() const { return "QENS"; } + + virtual void init(); + +protected: + + virtual void function1D( double * out, const double* xValues, const size_t nData ) const; + +private: + + const double m_hbar; // Plank constant, in meV*THz (or ueV*PHz) +}; + + +/* Class representing the dynamics structure factor of a particle undergoing + * discrete jumps on N-sites evenly distributed in a circle. The particle can only + * jump to neighboring sites. This is the most common type of discrete + * rotational diffusion in a circle. + */ +class DLLExport DiffRotDiscreteCircle : public API::ImmutableCompositeFunction +{ +public: + + /// Destructor + ~DiffRotDiscreteCircle() {}; + + virtual std::string name() const { return "DiffRotDiscreteCircle"; } + + virtual const std::string category() const { return "QENS"; } + + virtual int version() const { return 1; } + + virtual void init(); + + /// Propagate an attribute to member functions + virtual void trickleDownAttribute( const std::string &name ); + + /// Override parent definition + virtual void declareAttribute( const std::string & name, const API::IFunction::Attribute & defaultValue ); + + /// Override parent definition + virtual void setAttribute( const std::string & attName, const Attribute & att ); + +private: + + boost::shared_ptr m_elastic; + + boost::shared_ptr m_inelastic; + +}; + +} // namespace CurveFitting +} // namespace Mantid + +#endif /*MANTID_DIFFROTDISCRETECIRCLE_H_*/ diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/DiffSphere.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/DiffSphere.h index d75884a7c5c3..938002944280 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/DiffSphere.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/DiffSphere.h @@ -4,14 +4,13 @@ //---------------------------------------------------------------------- // Includes //---------------------------------------------------------------------- +#include "DeltaFunction.h" #include "MantidAPI/ParamFunction.h" #include "MantidAPI/IFunction1D.h" #include "MantidAPI/FunctionDomain.h" #include "MantidAPI/Jacobian.h" #include "MantidAPI/ImmutableCompositeFunction.h" #include "DeltaFunction.h" -#include "MantidCurveFitting/ElasticDiffSphere.h" -#include "MantidCurveFitting/InelasticDiffSphere.h" namespace Mantid { @@ -42,34 +41,133 @@ namespace CurveFitting Code Documentation is available at: */ -class DLLExport DiffSphere : public API::ImmutableCompositeFunction +class DLLExport ElasticDiffSphere : public DeltaFunction { public: + /// Constructor - DiffSphere(); + ElasticDiffSphere(); + + /// Destructor + virtual ~ElasticDiffSphere() {}; + + /// overwrite IFunction base class methods + virtual std::string name() const { return "ElasticDiffSphere"; } + + virtual const std::string category() const { return "QENS"; } + + /// A rescaling of the peak intensity + double HeightPrefactor() const; + + /// overwrite IFunction base class method, which declare function parameters + virtual void init(); +}; + + +/// structure to hold info on Volino's coefficients +struct xnlc { + double x; + size_t n; + size_t l; +}; + +/// simple structure to hold a linear interpolation of factor J around its numerical divergence point +struct linearJ { + double slope; + double intercept; +}; + +/* Class representing the inelastic portion of the DiffSphere algorithm. + * Contains the 98 Lorentzians. + */ +class DLLExport InelasticDiffSphere : public API::ParamFunction, public API::IFunction1D +{ +public: + + InelasticDiffSphere(); + + virtual ~InelasticDiffSphere() {} + + /// overwrite IFunction base class methods + virtual void init(); + + /// overwrite IFunction base class methods + virtual std::string name()const{return "InelasticDiffSphere"; } + + /// overwrite IFunction base class methods + virtual const std::string category() const { return "QENS"; } + + /// Calculate the (2l+1)*A_{n,l} coefficients for each Lorentzian + std::vector< double > LorentzianCoefficients( double a ) const; + +protected: + + virtual void function1D( double* out, const double* xValues, const size_t nData ) const; + +private: + + /// initialize the Xnl coefficients + void initXnlCoeff(); + + /// initialize the m_alpha coefficients + void initAlphaCoeff(); + + /// initialize the list of parameters for A_{n,l} linear interpolation around the indeterminacy point + void initLinJlist(); + + /// xnl coefficients + std::vector< xnlc > m_xnl; + + /// certain coefficients invariant during fitting + std::vector< double > m_alpha; + + /// maximum value of l in xnlist + size_t m_lmax; + + /// linear interpolation zone around the numerical divergence of factor J + double m_divZone; + + /// Plank's constant divided by \f$2\pi\f$, in units of meV*THz + double m_hbar; + + /// list of linearized J values + std::vector< linearJ > m_linearJlist; + +}; // end of class InelasticDiffSphere + + +class DLLExport DiffSphere : public API::ImmutableCompositeFunction +{ + +public: /// Destructor ~DiffSphere() {}; /// overwrite IFunction base class methods - std::string name()const{return "DiffSphere";} + std::string name()const{ return "DiffSphere"; } - virtual const std::string category() const { return "QuasiElastic";} + /// overwrite IFunction base class methods + virtual const std::string category() const { return "QENS"; } - virtual int version() const { return 1;} + /// overwrite IFunction base class methods + virtual int version() const { return 1; } /// Propagate an attribute to member functions virtual void trickleDownAttribute( const std::string& name ); /// Override parent definition - virtual void declareAttribute(const std::string & name,const API::IFunction::Attribute & defaultValue); + virtual void declareAttribute(const std::string & name,const API::IFunction::Attribute & defaultValue ); /// Override parent definition - virtual void setAttribute(const std::string& attName,const Attribute& att); + virtual void setAttribute( const std::string& attName, const Attribute& att ); + + /// overwrite IFunction base class method, which declare function parameters + virtual void init(); private: - //API::IFunctionMW* m_elastic; //elastic intensity of the DiffSphere structure factor - boost::shared_ptr m_elastic; + + boost::shared_ptr m_elastic; //elastic intensity of the DiffSphere structure factor boost::shared_ptr m_inelastic; //inelastic intensity of the DiffSphere structure factor }; diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ElasticDiffSphere.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ElasticDiffSphere.h deleted file mode 100644 index 827afacdc291..000000000000 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ElasticDiffSphere.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef MANTID_ELASTICDIFFSPHERE_H_ -#define MANTID_ELASTICDIFFSPHERE_H_ - -//---------------------------------------------------------------------- -// Includes -//---------------------------------------------------------------------- -#include "MantidAPI/ParamFunction.h" -#include "MantidAPI/IFunction1D.h" -#include "MantidAPI/FunctionDomain.h" -#include "MantidAPI/Jacobian.h" -#include "MantidAPI/ImmutableCompositeFunction.h" -#include "DeltaFunction.h" - -namespace Mantid -{ -namespace CurveFitting -{ - /** - @author Jose Borreguero, NScD - @date 11/14/2011 - - Copyright © 2007-8 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory - - This file is part of Mantid. - - Mantid is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - Mantid is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - File change history is stored at: - Code Documentation is available at: - */ - -class DLLExport ElasticDiffSphere : public DeltaFunction -{ -public: - - /// Constructor - ElasticDiffSphere(); - - /// Destructor - virtual ~ElasticDiffSphere() {}; - - /// overwrite IFunction base class methods - virtual std::string name()const{return "ElasticDiffSphere";} - - /// A rescaling of the peak intensity - double HeightPrefactor() const; - -}; - -} // namespace CurveFitting -} // namespace Mantid - -#endif /*MANTID_ELASTICDIFFSPHERE_H_*/ diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/GSLMatrix.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/GSLMatrix.h index 0e99f6abddac..2961cdfeacd6 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/GSLMatrix.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/GSLMatrix.h @@ -10,7 +10,6 @@ #include #include -#include #include namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/InelasticDiffSphere.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/InelasticDiffSphere.h deleted file mode 100644 index 1fe2e47f89eb..000000000000 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/InelasticDiffSphere.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef MANTID_INELASTICDIFFSPHERE_H_ -#define MANTID_INELASTICDIFFSPHERE_H_ - -//---------------------------------------------------------------------- -// Includes -//---------------------------------------------------------------------- -#include "MantidAPI/ParamFunction.h" -#include "MantidAPI/IFunction1D.h" -#include "MantidAPI/FunctionDomain.h" -#include "MantidAPI/Jacobian.h" -#include "MantidAPI/ImmutableCompositeFunction.h" -#include "DeltaFunction.h" - -namespace Mantid -{ -namespace CurveFitting -{ - /** - @author Jose Borreguero, NScD - @date 11/14/2011 - - Copyright © 2007-8 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory - - This file is part of Mantid. - - Mantid is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - Mantid is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - File change history is stored at: - Code Documentation is available at: - */ - -/// structure to hold info on Volino's coefficients -struct xnlc{ - double x; - unsigned int l; - unsigned int n; -}; - -/// simple structure to hold a linear interpolation of factor J around its numerical divergence point -struct linearJ{ - double slope; - double intercept; -}; - -/* Class representing the inelastic portion of the DiffSphere algorithm. - * Contains the 98 Lorentzians. - */ -class DLLExport InelasticDiffSphere : public API::ParamFunction, public API::IFunction1D -{ -public: - - InelasticDiffSphere(); - virtual ~InelasticDiffSphere() {} - - virtual std::string name()const{return "InelasticDiffSphere";} - virtual const std::string category() const { return "QuasiElastic";} - - void calNumericalDeriv2(const API::FunctionDomain& domain, API::Jacobian& out); - -protected: - virtual void function1D(double* out, const double* xValues, const size_t nData)const; - virtual void functionDeriv1D(API::Jacobian* out, const double* xValues, const size_t nData); - virtual void functionDeriv(const API::FunctionDomain& domain, API::Jacobian& jacobian); - std::vector LorentzianCoefficients(double a) const; - -private: - - /// initialize the Xnl coefficients - void initXnlCoeff(); - - /// initialize the alpha coefficients - void initAlphaCoeff(); - - /// initialize the list of Linearized J values - void initLinJlist(); - - /// xnl coefficients - std::vector xnl; - - /// certain coefficients invariant during fitting - std::vector alpha; - - /// maximum value of l in xnlist - unsigned int lmax; - - /// number of coefficients - unsigned int ncoeff; - - /// linear interpolation zone around the numerical divergence of factor J - double m_divZone; - - /// list of linearized J values - std::vector linearJlist; - -}; // end of class InelasticDiffSphere - -} // namespace CurveFitting -} // namespace Mantid - -#endif /*MANTID_INELASTICDIFFSPHERE_H_*/ diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/Lorentzian.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/Lorentzian.h index 115395710c6e..18fbb764193c 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/Lorentzian.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/Lorentzian.h @@ -12,14 +12,15 @@ namespace Mantid { /** Provide lorentzian peak shape function interface to IPeakFunction. - I.e. the function: \frac{A}{\pi}( \Gamma/((x-PeakCentre)^2+HWHM^2) ). + I.e. the function: \f$ \frac{A}{\pi}( \Gamma/2((x-PeakCentre)^2+(\Gamma/2)^2) )\f$. + \f$\Gamma/2\f$ (HWHM) - half-width at half-maximum Lorentzian parameters:
    -
  • Amplitude - Maximum height of peak at x=PeakCentre(default 1.0)
  • +
  • Amplitude - Intensity scaling (default 1.0)
  • PeakCentre - centre of peak (default 0.0)
  • -
  • HWHM - half-width half-maximum (default 0.0)
  • +
  • FWHM - Full-width half-maximum (default 0.0)
Copyright © 2007-8 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ProcessBackground.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ProcessBackground.h index d913150a75df..765054cfe07b 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ProcessBackground.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ProcessBackground.h @@ -101,7 +101,7 @@ class DLLExport ProcessBackground : public API::Algorithm DataObjects::Workspace2D_sptr autoBackgroundSelection(DataObjects::Workspace2D_sptr bkgdWS); /// Create a background function from input properties - BackgroundFunction_sptr createBackgroundFunction(); + BackgroundFunction_sptr createBackgroundFunction(const std::string backgroundtype); /// Filter non-background data points out and create a background workspace DataObjects::Workspace2D_sptr filterForBackground(BackgroundFunction_sptr bkgdfunction); @@ -114,6 +114,10 @@ class DLLExport ProcessBackground : public API::Algorithm double m_lowerBound; double m_upperBound; + std::string m_bkgdType; + + bool m_doFitBackground; + // double mTolerance; /// Number of FWHM of range of peak to be removed. @@ -127,6 +131,8 @@ class DLLExport ProcessBackground : public API::Algorithm /// Add a certain region from a reference workspace void addRegion(); + + void fitBackgroundFunction(std::string bkgdfunctiontype); }; diff --git a/Code/Mantid/Framework/CurveFitting/src/AugmentedLagrangianOptimizer.cpp b/Code/Mantid/Framework/CurveFitting/src/AugmentedLagrangianOptimizer.cpp index 6209baa35689..cc134678e13b 100644 --- a/Code/Mantid/Framework/CurveFitting/src/AugmentedLagrangianOptimizer.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/AugmentedLagrangianOptimizer.cpp @@ -301,9 +301,11 @@ namespace Mantid } /** + * @param lambda + * @param mu + * @param rho * @param xcur The starting parameters for the limited unconstrained optimization. They will - * be updated as it proceeds - * @param d + * be updated as it proceeds */ void AugmentedLagrangianOptimizer::unconstrainedOptimization(const std::vector & lambda, const std::vector & mu, diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateGammaBackground.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateGammaBackground.cpp index 1f3c7f718168..abb94f850f09 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateGammaBackground.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateGammaBackground.cpp @@ -218,6 +218,7 @@ namespace Mantid void CalculateGammaBackground::calculateSpectrumFromDetector(const size_t inputIndex, const size_t outputIndex) { auto det = m_inputWS->getDetector(inputIndex); + const auto & pmap = m_inputWS->constInstrumentParameters(); auto detPos = det->getPos(); // -- Setup detector & resolution parameters -- @@ -225,15 +226,15 @@ namespace Mantid detPar.l1 = m_l1; detPar.l2 = m_samplePos.distance(detPos); detPar.theta = m_inputWS->detectorTwoTheta(det); //radians - detPar.t0 = getComponentParameter(*det, "t0")*1e-6; // seconds - detPar.efixed = getComponentParameter(*det,"efixed"); + detPar.t0 = ComptonProfile::getComponentParameter(det, pmap, "t0")*1e-6; // seconds + detPar.efixed = ComptonProfile::getComponentParameter(det, pmap,"efixed"); ResolutionParams detRes; - detRes.dl1 = getComponentParameter(*det, "sigma_l1"); // DL0 - detRes.dl2 = getComponentParameter(*det, "sigma_l2"); // DL1 - detRes.dthe = getComponentParameter(*det, "sigma_theta"); //DTH in radians - detRes.dEnGauss = getComponentParameter(*det, "sigma_gauss"); - detRes.dEnLorentz = getComponentParameter(*det, "hwhm_lorentz"); + detRes.dl1 = ComptonProfile::getComponentParameter(det, pmap, "sigma_l1"); // DL0 + detRes.dl2 = ComptonProfile::getComponentParameter(det, pmap, "sigma_l2"); // DL1 + detRes.dthe = ComptonProfile::getComponentParameter(det, pmap, "sigma_theta"); //DTH in radians + detRes.dEnGauss = ComptonProfile::getComponentParameter(det, pmap, "sigma_gauss"); + detRes.dEnLorentz = ComptonProfile::getComponentParameter(det, pmap, "hwhm_lorentz"); // Compute a time of flight spectrum convolved with a Voigt resolution function for each mass // at the detector point & sum to a single spectrum @@ -255,6 +256,7 @@ namespace Mantid void CalculateGammaBackground::calculateBackgroundFromFoils(const size_t inputIndex, const size_t outputIndex) { auto det = m_inputWS->getDetector(inputIndex); + const auto & pmap = m_inputWS->constInstrumentParameters(); auto detPos = det->getPos(); // -- Setup detector & resolution parameters -- @@ -262,15 +264,15 @@ namespace Mantid detPar.l1 = m_l1; detPar.l2 = m_samplePos.distance(detPos); detPar.theta = m_inputWS->detectorTwoTheta(det); //radians - detPar.t0 = getComponentParameter(*det, "t0")*1e-6; // seconds - detPar.efixed = getComponentParameter(*det,"efixed"); + detPar.t0 = ComptonProfile::getComponentParameter(det, pmap, "t0")*1e-6; // seconds + detPar.efixed = ComptonProfile::getComponentParameter(det, pmap,"efixed"); ResolutionParams detRes; - detRes.dl1 = getComponentParameter(*det, "sigma_l1"); // DL0 - detRes.dl2 = getComponentParameter(*det, "sigma_l2"); // DL1 - detRes.dthe = getComponentParameter(*det, "sigma_theta"); //DTH in radians - detRes.dEnGauss = getComponentParameter(*det, "sigma_gauss"); - detRes.dEnLorentz = getComponentParameter(*det, "hwhm_lorentz"); + detRes.dl1 = ComptonProfile::getComponentParameter(det, pmap, "sigma_l1"); // DL0 + detRes.dl2 = ComptonProfile::getComponentParameter(det, pmap, "sigma_l2"); // DL1 + detRes.dthe = ComptonProfile::getComponentParameter(det, pmap, "sigma_theta"); //DTH in radians + detRes.dEnGauss = ComptonProfile::getComponentParameter(det, pmap, "sigma_gauss"); + detRes.dEnLorentz = ComptonProfile::getComponentParameter(det, pmap, "hwhm_lorentz"); const size_t nxvalues = m_backgroundWS->blocksize(); std::vector foilSpectrum(nxvalues); @@ -510,6 +512,7 @@ namespace Mantid // foil geometry // there should be the same number in each position + const auto & pmap = m_inputWS->constInstrumentParameters(); auto foils0 = inst->getAllComponentsWithName("foil-pos0"); auto foils1 = inst->getAllComponentsWithName("foil-pos1"); const size_t nfoils = foils0.size(); @@ -534,16 +537,16 @@ namespace Mantid FoilInfo descr; descr.thetaMin = thetaRng0.first; descr.thetaMax = thetaRng0.second; - descr.lorentzWidth = getComponentParameter(*foil0, "hwhm_lorentz"); - descr.gaussWidth = getComponentParameter(*foil0, "sigma_gauss"); + descr.lorentzWidth = ComptonProfile::getComponentParameter(foil0, pmap, "hwhm_lorentz"); + descr.gaussWidth = ComptonProfile::getComponentParameter(foil0, pmap, "sigma_gauss"); m_foils0[i] = descr; //copy const auto & foil1 = foils1[i]; auto thetaRng1 = calculateThetaRange(foil1, m_foilRadius,refFrame->pointingHorizontal()); descr.thetaMin = thetaRng1.first; descr.thetaMax = thetaRng1.second; - descr.lorentzWidth = getComponentParameter(*foil1, "hwhm_lorentz"); - descr.gaussWidth = getComponentParameter(*foil1, "sigma_gauss"); + descr.lorentzWidth = ComptonProfile::getComponentParameter(foil1, pmap, "hwhm_lorentz"); + descr.gaussWidth = ComptonProfile::getComponentParameter(foil1, pmap, "sigma_gauss"); m_foils1[i] = descr; //copy } @@ -596,24 +599,5 @@ namespace Mantid return std::make_pair(theta - dtheta, theta + dtheta); } - /** - * @param comp A reference to the component that should contain the parameter - * @param name The name of the parameter - * @returns The value of the parameter if it exists - * @throws A std::invalid_argument error if the parameter does not exist - */ - double CalculateGammaBackground::getComponentParameter(const Geometry::IComponent & comp,const std::string &name) const - { - std::vector pars = comp.getNumberParameter(name); - if(!pars.empty()) - { - return pars[0]; - } - else - { - throw std::invalid_argument("CalculateGammaBackground - Cannot find component parameter \"" + name + "\"."); - } - } - } // namespace CurveFitting } // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/src/ComptonProfile.cpp b/Code/Mantid/Framework/CurveFitting/src/ComptonProfile.cpp index 0e9d10b26d93..698e7fefbd32 100644 --- a/Code/Mantid/Framework/CurveFitting/src/ComptonProfile.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/ComptonProfile.cpp @@ -3,7 +3,7 @@ //----------------------------------------------------------------------------- #include "MantidCurveFitting/ComptonProfile.h" #include "MantidAPI/FunctionFactory.h" - +#include "MantidGeometry/Instrument/DetectorGroup.h" #include namespace Mantid @@ -21,6 +21,47 @@ namespace CurveFitting ///@endcond } + /** + * If a DetectorGroup is encountered then the parameters are averaged over the group + * @param comp A pointer to the component that should contain the parameter + * @param pmap A reference to the ParameterMap that stores the parameters + * @param name The name of the parameter + * @returns The value of the parameter if it exists + * @throws A std::invalid_argument error if the parameter does not exist + */ + double ComptonProfile::getComponentParameter(const Geometry::IComponent_const_sptr & comp, const Geometry::ParameterMap &pmap, + const std::string &name) + { + if(!comp) throw std::invalid_argument("ComptonProfile - Cannot retrieve parameter from NULL component"); + + double result(0.0); + if(const auto group = boost::dynamic_pointer_cast(comp)) + { + const auto dets = group->getDetectors(); + double avg(0.0); + for(auto it = dets.begin(); it!= dets.end(); ++it) + { + auto param = pmap.getRecursive((*it)->getComponentID(), name); + if(param) avg += param->value(); + else throw std::invalid_argument("ComptonProfile - Unable to find DetectorGroup component parameter \"" + name + "\"."); + } + result = avg/static_cast(group->nDets()); + } + else + { + auto param = pmap.getRecursive(comp->getComponentID(), name); + if(param) + { + result = param->value(); + } + else + { + throw std::invalid_argument("ComptonProfile - Unable to find component parameter \"" + name + "\"."); + } + } + return result; + } + /** */ ComptonProfile::ComptonProfile() : API::ParamFunction(), API::IFunction1D(), @@ -89,18 +130,19 @@ namespace CurveFitting } DetectorParams detpar; + const auto & pmap = workspace->constInstrumentParameters(); detpar.l1 = sample->getDistance(*source); detpar.l2 = det->getDistance(*sample); detpar.theta = workspace->detectorTwoTheta(det); - detpar.t0 = getComponentParameter(*det,"t0")*1e-6; // Convert to seconds - detpar.efixed = getComponentParameter(*det,"efixed"); + detpar.t0 = getComponentParameter(det, pmap, "t0")*1e-6; // Convert to seconds + detpar.efixed = getComponentParameter(det, pmap, "efixed"); ResolutionParams respar; - respar.dl1 = getComponentParameter(*det, "sigma_l1"); - respar.dl2 = getComponentParameter(*det, "sigma_l2"); - respar.dthe = getComponentParameter(*det,"sigma_theta"); //radians - respar.dEnLorentz = getComponentParameter(*det, "hwhm_lorentz"); - respar.dEnGauss = getComponentParameter(*det, "sigma_gauss"); + respar.dl1 = getComponentParameter(det, pmap, "sigma_l1"); + respar.dl2 = getComponentParameter(det, pmap, "sigma_l2"); + respar.dthe = getComponentParameter(det, pmap, "sigma_theta"); //radians + respar.dEnLorentz = getComponentParameter(det, pmap, "hwhm_lorentz"); + respar.dEnGauss = getComponentParameter(det, pmap, "sigma_gauss"); this->cacheYSpaceValues(workspace->readX(m_wsIndex), workspace->isHistogramData(), detpar, respar); } @@ -304,25 +346,5 @@ namespace CurveFitting std::transform(voigt.begin(), voigt.end(), voigt.begin(), std::bind2nd(std::multiplies(), norm)); } - /** - * @param comp A reference to the component that should contain the parameter - * @param name The name of the parameter - * @returns The value of the parameter if it exists - * @throws A std::invalid_argument error if the parameter does not exist - */ - double ComptonProfile::getComponentParameter(const Geometry::IComponent & comp,const std::string &name) const - { - std::vector pars = comp.getNumberParameter(name); - if(!pars.empty()) - { - return pars[0]; - } - else - { - throw std::invalid_argument("ComptonProfile - Unable to find component parameter \"" + name + "\"."); - } - } - - } // namespace CurveFitting } // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/src/ComptonScatteringCountRate.cpp b/Code/Mantid/Framework/CurveFitting/src/ComptonScatteringCountRate.cpp index a36f2b5ac822..85700dcb797f 100644 --- a/Code/Mantid/Framework/CurveFitting/src/ComptonScatteringCountRate.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/ComptonScatteringCountRate.cpp @@ -307,7 +307,6 @@ namespace CurveFitting } /** - * @param funcIndex Index within composite * @param profile Function of type ComptonProfile * @param paramsOffset The offset of the given function's parameters within composite */ @@ -325,7 +324,6 @@ namespace CurveFitting } /** - * @param funcIndex Index within composite * @param function1D Function of type IFunction1D * @param paramsOffset The offset of the given function's parameters within composite */ diff --git a/Code/Mantid/Framework/CurveFitting/src/CubicSpline.cpp b/Code/Mantid/Framework/CurveFitting/src/CubicSpline.cpp index 0f5d09f84506..7b116f6a4c3a 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CubicSpline.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CubicSpline.cpp @@ -190,9 +190,8 @@ namespace Mantid /**Evaluate a point on the spline. Includes basic error handling * - * @param out :: The array to store the calculated values - * @param xValues :: The array of x values we wish to interpolate - * @param nData :: The size of the arrays + * @param x :: Point to evaluate + * @return :: the value of the spline at the given point */ double CubicSpline::splineEval(const double x) const { diff --git a/Code/Mantid/Framework/CurveFitting/src/DiffRotDiscreteCircle.cpp b/Code/Mantid/Framework/CurveFitting/src/DiffRotDiscreteCircle.cpp new file mode 100644 index 000000000000..f5f3a6344cc2 --- /dev/null +++ b/Code/Mantid/Framework/CurveFitting/src/DiffRotDiscreteCircle.cpp @@ -0,0 +1,235 @@ +/*WIKI* +== Summary == + +This fitting function models the dynamics structure factor of a particle undergoing discrete jumps on N-sites +evenly distributed in a circle. The particle can only jump to neighboring sites. +This is the most common type of discrete rotational diffusion in a circle. + +Markov model for jumps between neighboring sites: + +
\frac{d}{dt} p_j(t) = \frac{1}{\tau} [p_{j-1}(t) -2 p_j(t) + p_{j+1}(t)]
+ +The Decay fitting parameter \tau is the inverse of the transition rate. This, along with the circle radius r, conform the two fundamental fitting parameters of the structure factor S(Q,E): + +
S(Q,E) \equiv = \int e^{-iEt/\hbar} I(Q,t) dt = A_0(Q,r) \delta (E) + \frac{1}{\pi} \sum_{l=1}^{N-1} A_l (Q,r) \frac{\hbar \tau_l^{-1}}{(\hbar \tau_l^{-1})^2+E^2}
+ +
A_l(Q,r) = \frac{1}{N} \sum_{k=1}^{N} j_0( 2 Q r sin(\frac{\pi k}{N}) ) cos(\frac{2\pi lk}{N})
+ +
\tau_l^{-1} = 4 \tau^{-1} sin^2(\frac{\pi l}{N})
+ +The transition rate, expressed in units of energy is h\tau^{-1}, with h = 4.135665616 meV THz. + +== Example: Methyl Rotations == +Methyl Rotations can be modelled setting N=3. In this case, the inelastic part reduces to a single Lorentzian: + +
S(Q,E) = A_0(Q,r) \delta (E) + \frac{2}{\pi} A_1 (Q,r) \frac{3 \hbar \tau^{-1}}{(3 \hbar \tau^{-1})^2+E^2}
+ +If, alternatively, one models these dynamics using the [[Lorentzian]] function provided in Mantid: + +
S(Q,E) = A \delta (\omega) + \frac{B}{\pi} \left( \frac{\frac{\Gamma}{2}}{(\frac{\Gamma}{2})^2 + (\hbar\omega)^2}\right)
+Then: +
B = \frac{1}{\pi}h A_1
+
\Gamma = \frac{3}{\pi} h\tau^{-1} = 3.949269754 meV\cdot THz\cdot \tau^{-1}
+ +== Properties == + +{| border="1" cellpadding="5" cellspacing="0" +!Order +!Name +!Default +!Description +|- +|1 +|Intensity +|1.0 +|Intensity of the peak [arbitrary units] +|- +|2 +|Radius +|1.0 +|Circle radius [Angstroms] +|- +|3 +|Decay +|1.0 +|inverse of the transition rate (ps if energy in meV; ns if energy in \mueV) +|} + +[[Category:Fit_functions]] +*WIKI*/ + +//---------------------------------------------------------------------- +// Includes +//---------------------------------------------------------------------- +#include +#include "MantidCurveFitting/DiffRotDiscreteCircle.h" +#include "MantidCurveFitting/BoundaryConstraint.h" +#include "MantidAPI/FunctionFactory.h" +#include +#include "MantidAPI/ParameterTie.h" + +namespace Mantid +{ +namespace CurveFitting +{ + +DECLARE_FUNCTION(ElasticDiffRotDiscreteCircle); +DECLARE_FUNCTION(InelasticDiffRotDiscreteCircle); +DECLARE_FUNCTION(DiffRotDiscreteCircle); + +ElasticDiffRotDiscreteCircle::ElasticDiffRotDiscreteCircle(){ + //declareParameter("Height", 1.0); //parameter "Height" already declared in constructor of base class DeltaFunction + declareParameter( "Radius", 1.0, "Circle radius [Angstroms] " ); + declareAttribute( "Q", API::IFunction::Attribute(0.5) ); + declareAttribute( "N", API::IFunction::Attribute(3) ); +} + +void ElasticDiffRotDiscreteCircle::init() +{ + // Ensure positive values for Height and Radius + BoundaryConstraint* HeightConstraint = new BoundaryConstraint( this, "Height", std::numeric_limits::epsilon(), true ); + addConstraint( HeightConstraint ); + + BoundaryConstraint* RadiusConstraint = new BoundaryConstraint( this, "Radius", std::numeric_limits::epsilon(), true ); + addConstraint( RadiusConstraint ); +} + +double ElasticDiffRotDiscreteCircle::HeightPrefactor() const{ + const double R = getParameter( "Radius" ); + const double Q = getAttribute( "Q" ).asDouble(); + const int N = getAttribute( "N" ).asInt(); + double aN = 0; + for ( int k = 1; k < N; k++ ) + { + double x = 2 * Q * R * sin( M_PI * k / N ); + aN += sin( x ) / x; // spherical Besell function of order zero j0==sin(x)/x + } + aN += 1; // limit for j0 when k==N, or x==0 + return aN / N; +} + +InelasticDiffRotDiscreteCircle::InelasticDiffRotDiscreteCircle() : m_hbar(0.658211626) +{ + declareParameter( "Intensity",1.0, "scaling factor [arbitrary units]" ); + declareParameter( "Radius", 1.0, "Circle radius [Angstroms]" ); + declareParameter( "Decay", 1.0, "Inverse of transition rate, in nanoseconds if energy in micro-ev, or picoseconds if energy in mili-eV" ); + + declareAttribute( "Q", API::IFunction::Attribute( 0.5 ) ); + declareAttribute( "N", API::IFunction::Attribute( 3 ) ); +} + +void InelasticDiffRotDiscreteCircle::init() +{ + // Ensure positive values for Intensity, Radius, and decay + BoundaryConstraint* IntensityConstraint = new BoundaryConstraint( this, "Intensity", std::numeric_limits< double >::epsilon(), true ); + addConstraint(IntensityConstraint); + + BoundaryConstraint* RadiusConstraint = new BoundaryConstraint( this, "Radius", std::numeric_limits< double >::epsilon(), true ); + addConstraint( RadiusConstraint ); + + BoundaryConstraint* DecayConstraint = new BoundaryConstraint( this, "Decay", std::numeric_limits< double >::epsilon(), true ); + addConstraint( DecayConstraint ); +} + +void InelasticDiffRotDiscreteCircle::function1D( double *out, const double* xValues, const size_t nData ) const +{ + const double I = getParameter( "Intensity" ); + const double R = getParameter( "Radius" ); + const double rate = m_hbar / getParameter( "Decay" ); // micro-eV or mili-eV + const double Q = getAttribute( "Q" ).asDouble(); + const int N = getAttribute( "N" ).asInt(); + + + std::vector sph( N ); + for ( int k = 1; k < N; k++ ) + { + double x = 2 * Q * R * sin( M_PI * k / N ); + sph[ k ] = sin( x ) / x; // spherical Besell function of order zero 'j0' is sin(x)/x + } + + std::vector ratel( N ); + for ( int l = 1; l < N; l++) // l goes up to N-1 + { + ratel[ l ] = rate * 4 * pow( sin( M_PI * l / N ), 2 ); // notice that 0 < l/N < 1 + } + + for ( size_t i = 0; i < nData; i++ ) + { + double w = xValues[ i ]; + double S = 0.0; + for ( int l = 1; l < N; l++ ) // l goes up to N-1 + { + double lorentzian = ratel[ l ] / ( ratel[ l ] * ratel[ l ] + w * w ); + double al = 0.0; + for ( int k = 1; k < N; k++ ) // case k==N after the loop + { + double y = 2 * M_PI * l * k / N; + al += cos( y ) * sph[ k ]; + } + al += 1; // limit for j0 when k==N, or x==0 + al /= N; + S += al * lorentzian; + } + out[ i ] = I * S / M_PI; + } + +} + +/* Propagate the attribute to its member functions. + * NOTE: we pass this->getAttribute(name) by reference, thus the same + * object is shared by the composite function and its members. + */ +void DiffRotDiscreteCircle::trickleDownAttribute( const std::string & name ) +{ + for ( size_t iFun = 0; iFun < nFunctions(); iFun++ ) + { + API::IFunction_sptr fun = getFunction( iFun ); + if( fun -> hasAttribute( name ) ) + fun -> setAttribute( name, this -> getAttribute( name ) ); + } +} + +/* Same as parent except we overwrite attributes of member functions + * having the same name + */ +void DiffRotDiscreteCircle::declareAttribute( const std::string & name, const API::IFunction::Attribute & defaultValue ) +{ + API::ImmutableCompositeFunction::declareAttribute( name, defaultValue ); + trickleDownAttribute( name ); +} + +/* Same as parent except we overwrite attributes of member functions + * having the same name + */ +void DiffRotDiscreteCircle::setAttribute( const std::string& name, const Attribute& att ) +{ + API::ImmutableCompositeFunction::setAttribute( name, att ); + trickleDownAttribute( name ); +} + +//DiffRotDiscreteCircle::DiffRotDiscreteCircle() +void DiffRotDiscreteCircle::init() +{ + m_elastic = boost::dynamic_pointer_cast( API::FunctionFactory::Instance().createFunction( "ElasticDiffRotDiscreteCircle" ) ); + addFunction( m_elastic ); + m_inelastic = boost::dynamic_pointer_cast( API::FunctionFactory::Instance().createFunction( "InelasticDiffRotDiscreteCircle" ) ); + addFunction( m_inelastic ); + + setAttributeValue( "NumDeriv", true ); + + declareAttribute( "Q", API::IFunction::Attribute( 0.5 ) ); + declareAttribute( "N", API::IFunction::Attribute( 3 ) ); + + //Set the aliases + setAlias( "f1.Intensity", "Intensity" ); + setAlias( "f1.Radius", "Radius" ); + setAlias( "f1.Decay", "Decay" ); + + //Set the ties between Elastic and Inelastic parameters + addDefaultTies( "f0.Height=f1.Intensity,f0.Radius=f1.Radius" ); + applyTies(); + +} + +} // namespace CurveFitting +} // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/src/DiffSphere.cpp b/Code/Mantid/Framework/CurveFitting/src/DiffSphere.cpp index bb4fae91cacd..71dcea6583de 100644 --- a/Code/Mantid/Framework/CurveFitting/src/DiffSphere.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/DiffSphere.cpp @@ -1,14 +1,62 @@ +/*WIKI* +== Summary == + +This fitting function models the dynamics structure factor of a particle undergoing continuous diffusion but confined to a spherical volume. According to Volino and Dianoux +[http://apps.webofknowledge.com/InboundService.do?SID=4Bayo9ujffV3CUc9Qx8&product=WOS&UT=A1980KQ74800002&SrcApp=EndNote&DestFail=http%3A%2F%2Fwww.webofknowledge.com&Init=Yes&action=retrieve&Func=Frame&customersID=ResearchSoft&SrcAuth=ResearchSoft&IsProductCode=Yes&mode=FullRecord], + +
+S(Q,E\equiv \hbar \omega) = A_{0,0}(Q\cdot R) \delta (\omega) + \frac{1}{\pi} \sum_{l=1}^{N-1} (2l+1) A_{n,l} (Q\cdot R) \frac{x_{n,l}^2 D/R^2}{[x_{n,l}^2 D/R^2]^21+\omega^2}, + +A_{n,l} = \frac{6x_{n,l}^2}{x_{n,l}^2-l(l+1)} [\frac{QRj_{l+1}(QR) - lj_l(QR)}{(QR)^2 - x_{n,l}^2}]^2 +
+ +Because of the spherical symmetry of the problem, the structure factor is expressed in terms of the j_l(z) spherical Bessel functions. Furthermore, the requirement that no particle flux can escape the sphere leads to the following boundary condition[http://apps.webofknowledge.com/InboundService.do?SID=4Bayo9ujffV3CUc9Qx8&product=WOS&UT=A1980KQ74800002&SrcApp=EndNote&DestFail=http%3A%2F%2Fwww.webofknowledge.com&Init=Yes&action=retrieve&Func=Frame&customersID=ResearchSoft&SrcAuth=ResearchSoft&IsProductCode=Yes&mode=FullRecord]: + +
\frac{d}{dr}j_l(rx_{n,l}/R)|_{r=R}=0 \,\,\,\, \forall l
+ +The roots of this set of equations are the numerical coefficients x_{n,l} . + +The fit function DiffSphere has an elastic part, modelled by fitting function ElasticDiffSphere and an inelastic part, modelled by InelasticDiffSphere. + +== Properties == + +{| border="1" cellpadding="5" cellspacing="0" +!Order +!Name +!Default +!Description +|- +|1 +|Intensity +|1.0 +|Intensity of the peak [arbitrary units] +|- +|2 +|Radius +|2.0 +|Sphere radius [Ã…] +|- +|3 +|Diffusion +|0.05 +|Diffusion constant [Ã…{}^2/ps \equiv 10 \cdot (10^{-5} cm^2/s)] +|} + +[[Category:Fit_functions]] +*WIKI*/ + //---------------------------------------------------------------------- // Includes //---------------------------------------------------------------------- +#include +#include + #include "MantidCurveFitting/DiffSphere.h" #include "MantidCurveFitting/BoundaryConstraint.h" #include "MantidAPI/FunctionFactory.h" -#include #include #include "MantidAPI/ParameterTie.h" - #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -21,61 +69,270 @@ namespace CurveFitting using namespace Kernel; using namespace API; +DECLARE_FUNCTION(ElasticDiffSphere); +DECLARE_FUNCTION(InelasticDiffSphere); DECLARE_FUNCTION(DiffSphere); +ElasticDiffSphere::ElasticDiffSphere() +{ + //declareParameter("Height", 1.0); //parameter "Height" already declared in constructor of base class DeltaFunction + declareParameter( "Radius", 2.0, "Sphere radius" ); + declareAttribute( "Q", API::IFunction::Attribute( 1.0 ) ); +} + +void ElasticDiffSphere::init() +{ + // Ensure positive values for Height and Radius + BoundaryConstraint* HeightConstraint = new BoundaryConstraint( this, "Height" , std::numeric_limits::epsilon() , true ); + addConstraint( HeightConstraint ); + + BoundaryConstraint* RadiusConstraint = new BoundaryConstraint( this, "Radius", std::numeric_limits::epsilon() , true ); + addConstraint( RadiusConstraint ); +} + +double ElasticDiffSphere::HeightPrefactor() const +{ + const double R = getParameter( "Radius" ); + const double Q = getAttribute( "Q" ).asDouble(); + + // Penalize negative parameters + if ( R < std::numeric_limits::epsilon() ) + { + return std::numeric_limits::infinity(); + } + + return pow( 3 * boost::math::sph_bessel( 1, Q * R ) / ( Q * R ), 2 ); +} + +// initialize class attribute m_xnl with a list of coefficients in string format +void InelasticDiffSphere::initXnlCoeff(){ + /* List of 98 coefficients sorted by increasing value (F.Volino,Mol. Phys. 41,271-279,1980) + * For each coefficient, the triad (coeff, l, n) is defined + */ + size_t ncoeff = 98; + + double xvalues[] = + { + 2.081576, 3.342094, 4.493409, 4.514100, 5.646704, 5.940370, 6.756456, 7.289932, + 7.725252, 7.851078, 8.583755, 8.934839, 9.205840, 9.840446, 10.010371, 10.613855, + 10.904122, 11.070207, 11.079418, 11.972730, 12.143204, 12.279334, 12.404445, + 13.202620, 13.295564, 13.472030, 13.846112, 14.066194, 14.258341, 14.590552, + 14.651263, 15.244514, 15.310887, 15.579236, 15.819216, 15.863222, 16.360674, + 16.609346, 16.977550, 17.042902, 17.117506, 17.220755, 17.408034, 17.947180, + 18.127564, 18.356318, 18.453241, 18.468148, 18.742646, 19.262710, 19.270294, + 19.496524, 19.581889, 19.862424, 20.221857, 20.371303, 20.406581, 20.538074, + 20.559428, 20.795967, 21.231068, 21.537120, 21.578053, 21.666607, 21.840012, + 21.899697, 21.999955, 22.578058, 22.616601, 22.662493, 23.082796, 23.106568, + 23.194996, 23.390490, 23.519453, 23.653839, 23.783192, 23.906450, 24.360789, + 24.382038, 24.474825, 24.689873, 24.850085, 24.899636, 25.052825, 25.218652, + 25.561873, 25.604057, 25.724794, 25.846084, 26.012188, 26.283265, 26.516603, + 26.552589, 26.666054, 26.735177, 26.758685, 26.837518 + }; + + size_t lvalues[] = + { + 1, 2, 0, 3, 4, 1, 5, 2, 0, 6, 3, 7, 1, 4, 8, 2, 0, 5, 9, 3, 10, 6, 1, 11, 4, 7, + 2, 0, 12, 5, 8, 3, 13, 1, 9, 6, 14, 4, 10, 2, 7, 0, 15, 5, 11, 8, 16, 3, 1, 6, 12, + 17, 9, 4, 2, 0, 13, 18, 7, 10, 5, 14, 19, 3, 8, 1, 11, 6, 20, 15, 4, 9, 12, 2, 0, + 21, 16, 7, 10, 13, 5, 22, 3, 17, 1, 8, 14, 11, 23, 6, 18, 4, 9, 2, 0, 15, 24, 12 + }; + + size_t nvalues[] = + { + 0, 0, 1, 0, 0, 1, 0, 1, 2, 0, 1, 0, 2, 1, 0, 2, 3, 1, 0, 2, 0, 1, 3, 0, 2, 1, 3, + 4, 0, 2, 1, 3, 0, 4, 1, 2, 0, 3, 1, 4, 2, 5, 0, 3, 1, 2, 0, 4, 5, 3, 1, 0, 2, 4, 5, + 6, 1, 0, 3, 2, 4, 1, 0, 5, 3, 6, 2, 4, 0, 1, 5, 3, 2, 6, 7, 0, 1, 4, 3, 2, 5, 0, + 6, 1, 7, 4, 2, 3, 0, 5, 1, 6, 4, 7, 8, 2, 0, 3 + }; + + for( size_t i = 0; i < ncoeff; i ++ ) + { + xnlc coeff; + coeff.x = xvalues[ i ]; + coeff.l = lvalues[ i ]; + coeff.n = nvalues[ i ]; + m_xnl.push_back( coeff ); + } +} + +/// Initialize a set of coefficients that will remain constant during fitting +void InelasticDiffSphere::initAlphaCoeff(){ + for( std::vector< xnlc >::const_iterator it = m_xnl.begin(); it != m_xnl.end(); ++it ) + { + double x = it->x; // eigenvalue for a (n, l) pair + double l = ( double )( it->l ); + m_alpha.push_back( ( 2.0 * l + 1 ) * 6.0 * x*x / ( x*x - l*(l + 1) ) ); + } +} + +/* Factor "J" is defined as [Q*a*j(l+1,Q*a) - l*j(l,Q*a)] / [(Q*a)^2 - x^2] + * Both numerator and denominator goes to zero when Q*a approaches x, giving rise to + * numerical indeterminacies. To avoid them, we will interpolate linearly. + */ +void InelasticDiffSphere::initLinJlist() +{ + for( size_t i = 0; i < m_xnl.size(); i++ ) + { + linearJ abJ; + double x = m_xnl[ i ].x; // eigenvalue for a (n, l) pair + unsigned int l = ( unsigned int )( m_xnl[ i ].l ); + double Qa = x - m_divZone; //left of the numerical divergence point + double J0 = ( Qa * boost::math::sph_bessel( l + 1, Qa ) - l * boost::math::sph_bessel( l, Qa ) ) / ( Qa*Qa - x*x ); + Qa = x + m_divZone; //right of the numerical divergence point + double J1 = ( Qa * boost::math::sph_bessel( l + 1, Qa ) - l * boost::math::sph_bessel( l, Qa ) ) / ( Qa*Qa - x*x ); + abJ.slope = ( J1 - J0 ) / ( 2 * m_divZone ); //slope of the linear interpolation + abJ.intercept = J0 - abJ.slope * ( x - m_divZone ); //intercept of the linear interpolation + m_linearJlist.push_back( abJ ); //store the parameters of the linear interpolation for this it->x + } +} + +InelasticDiffSphere::InelasticDiffSphere() : m_lmax( 24 ), m_divZone( 0.1 ), m_hbar(0.658211626) +{ + declareParameter( "Intensity", 1.0, "scaling factor" ); + declareParameter( "Radius", 2.0, "Sphere radius, in Angstroms" ); + declareParameter( "Diffusion", 0.05, "Diffusion coefficient, in units of A^2*THz, if energy in meV, or A^2*PHz if energy in ueV" ); + + declareAttribute( "Q", API::IFunction::Attribute( 1.0 ) ); +} + +/// Initialize a set of coefficients and terms that are invariant during fitting +void InelasticDiffSphere::init() +{ + initXnlCoeff(); // initialize m_xnl with the list of coefficients xnlist + initAlphaCoeff(); // initialize m_alpha, certain factors constant over the fit + initLinJlist(); // initialize m_linearJlist, linear interpolation around numerical divergence +} + +/// Calculate the (2l+1)*A_{n,l} coefficients for each Lorentzian +std::vector< double > InelasticDiffSphere::LorentzianCoefficients( double a ) const +{ + //precompute the 2+m_lmax spherical bessel functions (26 in total) + std::vector< double > jl( 2 + m_lmax ); + for( size_t l = 0; l < 2 + m_lmax; l++ ) + { + jl[ l ] = boost::math::sph_bessel( ( unsigned int )( l ), a ); + } + + //store the coefficient of each Lorentzian in vector YJ(a,w) + size_t ncoeff = m_xnl.size(); + std::vector< double > YJ( ncoeff ); + + for( size_t i = 0; i < ncoeff; i++ ) + { + double x = m_xnl[ i ].x; + unsigned int l = ( unsigned int )( m_xnl[ i ].l ); + + double J; + if( fabs( a - x ) > m_divZone ) + { + J = ( a * jl[l + 1] - l * jl[l] ) / ( a*a - x*x ); + } + else + { + J = m_linearJlist[ i ].slope * a + m_linearJlist[ i ].intercept; // linear interpolation instead + } + + YJ[ i ] = m_alpha[ i ] * ( J * J ); + } + + return YJ; +} // end of LorentzianCoefficients + + +void InelasticDiffSphere::function1D( double* out, const double* xValues, const size_t nData ) const +{ + const double I = getParameter( "Intensity" ); + const double R = getParameter( "Radius" ); + const double D = getParameter( "Diffusion" ); + const double Q = getAttribute( "Q" ).asDouble(); + + // // Penalize negative parameters + if ( I < std::numeric_limits::epsilon() || + R < std::numeric_limits::epsilon() || + D < std::numeric_limits::epsilon() ) + { + for (size_t i = 0; i < nData; i++) + { + out[ i ] = std::numeric_limits::infinity(); + } + return; + } + + // List of Lorentzian HWHM + std::vector< double > HWHM; + size_t ncoeff = m_xnl.size(); + for ( size_t n = 0; n < ncoeff; n++ ) + { + double x = m_xnl[ n ].x; // eigenvalue + HWHM.push_back( m_hbar * x * x * D / ( R * R ) ); + } + + std::vector< double > YJ; + YJ = LorentzianCoefficients( Q * R ); // The (2l+1)*A_{n,l} + for (size_t i = 0; i < nData; i++) + { + double energy = xValues[ i ]; // from meV to THz (or from micro-eV to PHz) + out[ i ] = 0.0; + for ( size_t n = 0; n < ncoeff; n++ ) + { + double L = ( 1.0 / M_PI ) * HWHM[n] / ( HWHM[n] * HWHM[n] + energy * energy ); //Lorentzian + out[i] += I * YJ[ n ] * L; + } + } +} + /* Propagate the attribute to its member functions. * NOTE: we pass this->getAttribute(name) by reference, thus the same * object is shared by the composite function and its members. */ void DiffSphere::trickleDownAttribute( const std::string& name ) { - for(size_t iFun=0;iFunhasAttribute( name ) ) - fun->setAttribute( name, this->getAttribute(name) ); + { + fun->setAttribute( name, this->getAttribute( name ) ); + } } } /* Same as parent except we overwrite attributes of member functions * having the same name */ -void DiffSphere::declareAttribute(const std::string & name,const API::IFunction::Attribute & defaultValue) +void DiffSphere::declareAttribute( const std::string & name, const API::IFunction::Attribute & defaultValue ) { - API::ImmutableCompositeFunction::declareAttribute(name, defaultValue); + API::ImmutableCompositeFunction::declareAttribute( name, defaultValue ); trickleDownAttribute( name ); } /* Same as parent except we overwrite attributes of member functions * having the same name */ -void DiffSphere::setAttribute(const std::string& name,const Attribute& att) +void DiffSphere::setAttribute( const std::string & name, const Attribute & att ) { API::ImmutableCompositeFunction::setAttribute( name, att ); trickleDownAttribute( name ); } -DiffSphere::DiffSphere() +void DiffSphere::init() { - m_elastic = boost::dynamic_pointer_cast(API::FunctionFactory::Instance().createFunction("ElasticDiffSphere")); + m_elastic = boost::dynamic_pointer_cast< ElasticDiffSphere >( API::FunctionFactory::Instance().createFunction( "ElasticDiffSphere" ) ); addFunction( m_elastic ); - m_inelastic = boost::dynamic_pointer_cast(API::FunctionFactory::Instance().createFunction("InelasticDiffSphere")); + m_inelastic = boost::dynamic_pointer_cast< InelasticDiffSphere >( API::FunctionFactory::Instance().createFunction( "InelasticDiffSphere" ) ); addFunction( m_inelastic ); - this->setAttributeValue("NumDeriv", true ); - - this->declareAttribute("Q", API::IFunction::Attribute(1.0) ); + setAttributeValue( "NumDeriv", true ); + declareAttribute( "Q", API::IFunction::Attribute( 1.0 ) ); //Set the aliases - this->setAlias("f0.Height","elasticHeight"); - this->setAlias("f0.Radius","elasticRadius"); - this->setAlias("f1.Intensity","Intensity"); - this->setAlias("f1.Radius","Radius"); - this->setAlias("f1.Diffusion","Diffusion"); + setAlias( "f1.Intensity", "Intensity" ); + setAlias( "f1.Radius", "Radius" ); + setAlias( "f1.Diffusion", "Diffusion" ); //Set the ties between Elastic and Inelastic parameters - this->addDefaultTies("f0.Height=f1.Intensity,f0.Radius=f1.Radius"); - this->applyTies(); + addDefaultTies( "f0.Height=f1.Intensity,f0.Radius=f1.Radius" ); + applyTies(); } } // namespace CurveFitting diff --git a/Code/Mantid/Framework/CurveFitting/src/ElasticDiffSphere.cpp b/Code/Mantid/Framework/CurveFitting/src/ElasticDiffSphere.cpp deleted file mode 100644 index 3995f2616e2c..000000000000 --- a/Code/Mantid/Framework/CurveFitting/src/ElasticDiffSphere.cpp +++ /dev/null @@ -1,48 +0,0 @@ -//---------------------------------------------------------------------- -// Includes -//---------------------------------------------------------------------- -#include "MantidCurveFitting/ElasticDiffSphere.h" -#include "MantidCurveFitting/BoundaryConstraint.h" -#include "MantidAPI/FunctionFactory.h" -#include -#include -#include "MantidAPI/ParameterTie.h" - - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -namespace Mantid -{ -namespace CurveFitting -{ - -using namespace Kernel; -using namespace API; - -DECLARE_FUNCTION(ElasticDiffSphere); - -ElasticDiffSphere::ElasticDiffSphere(){ - //declareParameter("Height", 1.0); //parameter "Height" already declared in constructor of base class DeltaFunction - declareParameter("Radius", 1.0, "Sphere radius"); - - // Ensure positive values for Height and Radius - BoundaryConstraint* HeightConstraint = new BoundaryConstraint(this,"Height",0,true); - addConstraint(HeightConstraint); - - BoundaryConstraint* RadiusConstraint = new BoundaryConstraint(this,"Radius",0,true); - addConstraint(RadiusConstraint); - - declareAttribute( "Q", API::IFunction::Attribute(1.0) ); - -} - -double ElasticDiffSphere::HeightPrefactor() const{ - const double R = getParameter("Radius"); - const double Q = getAttribute("Q").asDouble(); - return pow(3*boost::math::sph_bessel(1,Q*R)/(Q*R),2); -} - -} // namespace CurveFitting -} // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/src/InelasticDiffSphere.cpp b/Code/Mantid/Framework/CurveFitting/src/InelasticDiffSphere.cpp deleted file mode 100644 index 290303fb3d08..000000000000 --- a/Code/Mantid/Framework/CurveFitting/src/InelasticDiffSphere.cpp +++ /dev/null @@ -1,250 +0,0 @@ -//---------------------------------------------------------------------- -// Includes -//---------------------------------------------------------------------- -#include "MantidCurveFitting/DiffSphere.h" -#include "MantidCurveFitting/BoundaryConstraint.h" -#include "MantidAPI/FunctionFactory.h" -#include -#include -#include "MantidAPI/ParameterTie.h" - - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -namespace Mantid -{ -namespace CurveFitting -{ - -using namespace Kernel; -using namespace API; - -DECLARE_FUNCTION(InelasticDiffSphere); - -// initialize class attribute xnl with a list of coefficients in string format -void InelasticDiffSphere::initXnlCoeff(){ - /* List of 98 coefficients sorted by increasing value (F.Volino,Mol. Phys. 41,271-279,1980) - * For each coefficient, the triad (coeff, l, n) is defined - */ - ncoeff = 98; - double xnlist[]={ - 2.081576, 1, 0, 3.342094, 2, 0, 4.493409, 0, 1, 4.514100, 3, 0, - 5.646704, 4, 0, 5.940370, 1, 1, 6.756456, 5, 0, 7.289932, 2, 1, - 7.725252, 0, 2, 7.851078, 6, 0, 8.583755, 3, 1, 8.934839, 7, 0, - 9.205840, 1, 2, 9.840446, 4, 1, 10.010371, 8, 0, 10.613855, 2, 2, - 10.904122, 0, 3, 11.070207, 5, 1, 11.079418, 9, 0, 11.972730, 3, 2, - 12.143204, 10, 0, 12.279334, 6, 1, 12.404445, 1, 3, 13.202620, 11, 0, - 13.295564, 4, 2, 13.472030, 7, 1, 13.846112, 2, 3, 14.066194, 0, 4, - 14.258341, 12, 0, 14.590552, 5, 2, 14.651263, 8, 1, 15.244514, 3, 3, - 15.310887, 13, 0, 15.579236, 1, 4, 15.819216, 9, 1, 15.863222, 6, 2, - 16.360674, 14, 0, 16.609346, 4, 3, 16.977550, 10, 1, 17.042902, 2, 4, - 17.117506, 7, 2, 17.220755, 0, 5, 17.408034, 15, 0, 17.947180, 5, 3, - 18.127564, 11, 1, 18.356318, 8, 2, 18.453241, 16, 0, 18.468148, 3, 4, - 18.742646, 1, 5, 19.262710, 6, 3, 19.270294, 12, 1, 19.496524, 17, 0, - 19.581889, 9, 2, 19.862424, 4, 4, 20.221857, 2, 5, 20.371303, 0, 6, - 20.406581, 13, 1, 20.538074, 18, 0, 20.559428, 7, 3, 20.795967, 10, 2, - 21.231068, 5, 4, 21.537120, 14, 1, 21.578053, 19, 0, 21.666607, 3, 5, - 21.840012, 8, 3, 21.899697, 1, 6, 21.999955, 11, 2, 22.578058, 6, 4, - 22.616601, 20, 0, 22.662493, 15, 1, 23.082796, 4, 5, 23.106568, 9, 3, - 23.194996, 12, 2, 23.390490, 2, 6, 23.519453, 0, 7, 23.653839, 21, 0, - 23.783192, 16, 1, 23.906450, 7, 4, 24.360789, 10, 3, 24.382038, 13, 2, - 24.474825, 5, 5, 24.689873, 22, 0, 24.850085, 3, 6, 24.899636, 17, 1, - 25.052825, 1, 7, 25.218652, 8, 4, 25.561873, 14, 2, 25.604057, 11, 3, - 25.724794, 23, 0, 25.846084, 6, 5, 26.012188, 18, 1, 26.283265, 4, 6, - 26.516603, 9, 4, 26.552589, 2, 7, 26.666054, 0, 8, 26.735177, 15, 2, - 26.758685, 24, 0, 26.837518, 12, 3}; - - for(unsigned int i=0; i::const_iterator it=xnl.begin(); it!=xnl.end(); ++it){ - double x = it->x; - double l = (double)(it->l); - alpha.push_back( (2.0*l+1) * ( 6.0*x*x/(x*x-l*(l+1)) ) / M_PI ); - } -} - -//initialize linear interpolation of factor J around its numerical divergence point a = it->x -void InelasticDiffSphere::initLinJlist(){ - for(std::vector::const_iterator it = xnl.begin(); it != xnl.end(); ++it){ - linearJ abJ; - double x = it->x; - unsigned int l = it->l; - double a = x - m_divZone; //left of the numerical divergence point - double J0 = ( a * boost::math::sph_bessel(l+1,a) - l * boost::math::sph_bessel(l,a) ) / (a*a - x*x); - a = x + m_divZone; //right of the numerical divergence point - double J1 = ( a * boost::math::sph_bessel(l+1,a) - l * boost::math::sph_bessel(l,a) ) / (a*a - x*x); - abJ.slope = (J1 - J0) / (2 * m_divZone); //slope of the linear interpolation - abJ.intercept = J0 - abJ.slope * (x - m_divZone); //intercept of the linear interpolation - linearJlist.push_back(abJ); //store the parameters of the linear interpolation for this it->x - } -} - -InelasticDiffSphere::InelasticDiffSphere() : lmax(24), m_divZone(0.1) { - declareParameter("Intensity",1.0, "scaling factor"); - declareParameter("Radius", 1.0, "Sphere radius"); - declareParameter("Diffusion", 1.0, "Diffusion coefficient, in units of"); - - declareAttribute( "Q", API::IFunction::Attribute(1.0) ); - - // Ensure positive values for Intensity, Radius, and Diffusion coefficient - BoundaryConstraint* IntensityConstraint = new BoundaryConstraint(this,"Intensity",0,true); - addConstraint(IntensityConstraint); - - BoundaryConstraint* RadiusConstraint = new BoundaryConstraint(this,"Radius",0,true); - addConstraint(RadiusConstraint); - - BoundaryConstraint* DiffusionConstraint = new BoundaryConstraint(this,"Diffusion",0,true); - addConstraint(DiffusionConstraint); - - initXnlCoeff(); // initialize this->xnl with the list of coefficients xnlist - initAlphaCoeff(); // initialize this->alpha, certain factors constant over the fit - initLinJlist(); // initialize this->linJlist, linear interpolation around numerical divergence -} - -//calculate the coefficients for each Lorentzian -std::vector InelasticDiffSphere::LorentzianCoefficients(double a)const{ - - //precompute the 2+lmax spherical bessel functions (26 in total) - double* jl = new double[2+lmax]; - for(unsigned int l=0; l<=1+lmax; l++){ - jl[l] = boost::math::sph_bessel(l,a); - } - - //store the coefficient of each Lorentzian in vector YJ(a,w) - std::vector YJ(ncoeff); - std::vector::const_iterator itlinJ=linearJlist.begin(); - //loop over all coefficients - std::vector::const_iterator italpha=alpha.begin(); - std::vector::iterator itYJ=YJ.begin(); - for(std::vector::const_iterator it=xnl.begin(); it!=xnl.end(); ++it){ - //only to make expressions more readable - double x = it->x; - unsigned int l = it->l; - //compute factors Y and J - double Y = *italpha; //Y is independent of parameters a and w, and independent of data x - /* J is dependent on parameter a, cannot be computed when active parameter a obeys a*a=c. - * Thus for each it->x we stored J(it->x-m_divZone) and J(it->x_m_divZone), and use linear - * interpolation - */ - double J; - if(fabs(a-x) > m_divZone ){ - J = ( a*jl[l+1]-l*jl[l] ) / (a*a - x*x); - }else{ - J = itlinJ->slope*a + itlinJ->intercept; //linear interpolation - } - *itYJ = Y*J*J; - ++itYJ; - ++italpha; - ++itlinJ; //retrieve next linear interpolation - } // end of for(std::vector::const_iterator it=xnl.begin() - - delete[] jl; - return YJ; -} // end of LorentzianCoefficients - - -void InelasticDiffSphere::function1D(double* out, const double* xValues, const size_t nData)const{ - const double I = getParameter("Intensity"); - const double R = getParameter("Radius"); - const double D = getParameter("Diffusion"); - const double Q = getAttribute("Q").asDouble(); - - std::vector YJ; - YJ = LorentzianCoefficients( Q * R ); - for (unsigned int i = 0; i < nData; i++){ - double x = xValues[i]; - //loop over all coefficients - out[i] = 0.0; - std::vector::const_iterator itYJ=YJ.begin(); - for(std::vector::const_iterator it=xnl.begin(); it!=xnl.end(); ++it){ - double z = it->x; - double zw = z*z*D/(R*R); - double L = zw/(zw*zw+x*x); //Lorentzian - out[i] += I * (*itYJ) * L; - ++itYJ; //retrieve next coefficient - } // end of for(std::vector::const_iterator it.... - } // end of for (unsigned int i... -} // end of void DiffSphere::functionMW - -/** Calculate numerical derivatives. - * @param domain :: The domain of the function - * @param jacobian :: A Jacobian matrix. It is expected to have dimensions of domain.size() by nParams(). - */ -void InelasticDiffSphere::calNumericalDeriv2(const API::FunctionDomain& domain, API::Jacobian& jacobian) -{ - const double minDouble = std::numeric_limits::min(); - const double epsilon = std::numeric_limits::epsilon() * 100; - double stepPercentage = 0.001; // step percentage - double step; // real step - double cutoff = 100.0*minDouble/stepPercentage; - size_t nParam = nParams(); - size_t nData = domain.size(); - - FunctionValues minusStep(domain); - FunctionValues plusStep(domain); - - //PARALLEL_CRITICAL(numeric_deriv) - { - applyTies(); // just in case - function(domain,minusStep); - } - - for (size_t iP = 0; iP < nParam; iP++) - { - if ( isActive(iP) ) - { - const double val = activeParameter(iP); - if (fabs(val) < cutoff) - { - step = epsilon; - } - else - { - step = val*stepPercentage; - } - - double paramPstep = val + step; - //PARALLEL_CRITICAL(numeric_deriv) - { - setActiveParameter(iP, paramPstep); - applyTies(); - function(domain,plusStep); - setActiveParameter(iP, val); - } - - step = paramPstep - val; - for (size_t i = 0; i < nData; i++) - { - jacobian.set(i, iP, (plusStep.getCalculated(i) - minusStep.getCalculated(i)) / step); - } - } - } -} // calNumericalDeriv() - -/// Using numerical derivative -void InelasticDiffSphere::functionDeriv(const API::FunctionDomain &domain, API::Jacobian &jacobian) -{ - this->calNumericalDeriv(domain, jacobian); - return; -} - -/// Using numerical derivative -void InelasticDiffSphere::functionDeriv1D(Jacobian* jacobian, const double* xValues, const size_t nData) -{ - FunctionDomain1DView domain(xValues,nData); - this->calNumericalDeriv(domain,*jacobian); -} - -} // namespace CurveFitting -} // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/src/LeBailFunction.cpp b/Code/Mantid/Framework/CurveFitting/src/LeBailFunction.cpp index 53511ce35454..fc844d293e4e 100644 --- a/Code/Mantid/Framework/CurveFitting/src/LeBailFunction.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/LeBailFunction.cpp @@ -112,7 +112,8 @@ namespace CurveFitting /** Calculate powder diffraction pattern by Le Bail algorithm * @param out :: output vector * @param xvalues :: input vector - * @param includebkgd :: if true, then calculate background and add to output. otherwise, assume zero background + * @param calpeaks :: if true, calculate peaks + * @param calbkgd :: if true, then calculate background and add to output. otherwise, assume zero background */ void LeBailFunction::function(std::vector& out, const std::vector& xvalues, bool calpeaks, bool calbkgd) const { @@ -179,7 +180,7 @@ namespace CurveFitting //---------------------------------------------------------------------------------------------- /** Check whether a parameter is a profile parameter - * @param parammane :: parameter name to check with + * @param paramname :: parameter name to check with */ bool LeBailFunction::hasProfileParameter(std::string paramname) { @@ -256,7 +257,9 @@ namespace CurveFitting //---------------------------------------------------------------------------------------------- /** Set peak position tolerance during importing/adding peaks - * @param peakhkls :: list of Miller indexes (HKL) + * @param peakpostol :: tolerance for peak position + * @param tofmin :: minimum TOF for peak position + * @param tofmax :: maximum TOF for peak position */ void LeBailFunction::setPeakCentreTolerance(double peakpostol, double tofmin, double tofmax) { @@ -777,6 +780,7 @@ namespace CurveFitting //---------------------------------------------------------------------------------------------- /** Group peaks together * @param peakgroupvec: output vector containing peaks grouped together. + * @param outboundpeakvec: output vector containing peaks out of bound range * @param xmin : minimim x value of the data * @param xmax : maximum x value of the data * Disabled argument: MatrixWorkspace_sptr dataws, size_t workspaceindex, @@ -902,6 +906,8 @@ namespace CurveFitting /** Add background function. * The supported background types are Polynomial/Linear/Flat and Chebyshev * @param backgroundtype :: string, type of background, such as Polynomial, Chebyshev + * @param order :: polynomial order for the background + * @param vecparnames :: vector of parameter names * @param vecparvalues :: vector of parameter values from order 0. * @param startx :: background's StartX. Used by Chebyshev * @param endx :: background's EndX. Used by Chebyshev @@ -1046,7 +1052,7 @@ namespace CurveFitting //---------------------------------------------------------------------------------------------- /** Reset all peaks' height - * @param peakheights :: list of peak heights corresponding to each peak + * @param inheights :: list of peak heights corresponding to each peak */ void LeBailFunction::setPeakHeights(std::vector inheights) { diff --git a/Code/Mantid/Framework/CurveFitting/src/Linear.cpp b/Code/Mantid/Framework/CurveFitting/src/Linear.cpp deleted file mode 100644 index 482a7e765a06..000000000000 --- a/Code/Mantid/Framework/CurveFitting/src/Linear.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/*WIKI* - -This algorithm fits a line, of the form Y = c0 + c1 X , to the specified part of a particular spectrum. As well as outputting the result to the log (debug level) and as output properties, a 1D workspace is created with the same X bins/points as the fitted spectrum and the value and error on the fit at each point. The covariance matrix is not currently returned as an output: if this is required please contact the development team. - -==== References ==== -This algorithm uses the gsl linear regression algorithms ''gsl_fit_linear'' and ''gsl_fit_wlinear'', which are documented [http://www.gnu.org/software/gsl/manual/html_node/Linear-regression.html here]. - - -*WIKI*/ -//---------------------------------------------------------------------- -// Includes -//---------------------------------------------------------------------- -#include "MantidCurveFitting/Linear.h" -#include -#include -#include "MantidKernel/BoundedValidator.h" - -namespace Mantid -{ -namespace CurveFitting -{ - -// Register the algorithm into the AlgorithmFactory -DECLARE_ALGORITHM(Linear) - -/// Sets documentation strings for this algorithm -void Linear::initDocs() -{ - this->setWikiSummary("Performs linear least-squares regression on a spectrum (or portion of one)."); - this->setOptionalMessage("Performs linear least-squares regression on a spectrum (or portion of one)."); -} - - -using namespace Mantid::Kernel; -using namespace Mantid::API; - - -Linear::Linear() : API::Algorithm(), m_minX(0), m_maxX(0), m_progress(NULL) -{ - useAlgorithm("Fit"); -} - -Linear::~Linear() {if(m_progress) delete m_progress;m_progress=NULL;} - -void Linear::init() -{ - declareProperty(new WorkspaceProperty<>("InputWorkspace","",Direction::Input), - "The name of the workspace containing the spectrum to fit."); - declareProperty(new WorkspaceProperty<>("OutputWorkspace","",Direction::Output), - "A Workspace1D containing the fit result and errors for the X values of the input spectrum."); - - auto mustBePositive = boost::make_shared >(); - mustBePositive->setLower(0); - declareProperty("WorkspaceIndex",0, mustBePositive, - "Index number of the Workspace to fit"); - declareProperty("StartX", EMPTY_DBL(), - "The X value to start fitting from (default: " - "the lowest value of X)"); - declareProperty("EndX", EMPTY_DBL(), - "The last X value to include in the fitting range " - "(default: the highest X value"); - declareProperty("FitStatus", "", boost::make_shared(), - "Empty if the fit succeeded, otherwise contains the gsl error " - "message", Direction::Output); - declareProperty("FitIntercept", 0.0, - "The intercept with the ordinate of the fitted line. c0 in the equation below.", - Direction::Output); - declareProperty("FitSlope",0.0, - "The slope of the fitted line. c1 in the equation below.", - Direction::Output); - declareProperty("Cov00",0.0, - "The first diagonal component of the covariance matrix.", - Direction::Output); - declareProperty("Cov11",0.0, - "The second diagonal component of the covariance matrix.", - Direction::Output); - declareProperty("Cov01",0.0, - "The off-diagonal component of the covariance matrix.", - Direction::Output); - declareProperty("Chi2",0.0, - "The \\chi^2 value for the goodness of the fit.", - Direction::Output); - - // Disable default gsl error handler (which is to call abort!) - gsl_set_error_handler_off(); -} - -void Linear::exec() -{ - // Get the input workspace - MatrixWorkspace_const_sptr inputWorkspace = getProperty("InputWorkspace"); - // Get the spectrum to fit - const int histNumber = getProperty("WorkspaceIndex"); - // Check validity - if ( histNumber >= static_cast(inputWorkspace->getNumberHistograms()) ) - { - g_log.error() << "WorkspaceIndex set to an invalid value of " << histNumber << std::endl; - throw Exception::IndexError(histNumber,inputWorkspace->getNumberHistograms(),"Linear WorkspaceIndex property"); - } - - // Get references to the data in the chosen spectrum - const MantidVec& X = inputWorkspace->readX(histNumber); - const MantidVec& Y = inputWorkspace->readY(histNumber); - const MantidVec& E = inputWorkspace->readE(histNumber); - // Check if this spectrum has errors - double errorsCount = 0.0; - - // Retrieve the Start/EndX properties, if set - this->setRange(X,Y); - - const bool isHistogram = inputWorkspace->isHistogramData(); - // If the spectrum to be fitted has masked bins, we want to exclude them (even if only partially masked) - const MatrixWorkspace::MaskList * const maskedBins = - ( inputWorkspace->hasMaskedBins(histNumber) ? &(inputWorkspace->maskedBins(histNumber)) : NULL ); - // Put indices of masked bins into a set for easy searching later - std::set maskedIndices; - if (maskedBins) - { - MatrixWorkspace::MaskList::const_iterator it; - for (it = maskedBins->begin(); it != maskedBins->end(); ++it) - maskedIndices.insert(it->first); - } - - progress(0); - - // Declare temporary vectors and reserve enough space if they're going to be used - std::vector XCen, unmaskedY, weights; - int numPoints = m_maxX - m_minX; - if (isHistogram) XCen.reserve(numPoints); - if (maskedBins) unmaskedY.reserve(numPoints); - weights.reserve(numPoints); - - for (int i = 0; i < numPoints; ++i) - { - // If the current bin is masked, skip it - if ( maskedBins && maskedIndices.count(m_minX+i) ) continue; - // Need to adjust X to centre of bin, if a histogram - if (isHistogram) XCen.push_back( 0.5*(X[m_minX+i]+X[m_minX+i+1]) ); - // If there are masked bins present, we need to copy the unmasked Y values - if (maskedBins) unmaskedY.push_back(Y[m_minX+i]); - // GSL wants the errors as weights, i.e. 1/sigma^2 - // We need to be careful if E is zero because that would naively lead to an infinite weight on the point. - // Solution taken here is to zero weight if error is zero, which typically means Y is zero - // (so it is effectively excluded from the fit). - const double& currentE = E[m_minX+i]; - weights.push_back( currentE ? 1.0/(currentE*currentE) : 0.0 ); - // However, if the spectrum given has all errors of zero, then we should use the gsl function that - // doesn't take account of the errors. - if ( currentE ) ++errorsCount; - } - progress(0.3); - - // If masked bins present, need to recalculate numPoints here - if (maskedBins) numPoints = static_cast(unmaskedY.size()); - // If no points left for any reason, bail out - if (numPoints == 0) - { - g_log.error("No points in this range to fit"); - throw std::runtime_error("No points in this range to fit"); - } - - // Set up pointer variables to pass to gsl, pointing them to the right place - const double * const xVals = ( isHistogram ? &XCen[0] : &X[m_minX] ); - const double * const yVals = ( maskedBins ? &unmaskedY[0] : &Y[m_minX] ); - - // Call the gsl fitting function - // The stride value of 1 reflects that fact that we want every element of our input vectors - const int stride = 1; - double *c0(new double),*c1(new double),*cov00(new double),*cov01(new double),*cov11(new double),*chisq(new double); - int status; - // Unless our spectrum has error values for vast majority of points, - // call the gsl function that doesn't use errors - if ( errorsCount/numPoints < 0.9 ) - { - g_log.debug("Calling gsl_fit_linear (doesn't use errors in fit)"); - status = gsl_fit_linear(xVals,stride,yVals,stride,numPoints,c0,c1,cov00,cov01,cov11,chisq); - } - // Otherwise, call the one that does account for errors on the data points - else - { - g_log.debug("Calling gsl_fit_wlinear (uses errors in fit)"); - status = gsl_fit_wlinear(xVals,stride,&weights[0],stride,yVals,stride,numPoints,c0,c1,cov00,cov01,cov11,chisq); - } - progress(0.8); - - // Check that the fit succeeded - std::string fitStatus = gsl_strerror(status); - // For some reason, a fit where c0,c1 & chisq are all infinity doesn't report as a - // failure, so check explicitly. - if ( !gsl_finite(*chisq) || !gsl_finite(*c0) || !gsl_finite(*c1) ) - fitStatus = "Fit gives infinities"; - if (fitStatus != "success") g_log.error() << "The fit failed: " << fitStatus << "\n"; - else - g_log.information() << "The fit succeeded, giving y = " << *c0 << " + " << *c1 << "*x, with a Chi^2 of " << *chisq << "\n"; - - // Set the fit result output properties - setProperty("FitStatus",fitStatus); - setProperty("FitIntercept",*c0); - setProperty("FitSlope",*c1); - setProperty("Cov00",*cov00); - setProperty("Cov11",*cov11); - setProperty("Cov01",*cov01); - setProperty("Chi2",*chisq); - - // Create and fill a workspace2D with the same bins as the fitted spectrum and the value of the fit for the centre of each bin - const size_t YSize = Y.size(); - MatrixWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().create(inputWorkspace,1,X.size(),YSize); - - // Copy over the X bins - outputWorkspace->dataX(0).assign(X.begin(),X.end()); - // Now loop over the spectrum and use gsl function to calculate the Y & E values for the function - for (size_t i = 0; i < YSize; ++i) - { - const double x = ( isHistogram ? 0.5*(X[i]+X[i+1]) : X[i] ); - const int err = gsl_fit_linear_est(x,*c0,*c1,*cov00,*cov01,*cov11,&(outputWorkspace->dataY(0)[i]),&(outputWorkspace->dataE(0)[i])); - if (err) g_log.warning() << "Problem in filling the output workspace: " << gsl_strerror(err) << std::endl; - } - setProperty("OutputWorkspace",outputWorkspace); - progress(1); - // Clean up - delete c0; - delete c1; - delete cov00; - delete cov01; - delete cov11; - delete chisq; -} - -/// Retrieve and check the Start/EndX parameters, if set -void Linear::setRange(const MantidVec& X, const MantidVec& Y) -{ - //read in the values that the user selected - double startX = getProperty("StartX"); - double endX = getProperty("EndX"); - //If the user didn't a start default to the start of the data - if ( isEmpty(startX) ) startX = X.front(); - //the default for the end is the end of the data - if ( isEmpty(endX) ) endX = X.back(); - - // Check the validity of startX - if ( startX < X.front() ) - { - g_log.warning("StartX out of range! Set to start of frame."); - startX = X.front(); - } - // Now get the corresponding bin boundary that comes before (or coincides with) this value - for (m_minX = 0; X[m_minX+1] < startX; ++m_minX) {} - - // Check the validity of endX and get the bin boundary that come after (or coincides with) it - if ( endX >= X.back() || endX < startX ) - { - if ( endX != X.back() ) - { - g_log.warning("EndX out of range! Set to end of frame"); - endX = X.back(); - } - m_maxX = static_cast(Y.size()); - } - else - { - for (m_maxX = m_minX; X[m_maxX] < endX; ++m_maxX) {} - } -} - -} // namespace Algorithm -} // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/src/Lorentzian.cpp b/Code/Mantid/Framework/CurveFitting/src/Lorentzian.cpp index d9cc3a0f9be2..039f21e114d0 100644 --- a/Code/Mantid/Framework/CurveFitting/src/Lorentzian.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/Lorentzian.cpp @@ -5,12 +5,12 @@ A Lorentzian function is defined as: where:
    -
  • A (Amplitude) - Maximum peak height at peak centre
  • +
  • A (Amplitude) - Intensity scaling
  • x_0 (PeakCentre) - centre of peak
  • -
  • \Gamma (HWHM) - half-width at half-maximum
  • +
  • \Gamma/2 (HWHM) - half-width at half-maximum
-Note that the FWHM (Full Width Half Maximum) equals two times HWHM, and the integral over the Lorentzian equals 1. +Note that the FWHM (Full Width Half Maximum) equals two times HWHM, and the integral over the Lorentzian equals the intensity scaling A. The figure below illustrate this symmetric peakshape function fitted to a TOF peak: @@ -35,9 +35,9 @@ DECLARE_FUNCTION(Lorentzian); void Lorentzian::init() { - declareParameter("Amplitude", 1.0, "Maximum height of peak when x=x0"); + declareParameter("Amplitude", 1.0, "Intensity scaling"); declareParameter("PeakCentre", 0.0, "Centre of peak"); - declareParameter("FWHM", 0.0, "Falf-width at half-maximum"); + declareParameter("FWHM", 0.0, "Full-width at half-maximum"); } diff --git a/Code/Mantid/Framework/CurveFitting/src/ProcessBackground.cpp b/Code/Mantid/Framework/CurveFitting/src/ProcessBackground.cpp index eb778d2460b4..18d7a95bd3c6 100644 --- a/Code/Mantid/Framework/CurveFitting/src/ProcessBackground.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/ProcessBackground.cpp @@ -109,6 +109,7 @@ DECLARE_ALGORITHM(ProcessBackground) std::vector bkgdtype; bkgdtype.push_back("Polynomial"); bkgdtype.push_back("Chebyshev"); + // bkgdtype.push_back("FullprofPolynomial"); auto bkgdvalidator = boost::make_shared(bkgdtype); declareProperty("BackgroundType", "Polynomial", bkgdvalidator, "Type of the background. Options include Polynomial and Chebyshev."); @@ -177,6 +178,28 @@ DECLARE_ALGORITHM(ProcessBackground) setPropertySettings("UserBackgroundWorkspace", new VisibleWhenProperty("Options", IS_EQUAL_TO, "SelectBackgroundPoints")); + // Optional output workspace + declareProperty(new WorkspaceProperty("OutputBackgroundParameterWorkspace", "", Direction::Output, + PropertyMode::Optional), + "Output parameter table workspace containing the background fitting result. "); + setPropertySettings("OutputBackgroundParameterWorkspace", + new VisibleWhenProperty("Options", IS_EQUAL_TO, "SelectBackgroundPoints")); + + // Output background type. + std::vector outbkgdtype; + outbkgdtype.push_back("Polynomial"); + outbkgdtype.push_back("Chebyshev"); + auto outbkgdvalidator = boost::make_shared(bkgdtype); + declareProperty("OutputBackgroundType", "Polynomial", outbkgdvalidator, + "Type of background to fit with selected background points."); + setPropertySettings("OutputBackgroundType", + new VisibleWhenProperty("Options", IS_EQUAL_TO, "SelectBackgroundPoints")); + + // Output background type. + declareProperty("OutputBackgroundOrder", 6, + "Order of background to fit with selected background points."); + setPropertySettings("OutputBackgroundOrder", + new VisibleWhenProperty("Options", IS_EQUAL_TO, "SelectBackgroundPoints")); // Peak table workspac for "RemovePeaks" declareProperty(new WorkspaceProperty("BraggPeakTableWorkspace", "", Direction::Input, @@ -206,6 +229,8 @@ DECLARE_ALGORITHM(ProcessBackground) throw std::invalid_argument("Input Workspace cannot be obtained."); } + m_bkgdType = getPropertyValue("BackgroundType"); + int intemp = getProperty("WorkspaceIndex"); if (intemp < 0) throw std::invalid_argument("WorkspaceIndex is not allowed to be less than 0. "); @@ -236,6 +261,17 @@ DECLARE_ALGORITHM(ProcessBackground) } else if (option.compare("SelectBackgroundPoints") == 0) { + string outbkgdparwsname = getPropertyValue("OutputBackgroundParameterWorkspace"); + if (outbkgdparwsname.size() > 0) + { + // Will fit the selected background + m_doFitBackground = true; + } + else + { + m_doFitBackground = false; + } + string smode = getProperty("SelectionMode"); if (smode == "FitGivenDataPoints") { @@ -249,6 +285,14 @@ DECLARE_ALGORITHM(ProcessBackground) { throw runtime_error("N/A is not supported."); } + + if (m_doFitBackground) + { + // Fit the selected background + string bkgdfunctype = getPropertyValue("OutputBackgroundType"); + fitBackgroundFunction(bkgdfunctype); + } + } else { @@ -632,7 +676,7 @@ DECLARE_ALGORITHM(ProcessBackground) void ProcessBackground::execSelectBkgdPoints2() { // Process properties - BackgroundFunction_sptr bkgdfunc = createBackgroundFunction(); + BackgroundFunction_sptr bkgdfunc = createBackgroundFunction(m_bkgdType); TableWorkspace_sptr bkgdtablews = getProperty("BackgroundTableWorkspace"); // Set up background function from table @@ -670,7 +714,7 @@ DECLARE_ALGORITHM(ProcessBackground) DataObjects::Workspace2D_sptr ProcessBackground::autoBackgroundSelection(Workspace2D_sptr bkgdWS) { // Get background type and create bakground function - BackgroundFunction_sptr bkgdfunction = createBackgroundFunction(); + BackgroundFunction_sptr bkgdfunction = createBackgroundFunction(m_bkgdType); int bkgdorder = getProperty("BackgroundOrder"); bkgdfunction->setAttributeValue("n", bkgdorder); @@ -743,10 +787,8 @@ DECLARE_ALGORITHM(ProcessBackground) //---------------------------------------------------------------------------------------------- /** Create a background function from input properties */ - BackgroundFunction_sptr ProcessBackground::createBackgroundFunction() + BackgroundFunction_sptr ProcessBackground::createBackgroundFunction(const string backgroundtype) { - std::string backgroundtype = getProperty("BackgroundType"); - CurveFitting::BackgroundFunction_sptr bkgdfunction; if (backgroundtype.compare("Polynomial") == 0) @@ -842,12 +884,19 @@ DECLARE_ALGORITHM(ProcessBackground) << " total data points. " << "\n"; // Build new workspace + size_t nspec; + if (m_doFitBackground) + nspec = 3; + else + nspec = 1; + Workspace2D_sptr outws = boost::dynamic_pointer_cast - (API::WorkspaceFactory::Instance().create("Workspace2D", 1, vecx.size(), vecy.size())); + (API::WorkspaceFactory::Instance().create("Workspace2D", nspec, vecx.size(), vecy.size())); for (size_t i = 0; i < vecx.size(); ++i) { - outws->dataX(0)[i] = vecx[i]; + for(size_t j = 0; j < nspec; ++j) + outws->dataX(j)[i] = vecx[i]; outws->dataY(0)[i] = vecy[i]; outws->dataE(0)[i] = vece[i]; } @@ -1044,6 +1093,115 @@ DECLARE_ALGORITHM(ProcessBackground) return count; } + //---------------------------------------------------------------------------------------------- + /** Fit background function + */ + void ProcessBackground::fitBackgroundFunction(std::string bkgdfunctiontype) + { + // Get background type and create bakground function + BackgroundFunction_sptr bkgdfunction = createBackgroundFunction(bkgdfunctiontype); + + int bkgdorder = getProperty("OutputBackgroundOrder"); + bkgdfunction->setAttributeValue("n", bkgdorder); + + if (bkgdfunctiontype == "Chebyshev") + { + double xmin = m_outputWS->readX(0).front(); + double xmax = m_outputWS->readX(0).back(); + g_log.information() << "Chebyshev Fit range: " << xmin << ", " << xmax << "\n"; + bkgdfunction->setAttributeValue("StartX", xmin); + bkgdfunction->setAttributeValue("EndX", xmax); + } + + g_log.information() << "Fit selected background " << bkgdfunctiontype + << " to data workspace with " << m_outputWS->getNumberHistograms() << " spectra." + << "\n"; + + // Fit input (a few) background pionts to get initial guess + API::IAlgorithm_sptr fit; + try + { + fit = this->createChildAlgorithm("Fit", 0.9, 1.0, true); + } + catch (Exception::NotFoundError &) + { + g_log.error() << "Requires CurveFitting library." << std::endl; + throw; + } + + g_log.information() << "Fitting background function: " << bkgdfunction->asString() << "\n"; + + double startx = m_lowerBound; + double endx = m_upperBound; + fit->setProperty("Function", boost::dynamic_pointer_cast(bkgdfunction)); + fit->setProperty("InputWorkspace", m_outputWS); + fit->setProperty("WorkspaceIndex", 0); + fit->setProperty("MaxIterations", 500); + fit->setProperty("StartX", startx); + fit->setProperty("EndX", endx); + fit->setProperty("Minimizer", "Levenberg-MarquardtMD"); + fit->setProperty("CostFunction", "Least squares"); + + fit->executeAsChildAlg(); + + // Get fit status and chi^2 + std::string fitStatus = fit->getProperty("OutputStatus"); + bool allowedfailure = (fitStatus.find("cannot") < fitStatus.size()) && + (fitStatus.find("tolerance") < fitStatus.size()); + if (fitStatus.compare("success") != 0 && !allowedfailure) + { + g_log.error() << "ProcessBackground: Fit Status = " << fitStatus + << ". Not to update fit result" << std::endl; + throw std::runtime_error("Bad Fit"); + } + + const double chi2 = fit->getProperty("OutputChi2overDoF"); + g_log.information() << "Fit background: Fit Status = " << fitStatus << ", chi2 = " + << chi2 << "\n"; + + // Get out the parameter names + API::IFunction_sptr funcout = fit->getProperty("Function"); + TableWorkspace_sptr outbkgdparws = boost::make_shared(); + outbkgdparws->addColumn("str", "Name"); + outbkgdparws->addColumn("double", "Value"); + + TableRow typerow = outbkgdparws->appendRow(); + typerow << bkgdfunctiontype << 0.; + + vector parnames = funcout->getParameterNames(); + size_t nparam = funcout->nParams(); + for (size_t i = 0; i < nparam; ++i) + { + TableRow newrow = outbkgdparws->appendRow(); + newrow << parnames[i] << funcout->getParameter(i); + } + + TableRow chi2row = outbkgdparws->appendRow(); + chi2row << "Chi-square" << chi2; + + g_log.information() << "Set table workspace (#row = " << outbkgdparws->rowCount() + << ") to OutputBackgroundParameterTable. " << "\n"; + setProperty("OutputBackgroundParameterWorkspace", outbkgdparws); + + // Set output workspace + const MantidVec& vecX = m_outputWS->readX(0); + const MantidVec& vecY = m_outputWS->readY(0); + FunctionDomain1DVector domain(vecX); + FunctionValues values(domain); + + funcout->function(domain, values); + + MantidVec& dataModel = m_outputWS->dataY(1); + MantidVec& dataDiff = m_outputWS->dataY(2); + for (size_t i = 0; i < dataModel.size(); ++i) + { + dataModel[i] = values[i]; + dataDiff[i] = vecY[i] - dataModel[i]; + } + + return; + } + } // namespace CurveFitting } // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/src/SeqDomain.cpp b/Code/Mantid/Framework/CurveFitting/src/SeqDomain.cpp index c4a3ed24ac33..c5415b71c653 100644 --- a/Code/Mantid/Framework/CurveFitting/src/SeqDomain.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/SeqDomain.cpp @@ -101,7 +101,7 @@ void SeqDomain::leastSquaresVal(const CostFuncLeastSquares& leastSquares) //------------------------------------------------------------------------------------------------ /** * Calculate the value of a least squares cost function - * @param leastSquares :: The least squares cost func to calculate the value for + * @param rwp :: The RWP cost func to calculate the value for */ void SeqDomain::rwpVal(const CostFuncRwp& rwp) { diff --git a/Code/Mantid/Framework/CurveFitting/src/SplineInterpolation.cpp b/Code/Mantid/Framework/CurveFitting/src/SplineInterpolation.cpp index d83c26715a64..459d611eff38 100644 --- a/Code/Mantid/Framework/CurveFitting/src/SplineInterpolation.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/SplineInterpolation.cpp @@ -248,7 +248,6 @@ namespace Mantid * @param inputWorkspace :: The input workspace * @param outputWorkspace :: The output workspace * @param order :: The order of derivatives to calculate - * @param row :: The row of spectra to use */ void SplineInterpolation::calculateDerivatives(API::MatrixWorkspace_const_sptr inputWorkspace, API::MatrixWorkspace_sptr outputWorkspace, int order) const diff --git a/Code/Mantid/Framework/CurveFitting/test/DiffRotDiscreteCircleTest.h b/Code/Mantid/Framework/CurveFitting/test/DiffRotDiscreteCircleTest.h new file mode 100644 index 000000000000..8eeffacaaff2 --- /dev/null +++ b/Code/Mantid/Framework/CurveFitting/test/DiffRotDiscreteCircleTest.h @@ -0,0 +1,438 @@ +#ifndef DIFFROTDISCRETECIRCLETEST_H_ +#define DIFFROTDISCRETECIRCLETEST_H_ + +#include +#include +#include +#include +#include + +#include "MantidCurveFitting/Gaussian.h" +#include "MantidCurveFitting/DiffRotDiscreteCircle.h" +#include "MantidCurveFitting/Convolution.h" +#include "MantidTestHelpers/WorkspaceCreationHelper.h" +#include "MantidCurveFitting/Fit.h" +#include "MantidAPI/FunctionFactory.h" +#include "MantidAPI/AlgorithmFactory.h" + +class DiffRotDiscreteCircleTest : public CxxTest::TestSuite +{ + +public: + + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static DiffRotDiscreteCircleTest *createSuite() { return new DiffRotDiscreteCircleTest(); } + static void destroySuite( DiffRotDiscreteCircleTest *suite ) { delete suite; } + + // convolve the elastic part with a resolution function, here a Gaussian + void testDiffRotDiscreteCircleElastic() + { + // initialize the resolution function + const double w0 = random_value( -1.0, 1.0 ); + const double h = random_value( 1.0, 1000.0 ); + const double fwhm = random_value( 1.0, 100.0 ); + boost::shared_ptr resolution( new Mantid::CurveFitting::Gaussian() ); + resolution -> initialize(); // declare parameters + resolution -> setCentre( w0 ); + resolution -> setHeight( h ); + resolution -> setFwhm( fwhm ); + + // initialize the structure factor as the elastic part of DiffRotDiscreteCircle + const double I = random_value( 1.0, 1000.0 ); + const double r = random_value( 0.3, 9.8 ); + const double Q = 0.9; + const int N = 6; + boost::shared_ptr structure_factor( new Mantid::CurveFitting::ElasticDiffRotDiscreteCircle() ); + structure_factor -> setParameter( "Height", I ); + structure_factor -> setParameter( "Radius", r ); + structure_factor -> setAttributeValue( "Q", Q ); + structure_factor -> setAttributeValue( "N", N ); + + // initialize the convolution function + Mantid::CurveFitting::Convolution conv; + conv.addFunction( resolution ); + conv.addFunction( structure_factor ); + + // initialize some frequency values centered around zero + const size_t M = 4001; + double w[ M ]; + const double dw = random_value(0.1, 0.5); // bin width + for( size_t i = 0; i < M; i++ ) w[i] = (static_cast(i) - M/2 ) * dw; + Mantid::API::FunctionDomain1DView xView( &w[0], M ); + Mantid::API::FunctionValues out( xView ); + + // convolve + conv.function( xView, out ); + + // Result must be the resolution function multiplied by the intensity of ElasticDiffRotDiscreteCircle + double scaling = I * structure_factor -> HeightPrefactor(); + Mantid::API::FunctionValues out_resolution( xView ); + resolution -> function( xView, out_resolution ); + for( size_t i = 0; i < M; i++ ) + TS_ASSERT_DELTA( out.getCalculated( i ), scaling * out_resolution.getCalculated( i ), 1e-3 ); + + } // testDiffRotDiscreteCircleElastic + + + /// Fit the convolution of the inelastic part with a Gaussian resolution function + void testDiffRotDiscreteCircleInelastic() + { + /* Note: it turns out that parameters Intensity and Radius are highly covariant, so that more than one minimum exists. + * Thus, I tied parameter Radius. This is OK since one usually knows the radius of the circle of the jumping diffusion + */ + + // initialize the fitting function in a Fit algorithm + // Parameter units are assumed in micro-eV, Angstroms, Angstroms**(-1), and nano-seconds. Intensities have arbitrary units + std::string funtion_string = "(composite=Convolution,FixResolution=true,NumDeriv=true;name=Gaussian,Height=1.0,PeakCentre=0.0,Sigma=20.0,ties=(Height=1.0,PeakCentre=0.0,Sigma=20.0);name=InelasticDiffRotDiscreteCircle,N=3,Q=0.5,Intensity=47.014,Radius=1.567,Decay=7.567)"; + + // Initialize the fit function in the Fit algorithm + Mantid::CurveFitting::Fit fitalg; + TS_ASSERT_THROWS_NOTHING( fitalg.initialize() ); + TS_ASSERT( fitalg.isInitialized() ); + fitalg.setProperty( "Function", funtion_string ); + + // create the data workspace by evaluating the fit function in the Fit algorithm + auto data_workspace = generateWorkspaceFromFitAlgorithm( fitalg ); + //saveWorkspace( data_workspace, "/tmp/junk.nxs" ); // for debugging purposes only + + //override the function with new parameters, then do the Fit + funtion_string = "(composite=Convolution,FixResolution=true,NumDeriv=true;name=Gaussian,Height=1.0,PeakCentre=0.0,Sigma=20.0,ties=(Height=1.0,PeakCentre=0.0,Sigma=20.0);name=InelasticDiffRotDiscreteCircle,N=3,Q=0.5,Intensity=10.0,Radius=1.567,Decay=20.0,ties=(Radius=1.567))"; + fitalg.setProperty( "Function", funtion_string ); + fitalg.setProperty( "InputWorkspace", data_workspace ); + fitalg.setPropertyValue( "WorkspaceIndex", "0" ); + TS_ASSERT_THROWS_NOTHING( TS_ASSERT( fitalg.execute() ) ); + TS_ASSERT( fitalg.isExecuted() ); + + // check Chi-square is small + const double chi_squared = fitalg.getProperty("OutputChi2overDoF"); + TS_ASSERT_LESS_THAN( chi_squared, 0.001 ); + //std::cout << "\nchi_squared = " << chi_squared << "\n"; // only for debugging purposes + + // check the parameters of the resolution did not change + Mantid::API::IFunction_sptr fitalg_function = fitalg.getProperty( "Function" ); + auto fitalg_conv = boost::dynamic_pointer_cast( fitalg_function ) ; + Mantid::API::IFunction_sptr fitalg_resolution = fitalg_conv->getFunction( 0 ); + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "PeakCentre" ), 0.0, 0.00001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Height" ), 1.0, 1.0 * 0.001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Sigma" ), 20.0, 20.0* 0.001 ); // allow for a small percent variation + //std::cout << "\nPeakCentre = " << fitalg_resolution->getParameter("PeakCentre") << " Height= " << fitalg_resolution->getParameter("Height") << " Sigma=" << fitalg_resolution->getParameter("Sigma") << "\n"; // only for debugging purposes + + // check the parameters of the inelastic part + Mantid::API::IFunction_sptr fitalg_structure_factor = fitalg_conv->getFunction( 1 ); + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Intensity" ), 47.014, 47.014 * 0.05 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Radius" ), 1.567, 1.567 * 0.05 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Decay" ), 7.567, 7.567 * 0.05 ); // allow for a small percent variation + //std::cout << "\nGOAL: Intensity = 47.014, Radius = 1.567, Decay = 7.567\n"; // only for debugging purposes + //std::cout << "OPTIMIZED: Intensity = " << fitalg_structure_factor->getParameter("Intensity") << " Radius = " << fitalg_structure_factor->getParameter("Radius") << " Decay = " << fitalg_structure_factor->getParameter("Decay") << "\n"; // only for debugging purposes + + } // testDiffRotDiscreteCircleElastic + + /* Check the particular case for N = 3 + * In this case, the inelastic part should reduce to a single Lorentzian in 'w': + * ( 2 / pi ) * A1( Q ) * ( 3 * tao / ( 9 + ( w * tao )**2 ) ) + * A1( Q ) = ( 1 / 3 ) * ( 1 - j0( Q * R * sqrt( 3 ) ) ) + * j0( x ) = sin( x ) / x + */ + void testDiffRotDiscreteCircleInelasticN3() + { + const double I = 2.9; + const double R = 2.3; + const double tao = 0.468; + const double Q = 0.9; + + // generate data workspace with the single lorentzian function + auto data_workspace = generateN3Workspace( I, R, tao, Q ); + //saveWorkspace( data_workspace, "/tmp/junk_single_lorentzian.nxs" ); // for debugging purposes only + + // initialize the fitting function string + // Parameter units are assumed in micro-eV, Angstroms, Angstroms**(-1), and nano-seconds. Intensities have arbitrary units + std::string funtion_string = "name=InelasticDiffRotDiscreteCircle,N=3,Q=0.9,Intensity=2.9,Radius=2.3,Decay=0.468"; + + // Do a fit with no iterations + Mantid::CurveFitting::Fit fitalg; + TS_ASSERT_THROWS_NOTHING( fitalg.initialize() ); + TS_ASSERT( fitalg.isInitialized() ); + fitalg.setProperty( "Function", funtion_string ); + fitalg.setProperty( "MaxIterations", 0 ); // no iterations + fitalg.setProperty( "InputWorkspace", data_workspace ); + fitalg.setPropertyValue( "WorkspaceIndex", "0" ); + TS_ASSERT_THROWS_NOTHING( TS_ASSERT( fitalg.execute() ) ); + TS_ASSERT( fitalg.isExecuted() ); + + // create temporary workspace to check Y-values produced by the Fit algorithm // for debugging purposes only + //auto temp_workspace = generateWorkspaceFromFitAlgorithm( fitalg ); // for debugging purposes only + //saveWorkspace( temp_workspace, "/tmp/junk_from_fit_algorithm.nxs" ); // for debugging purposes only + + // check the parameters of the InelasticDiffRotDiscreteCircle did not change + Mantid::API::IFunction_sptr fitalg_structure_factor = fitalg.getProperty( "Function" ); + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Intensity" ), I, I * 0.01 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Radius" ), R, R * 0.01 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Decay" ), tao, tao * 0.01 ); // allow for a small percent variation + //std::cout << "\nGOAL: Intensity = 2.9, Radius = 2.3, Decay = 0.468\n"; // only for debugging purposes + //std::cout << "OPTIMIZED: Intensity = " << fitalg_structure_factor->getParameter("Intensity") << " Radius = " << fitalg_structure_factor->getParameter("Radius") << " Decay = " << fitalg_structure_factor->getParameter("Decay") << "\n"; // only for debugging purposes + + // check Chi-square is small + const double chi_squared = fitalg.getProperty("OutputChi2overDoF"); + TS_ASSERT_LESS_THAN( chi_squared, 1e-12 ); + //std::cout << "\nchi_squared = " << chi_squared << "\n"; // only for debugging purposes + + } + + + /// check ties between elastic and inelastic parts + void testDiffRotDiscreteCircleTies() + { + const double I = 2.9; + const double R = 2.3; + const double tao = 0.45; + const double Q = 0.7; + const int N = 4; + Mantid::CurveFitting::DiffRotDiscreteCircle func; + func.init(); + func.setParameter( "f1.Intensity", I ); + func.setParameter( "f1.Radius" , R ); + func.setParameter( "f1.Decay", tao ); + func.setAttributeValue( "Q" , Q ); + func.setAttributeValue( "N", N ); + + // check values where correctly initialized + auto ids = boost::dynamic_pointer_cast( func.getFunction(1) ); + TS_ASSERT_EQUALS( ids->getParameter("Intensity"), I ); + TS_ASSERT_EQUALS( ids->getParameter("Radius"), R ); + TS_ASSERT_EQUALS( ids->getParameter("Decay"), tao ); + TS_ASSERT_EQUALS( ids->getAttribute("Q").asDouble(), Q ); + TS_ASSERT_EQUALS( ids->getAttribute("Q").asDouble(), Q ); + + //check the ties were applied correctly + func.applyTies(); //elastic parameters are tied to inelastic parameters + auto eds = boost::dynamic_pointer_cast( func.getFunction(0) ); + TS_ASSERT_EQUALS( eds->getParameter("Height") , I ); + TS_ASSERT_EQUALS( eds->getParameter("Radius") , R ); + TS_ASSERT_EQUALS( eds->getAttribute("Q").asDouble(), Q ); + } + + + /// check aliases in the composite function + void testDiffRotDiscreteCircleAliases() + { + const double I = 2.9; + const double R = 2.3; + const double tao = 0.45; + + // This should set parameters of the inelastic part + Mantid::CurveFitting::DiffRotDiscreteCircle func; + func.init(); + func.setParameter( "Intensity", I ); + func.setParameter( "Radius", R ); + func.setParameter( "Decay", tao ); + + // check the parameter of the inelastic part + auto ifunc = boost::dynamic_pointer_cast( func.getFunction(1) ); + TS_ASSERT_EQUALS( ifunc -> getParameter( "Intensity" ), I ); + TS_ASSERT_EQUALS( ifunc -> getParameter( "Radius" ), R ); + TS_ASSERT_EQUALS( ifunc -> getParameter( "Decay" ), tao ); + + // check the parameters of the elastic part + func.applyTies(); //elastic parameters are tied to inelastic parameters + auto efunc = boost::dynamic_pointer_cast( func.getFunction(0) ); + TS_ASSERT_EQUALS( efunc -> getParameter( "Height" ) , I ); + TS_ASSERT_EQUALS( efunc -> getParameter( "Radius" ) , R ); + + } // testDiffRotDiscreteCircleAliases + + + /// Fit the convolution of the jumping diffusion with a Gaussian resolution function + void testDiffRotDiscreteCircle() + { + // initialize the fitting function in a Fit algorithm + // Parameter units are assumed in micro-eV, Angstroms, Angstroms**(-1), and nano-seconds. Intensities have arbitrary units + std::string funtion_string = "(composite=Convolution,FixResolution=true,NumDeriv=true;name=Gaussian,Height=1,PeakCentre=0,Sigma=20,ties=(Height=1,PeakCentre=0,Sigma=20);(name=DiffRotDiscreteCircle,N=3,NumDeriv=true,Q=0.5,Intensity=47.014,Radius=1.567,Decay=7.567))"; + + // Initialize the fit function in the Fit algorithm + Mantid::CurveFitting::Fit fitalg; + TS_ASSERT_THROWS_NOTHING( fitalg.initialize() ); + TS_ASSERT( fitalg.isInitialized() ); + fitalg.setProperty( "Function", funtion_string ); + + // create the data workspace by evaluating the fit function in the Fit algorithm + auto data_workspace = generateWorkspaceFromFitAlgorithm( fitalg ); + //saveWorkspace( data_workspace, "/tmp/junk.nxs" ); // for debugging purposes only + + //override the function with new parameters, then do the Fit + funtion_string = "(composite=Convolution,FixResolution=true,NumDeriv=true;name=Gaussian,Height=1,PeakCentre=0,Sigma=20,ties=(Height=1,PeakCentre=0,Sigma=20);(name=DiffRotDiscreteCircle,N=3,NumDeriv=true,Q=0.5,Intensity=10.0,Radius=1.567,Decay=20.0))"; + fitalg.setProperty( "Function", funtion_string ); + fitalg.setProperty( "InputWorkspace", data_workspace ); + fitalg.setPropertyValue( "WorkspaceIndex", "0" ); + TS_ASSERT_THROWS_NOTHING( TS_ASSERT( fitalg.execute() ) ); + TS_ASSERT( fitalg.isExecuted() ); + + // check Chi-square is small + const double chi_squared = fitalg.getProperty("OutputChi2overDoF"); + TS_ASSERT_LESS_THAN( chi_squared, 0.001 ); + //std::cout << "\nchi_squared = " << chi_squared << "\n"; // only for debugging purposes + + // check the parameters of the resolution did not change + Mantid::API::IFunction_sptr fitalg_function = fitalg.getProperty( "Function" ); + auto fitalg_conv = boost::dynamic_pointer_cast( fitalg_function ) ; + Mantid::API::IFunction_sptr fitalg_resolution = fitalg_conv->getFunction( 0 ); + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "PeakCentre" ), 0.0, 0.00001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Height" ), 1.0, 1.0 * 0.001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Sigma" ), 20.0, 20.0* 0.001 ); // allow for a small percent variation + //std::cout << "\nPeakCentre = " << fitalg_resolution->getParameter("PeakCentre") << " Height= " << fitalg_resolution->getParameter("Height") << " Sigma=" << fitalg_resolution->getParameter("Sigma") << "\n"; // only for debugging purposes + + // check the parameters of the DiffRotDiscreteCircle + Mantid::API::IFunction_sptr fitalg_structure_factor = fitalg_conv->getFunction( 1 ); + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Intensity" ), 47.014, 47.014 * 0.05 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Radius" ), 1.567, 1.567 * 0.05 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Decay" ), 7.567, 7.567 * 0.05 ); // allow for a small percent variation + //std::cout << "\nGOAL: Intensity = 47.014, Radius = 1.567, Decay = 7.567\n"; // only for debugging purposes + //std::cout << "OPTIMIZED: Intensity = " << fitalg_structure_factor->getParameter("Intensity") << " Radius = " << fitalg_structure_factor->getParameter("Radius") << " Decay = " << fitalg_structure_factor->getParameter("Decay") << "\n"; // only for debugging purposes + + } // testDiffRotDiscreteCircle + + +private: + + /// returns a real value from a uniform distribution + double random_value(const double & a, const double & b) + { + boost::mt19937 rng; + boost::uniform_real distribution( a, b ); + return distribution(rng); + } + + + /// save the domain and the values of a function to a nexus file + void saveValues( Mantid::API::IFunction_sptr &function_pointer, Mantid::API::FunctionDomain1DView &xView, const std::string &filename ) + { + Mantid::API::FunctionValues dataYvalues( xView ); + function_pointer -> function( xView, dataYvalues ); // evaluate the function + const size_t M = xView.size(); + // create temporaray workspace. + auto temp_ws = WorkspaceCreationHelper::Create2DWorkspace(1, static_cast( M ) ); + for( size_t i = 0; i < M; i++ ) + { + temp_ws -> dataX( 0 )[ i ] = xView[ i ]; + temp_ws -> dataY( 0 )[ i ] = dataYvalues.getCalculated( i ); + temp_ws -> dataE( 0 )[ i ] = 0.1 * dataYvalues.getCalculated( i ); // assume the error is 10% of the actual value + } + const double dw = xView[1]-xView[0]; // bin width + temp_ws -> dataX( 0 )[ M ] = temp_ws -> dataX( 0 )[ M - 1 ] + dw; + // save workspace to file. + auto save = Mantid::API::AlgorithmFactory::Instance().create( "SaveNexus", 1 ); + if ( !save ) throw std::runtime_error( "Algorithm not created" ); + save -> initialize(); + save -> setProperty( "Filename", filename ); + save -> setProperty( "InputWorkspace", temp_ws ); + save->execute(); + + // some cleaning + Mantid::API::AnalysisDataService::Instance().remove( temp_ws -> getName() ); + + } + + + /* Create a workspace with the following single lorentzian in 'w' + * ( 2 / pi ) * A1( Q ) * ( 3 * tao / ( 9 + ( w * tao )**2 ) ) + * A1( Q ) = ( 1 / 3 ) * ( 1 - j0( Q * R * sqrt( 3 ) ) ) + * j0( x ) = sin( x ) / x + */ + Mantid::DataObjects::Workspace2D_sptr generateN3Workspace( const double & I, const double & R, const double & tao, const double & Q ) + { + const double hbar = 0.658211626; // plank constant in meV*THz (or ueV*PHz) + const double rate = hbar / tao; // conversion from picosec to mili-eV, or from nanosec to micro-eV + + // calculate prefix A1. Better be verbose for clarity + const double x = Q * R * sqrt( 3.0 ); + const double j0 = sin( x ) / x; + const double A1 = ( 1.0 / 3.0 ) * ( 1.0 - j0 ); + + // initialize some frequency values centered around zero. Will work as dataX + const size_t M = 1001; + double dataX[ M ]; + const double dw = 0.4; // typical bin width for BASIS@ORNL beamline, in micro-seconds + for( size_t i = 0; i < M; i++ ) dataX[i] = (static_cast(i) - M/2 ) * dw; + + // create the workspace + auto ws = WorkspaceCreationHelper::Create2DWorkspace(1, M ); + double fractional_error = 0.01; // error taken as a percent of the signal + for( size_t i = 0; i < M; i++ ) + { + double bin_boundary = dataX[ i ] - dw / 2.0; // bin boundaries are shifted by half the bind width + double y = I * ( 2.0 / M_PI ) * A1 * ( 3.0 * rate / ( 9.0 * rate * rate + dataX[ i ] * dataX[ i ]) ); // verbose for clarity + ws -> dataX( 0 )[ i ] = bin_boundary ; + ws -> dataY( 0 )[ i ] = y; + ws -> dataE( 0 )[ i ] = fractional_error * y; // assume the error is a small percent of the actual value + } + ws -> dataX( 0 )[ M ] = dataX[ M - 1 ] + dw/2; // recall number of bin boundaries is 1 + #bins + + // return now the workspace + return ws; + } + + + /// save a worskapece to a nexus file + void saveWorkspace( Mantid::DataObjects::Workspace2D_sptr &ws, const std::string &filename ) + { + auto save = Mantid::API::AlgorithmFactory::Instance().create( "SaveNexus", 1 ); + if ( !save ) throw std::runtime_error( "Algorithm not created" ); + save -> initialize(); + save -> setProperty( "Filename", filename ); + save -> setProperty( "InputWorkspace", ws ); + save->execute(); + } + + + // create a data workspace using a Fit algorithm + Mantid::DataObjects::Workspace2D_sptr generateWorkspaceFromFitAlgorithm( Mantid::CurveFitting::Fit & fitalg ) + { + // initialize some frequency values centered around zero. Will work as dataX + const size_t M = 1001; + double dataX[ M ]; + const double dw = 0.4; // typical bin width for BASIS@ORNL beamline, in micro-seconds + for( size_t i = 0; i < M; i++ ) dataX[i] = (static_cast(i) - M/2 ) * dw; + + // Evaluate the fitting function. Will work as dataY + Mantid::API::FunctionDomain1DView dataXview( &dataX[0], M ); + Mantid::API::FunctionValues dataYvalues( dataXview ); + Mantid::API::IFunction_sptr fitalg_function = fitalg.getProperty( "Function" ); + fitalg_function -> function( dataXview, dataYvalues ); + + // create the workspace + auto ws = WorkspaceCreationHelper::Create2DWorkspace(1, M ); + double fractional_error = 0.01; // error taken as a percent of the signal + for( size_t i = 0; i < M; i++ ) + { + ws -> dataX( 0 )[ i ] = dataX[ i ] - dw/2; // bin boundaries are shifted by half the bind width + ws -> dataY( 0 )[ i ] = dataYvalues.getCalculated( i ); + ws -> dataE( 0 )[ i ] = fractional_error * dataYvalues.getCalculated( i ); // assume the error is a small percent of the actual value + } + ws -> dataX( 0 )[ M ] = dataX[ M - 1 ] + dw/2; // recall number of bin boundaries is 1 + #bins + + // return now the workspace + return ws; + } + +}; + + + + + + + + + + + + + + + + + + + + +#endif /* DIFFROTDISCRETECIRCLETEST_H_ */ diff --git a/Code/Mantid/Framework/CurveFitting/test/DiffSphereTest.h b/Code/Mantid/Framework/CurveFitting/test/DiffSphereTest.h index a6cd038c2224..92331903ddc9 100644 --- a/Code/Mantid/Framework/CurveFitting/test/DiffSphereTest.h +++ b/Code/Mantid/Framework/CurveFitting/test/DiffSphereTest.h @@ -3,382 +3,326 @@ #include #include +#include +#include #include -#include "MantidCurveFitting/Fit.h" +#include #include "MantidCurveFitting/DiffSphere.h" - -#include "MantidCurveFitting/LogNormal.h" - -#include "MantidCurveFitting/DeltaFunction.h" +#include "MantidCurveFitting/Gaussian.h" #include "MantidCurveFitting/Convolution.h" -#include -#include "MantidAPI/ParameterTie.h" +#include "MantidTestHelpers/WorkspaceCreationHelper.h" +#include "MantidCurveFitting/Fit.h" #include "MantidAPI/FunctionFactory.h" +#include "MantidAPI/AlgorithmFactory.h" -using namespace Mantid::Kernel; -using namespace Mantid::API; -using namespace Mantid::CurveFitting; -using namespace Mantid::DataObjects; -using namespace Mantid::DataHandling; - -//same class as ConvolutionTest_Gauss in ConvolutionTest.h -class DiffSphereTest_Gauss: public IPeakFunction +class DiffSphereTest : public CxxTest::TestSuite { public: -DiffSphereTest_Gauss() -{ - /// center of the peak - declareParameter("c"); + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static DiffSphereTest *createSuite() { return new DiffSphereTest(); } + static void destroySuite( DiffSphereTest *suite ) { delete suite; } - /// height of the peak - declareParameter("h",1.); + /// Convolve the elastic part with a resolution function, here a Gaussian + void testDiffSphereElastic() + { - /// 1/(2*sigma^2) - declareParameter("s",1.); -} + // define the fit function + std::string funtion_string = "(composite=Convolution,FixResolution=true,NumDeriv=true;name=Gaussian,Height=1.0,PeakCentre=0.0,Sigma=0.002,ties=(Height=1.0,PeakCentre=0.0,Sigma=0.002);name=ElasticDiffSphere,Q=0.5,Height=47.014,Radius=3.567)"; + + // Initialize the fit function in the Fit algorithm + Mantid::CurveFitting::Fit fitalg; + TS_ASSERT_THROWS_NOTHING( fitalg.initialize() ); + TS_ASSERT( fitalg.isInitialized() ); + fitalg.setProperty( "Function", funtion_string ); + + // create the data workspace by evaluating the fit function in the Fit algorithm + auto data_workspace = generateWorkspaceFromFitAlgorithm( fitalg ); + //saveWorkspace( data_workspace, "/tmp/junk.nxs" ); // for debugging purposes only + + /* override the function with new parameters, then do the Fit. The effect of ElasticDiffSphere + * is to multiply the Gaussian by height*[3*j_1(Q*Radius)/(Q*Radius)]^2. Thus, an increase in + * parameter 'height' can be offset by an increase in the Radius. These parameters are coupled + * and thus no unique fit exists. Thus, we fix parameter height and fit the radius. + */ + funtion_string = "(composite=Convolution,NumDeriv=true;name=Gaussian,Height=1.0,PeakCentre=0.0,Sigma=0.002,ties=(Height=1.0,PeakCentre=0.0,Sigma=0.002);name=ElasticDiffSphere,Q=0.5,Height=47.014,Radius=6.0,ties=(Height=47.014))"; + fitalg.setProperty( "Function", funtion_string ); + fitalg.setProperty( "InputWorkspace", data_workspace ); + fitalg.setPropertyValue( "WorkspaceIndex", "0" ); + TS_ASSERT_THROWS_NOTHING( TS_ASSERT( fitalg.execute() ) ); + TS_ASSERT( fitalg.isExecuted() ); + + // check Chi-square is small + const double chi_squared = fitalg.getProperty("OutputChi2overDoF"); + TS_ASSERT_LESS_THAN( chi_squared, 0.001 ); + //std::cout << "\nchi_squared = " << chi_squared << "\n"; // only for debugging purposes + + // check the parameters of the resolution did not change + Mantid::API::IFunction_sptr fitalg_function = fitalg.getProperty( "Function" ); + auto fitalg_conv = boost::dynamic_pointer_cast( fitalg_function ) ; + Mantid::API::IFunction_sptr fitalg_resolution = fitalg_conv->getFunction( 0 ); + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "PeakCentre" ), 0.0, 0.00001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Height" ), 1.0, 1.0 * 0.001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Sigma" ), 0.002, 0.002* 0.001 ); // allow for a small percent variation + //std::cout << "\nPeakCentre = " << fitalg_resolution->getParameter("PeakCentre") << " Height= " << fitalg_resolution->getParameter("Height") << " Sigma=" << fitalg_resolution->getParameter("Sigma") << "\n"; // only for debugging purposes + + // check the parameters of the elastic part + Mantid::API::IFunction_sptr fitalg_structure_factor = fitalg_conv->getFunction( 1 ); + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Height" ), 47.014, 47.014 * 0.05 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Radius" ), 3.567, 3.567 * 0.05 ); // allow for a small percent variation + //std::cout << "\nGOAL: Height = 47.014, Radius = 1.567\n"; // only for debugging purposes + //std::cout << "OPTIMIZED: Height = " << fitalg_structure_factor->getParameter("Height") << " Radius = " << fitalg_structure_factor->getParameter("Radius")<< "\n"; // only for debugging purposes -std::string name()const{return "DiffSphereTest_Gauss";} + } -void functionLocal(double* out, const double* xValues, const size_t nData)const -{ - double c = getParameter("c"); - double h = getParameter("h"); - double w = getParameter("s"); - for(size_t i=0;i elastic_part( new Mantid::CurveFitting::ElasticDiffSphere() ); + elastic_part -> setParameter( "Height", I ); + elastic_part -> setParameter( "Radius", R ); + elastic_part -> setAttributeValue( "Q", Q ); + elastic_part -> init(); + + // initialize the inelastic part + boost::shared_ptr inelastic_part( new Mantid::CurveFitting::InelasticDiffSphere() ); + inelastic_part -> setParameter( "Intensity", I ); + inelastic_part -> setParameter( "Radius", R ); + inelastic_part -> setParameter( "Diffusion", D ); + inelastic_part -> setAttributeValue( "Q", Q ); + inelastic_part -> init(); + + // calculate the normalization over different values of Q*R + while( Q*R < QR_max ) + { + elastic_part -> setParameter( "Radius", R ); + double elastic_intensity = elastic_part -> HeightPrefactor(); // A_{0,0} coefficient + inelastic_part -> setParameter( "Radius", R ); + std::vector< double > YJ = inelastic_part -> LorentzianCoefficients( Q * R ); // (2*l+1) * A_{n,l} coefficients + double inelastic_intensity = std::accumulate( YJ.begin(), YJ.end(), 0.0 ); + TS_ASSERT_DELTA( elastic_intensity + inelastic_intensity, 1.0, 0.02 ); // Allow for a 2% deviation + R += dR; + } } -} -void functionDerivLocal(Jacobian* out, const double* xValues, const size_t nData) -{ - //throw Mantid::Kernel::Exception::NotImplementedError(""); - double c = getParameter("c"); - double h = getParameter("h"); - double w = getParameter("s"); - for(size_t i=0;iset(i,0,x*h*e*w); - out->set(i,1,e); - out->set(i,2,-x*x*h*e); + // target fitting parameters + const double I_0(47.014); + const double R_0(2.1); + const double D_0(0.049); + const double Q(0.5); + + // Initialize the fit function in the Fit algorithm + Mantid::CurveFitting::Fit fitalg; + TS_ASSERT_THROWS_NOTHING( fitalg.initialize() ); + TS_ASSERT( fitalg.isInitialized() ); + std::ostringstream funtion_stream; + funtion_stream << "(composite=Convolution,FixResolution=true,NumDeriv=true;name=Gaussian,Height=1.0," + << "PeakCentre=0.0,Sigma=0.002,ties=(Height=1.0,PeakCentre=0.0,Sigma=0.002);" + << "name=InelasticDiffSphere,Q=" << boost::lexical_cast( Q ) << ",Intensity=" + << boost::lexical_cast( I_0 ) << ",Radius=" << boost::lexical_cast( R_0 ) + << ",Diffusion=" << boost::lexical_cast( D_0 ) << ")"; + fitalg.setProperty( "Function", funtion_stream.str() ); + + // create the data workspace by evaluating the fit function in the Fit algorithm + auto data_workspace = generateWorkspaceFromFitAlgorithm( fitalg ); + //saveWorkspace( data_workspace, "/tmp/junk_data.nxs" ); // for debugging purposes only + + // override the function with new parameters, our initial guess. + double I = I_0 * ( 0.75 + ( 0.5 * std::rand() ) / RAND_MAX ); + double R = R_0 * ( 0.75 + ( 0.5 * std::rand() ) / RAND_MAX ); + double D = D_0 * ( 0.75 + ( 0.5 * std::rand() ) / RAND_MAX ); + funtion_stream.str( std::string() ); + funtion_stream.clear(); + funtion_stream << "(composite=Convolution,FixResolution=true,NumDeriv=true;name=Gaussian,Height=1.0," + << "PeakCentre=0.0,Sigma=0.002,ties=(Height=1.0,PeakCentre=0.0,Sigma=0.002);" + << "name=InelasticDiffSphere,Q=" << boost::lexical_cast( Q ) << ",Intensity=" + << boost::lexical_cast( I ) << ",Radius=" << boost::lexical_cast( R ) + << ",Diffusion=" << boost::lexical_cast( D ) << ")"; + fitalg.setProperty( "Function", funtion_stream.str() ); + //auto before_workspace = generateWorkspaceFromFitAlgorithm( fitalg ); // for debugging purposes only + //saveWorkspace( before_workspace, "/tmp/junk_before_fitting.nxs" ); // for debugging purposes only + + // Do the fit + fitalg.setProperty( "InputWorkspace", data_workspace ); + fitalg.setPropertyValue( "WorkspaceIndex", "0" ); + TS_ASSERT_THROWS_NOTHING( TS_ASSERT( fitalg.execute() ) ); + TS_ASSERT( fitalg.isExecuted() ); + //auto after_workspace = generateWorkspaceFromFitAlgorithm( fitalg ); // for debugging purposes only + //saveWorkspace( after_workspace, "/tmp/junk_after_fitting.nxs" ); // for debugging purposes only + + // check Chi-square is small + const double chi_squared = fitalg.getProperty("OutputChi2overDoF"); + TS_ASSERT_LESS_THAN( chi_squared, 0.001 ); + //std::cout << "\nchi_squared = " << chi_squared << "\n"; // only for debugging purposes + + // check the parameters of the resolution did not change + Mantid::API::IFunction_sptr fitalg_function = fitalg.getProperty( "Function" ); + auto fitalg_conv = boost::dynamic_pointer_cast( fitalg_function ) ; + Mantid::API::IFunction_sptr fitalg_resolution = fitalg_conv->getFunction( 0 ); + + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "PeakCentre" ), 0.0, 0.00001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Height" ), 1.0, 1.0 * 0.001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Sigma" ), 0.002, 0.002* 0.001 ); // allow for a small percent variation + //std::cout << "\nPeakCentre = " << fitalg_resolution->getParameter("PeakCentre") << " Height= " << fitalg_resolution->getParameter("Height") << " Sigma=" << fitalg_resolution->getParameter("Sigma") << "\n"; // only for debugging purposes + + // check the parameters of the inelastic part close to the target parameters + Mantid::API::IFunction_sptr fitalg_structure_factor = fitalg_conv->getFunction( 1 ); + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Intensity" ), I_0, I_0 * 0.05 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Radius" ), R_0, R_0 * 0.05 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Diffusion" ), D_0, D_0 * 0.05 ); // allow for a small percent variation + //std::cout << "\nINITIAL GUESS: Intensity = "<(I)<<", Radius ="<(R)<<", Diffusion = "<(D)<<"\n"; // only for debugging purposes + //std::cout << "GOAL: Intensity = "<(I_0)<<", Radius = "<(R_0)<<", Diffusion = "<(D_0)<<"\n"; // only for debugging purposes + //std::cout << "OPTIMIZED: Intensity = " << fitalg_structure_factor->getParameter("Intensity") << " Radius = " << fitalg_structure_factor->getParameter("Radius") << " Diffusion = " << fitalg_structure_factor->getParameter("Diffusion") << "\n"; // only for debugging purposes } -} -double centre()const -{ - return getParameter(0); -} - -double height()const -{ - return getParameter(1); -} - -double fwhm()const -{ - return getParameter(2); -} - -void setCentre(const double c) -{ - setParameter(0,c); -} - -void setHeight(const double h) -{ - setParameter(1,h); -} - -void setFwhm(const double w) -{ - setParameter(2,w); -} - -}; - -DECLARE_FUNCTION(DiffSphereTest_Gauss); //register DiffSphereTest_Gauss in the DynamicFactory + void testDiffSphere() + { + // target parameters + const double I_0(47.014); + const double R_0(2.1); + const double D_0(0.049); + const double Q(0.5); + + // Initialize the fit function in the Fit algorithm + Mantid::CurveFitting::Fit fitalg; + TS_ASSERT_THROWS_NOTHING( fitalg.initialize() ); + TS_ASSERT( fitalg.isInitialized() ); + std::ostringstream funtion_stream; + funtion_stream << "(composite=Convolution,FixResolution=true,NumDeriv=true;name=Gaussian,Height=1.0," + << "PeakCentre=0.0,Sigma=0.002,ties=(Height=1.0,PeakCentre=0.0,Sigma=0.002);" + << "name=DiffSphere,Q=" << boost::lexical_cast( Q ) << ",Intensity=" + << boost::lexical_cast( I_0 ) << ",Radius=" << boost::lexical_cast( R_0 ) + << ",Diffusion=" << boost::lexical_cast( D_0 ) << ")"; + fitalg.setProperty( "Function", funtion_stream.str() ); + + // Find out whether ties were correctly applied + Mantid::API::IFunction_sptr fitalg_function = fitalg.getProperty( "Function" ); // main function + fitalg_function->initialize(); + auto fitalg_conv = boost::dynamic_pointer_cast( fitalg_function ) ; // cast to Convolution + fitalg_function = fitalg_conv->getFunction( 1 ); // DiffSphere + auto fitalg_structure_factor = boost::dynamic_pointer_cast( fitalg_function ); + + fitalg_function = fitalg_structure_factor->getFunction( 0 ); + auto fitalg_elastic = boost::dynamic_pointer_cast( fitalg_function ) ; + TS_ASSERT_DELTA( fitalg_elastic -> getParameter( "Height" ), I_0, std::numeric_limits::epsilon() ); + TS_ASSERT_DELTA( fitalg_elastic -> getParameter( "Radius" ), R_0, std::numeric_limits::epsilon() ); + TS_ASSERT_DELTA( fitalg_elastic -> getAttribute( "Q" ).asDouble(), Q, std::numeric_limits::epsilon() ); + //std::cout << "Height=" << fitalg_elastic -> getParameter( "Height" ) << " Radius=" << fitalg_elastic -> getParameter( "Radius" ) << "\n"; // for debugging purposes only + + fitalg_function = fitalg_structure_factor->getFunction( 1 ); + auto fitalg_inelastic = boost::dynamic_pointer_cast( fitalg_function ) ; + TS_ASSERT_DELTA( fitalg_inelastic -> getParameter( "Intensity" ), I_0, std::numeric_limits::epsilon() ); + TS_ASSERT_DELTA( fitalg_inelastic -> getParameter( "Radius" ), R_0, std::numeric_limits::epsilon() ); + TS_ASSERT_DELTA( fitalg_inelastic -> getParameter( "Diffusion" ), D_0, std::numeric_limits::epsilon() ); + TS_ASSERT_DELTA( fitalg_inelastic -> getAttribute( "Q" ).asDouble(), Q, std::numeric_limits::epsilon() ); + //std::cout << "Intensity=" << fitalg_inelastic->getParameter( "Intensity" ) << " Radius=" << fitalg_inelastic->getParameter( "Radius" ) << " Diffusion=" << fitalg_inelastic->getParameter( "Diffusion" ) <<"\n"; // for debugging purposes only + + // override the function with new parameters, our initial guess. + double I = I_0 * ( 0.75 + ( 0.5 * std::rand() ) / RAND_MAX ); + double R = R_0 * ( 0.75 + ( 0.5 * std::rand() ) / RAND_MAX ); + double D = D_0 * ( 0.75 + ( 0.5 * std::rand() ) / RAND_MAX ); + funtion_stream.str( std::string() ); + funtion_stream.clear(); + funtion_stream << "(composite=Convolution,FixResolution=true,NumDeriv=true;name=Gaussian,Height=1.0," + << "PeakCentre=0.0,Sigma=0.002,ties=(Height=1.0,PeakCentre=0.0,Sigma=0.002);" + << "name=DiffSphere,Q=" << boost::lexical_cast( Q ) << ",Intensity=" + << boost::lexical_cast( I ) << ",Radius=" << boost::lexical_cast( R ) + << ",Diffusion=" << boost::lexical_cast( D ) << ")"; + fitalg.setProperty( "Function", funtion_stream.str() ); + + // create the data workspace by evaluating the fit function in the Fit algorithm + auto data_workspace = generateWorkspaceFromFitAlgorithm( fitalg ); + //saveWorkspace( data_workspace, "/tmp/junk_data.nxs" ); // for debugging purposes only + + // Do the fit + fitalg.setProperty( "InputWorkspace", data_workspace ); + fitalg.setPropertyValue( "WorkspaceIndex", "0" ); + TS_ASSERT_THROWS_NOTHING( TS_ASSERT( fitalg.execute() ) ); + TS_ASSERT( fitalg.isExecuted() ); + + // check Chi-square is small + const double chi_squared = fitalg.getProperty("OutputChi2overDoF"); + TS_ASSERT_LESS_THAN( chi_squared, 0.001 ); + //std::cout << "\nchi_squared = " << chi_squared << "\n"; // only for debugging purposes + + // check the parameters of the resolution did not change + Mantid::API::IFunction_sptr fitalg_resolution = fitalg_conv->getFunction( 0 ); + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "PeakCentre" ), 0.0, 0.00001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Height" ), 1.0, 1.0 * 0.001 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_resolution -> getParameter( "Sigma" ), 0.002, 0.002* 0.001 ); // allow for a small percent variation + //std::cout << "\nPeakCentre = " << fitalg_resolution->getParameter("PeakCentre") << " Height= " << fitalg_resolution->getParameter("Height") << " Sigma=" << fitalg_resolution->getParameter("Sigma") << "\n"; // only for debugging purposes + + // check the parameters of the DiffSphere close to the target parameters + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Intensity" ), I_0, I_0 * 0.05 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Radius" ), R_0, R_0 * 0.05 ); // allow for a small percent variation + TS_ASSERT_DELTA( fitalg_structure_factor -> getParameter( "Diffusion" ), D_0, D_0 * 0.05 ); // allow for a small percent variation + //std::cout << "\nINITIAL GUESS: Intensity = "<(I)<<", Radius ="<(R)<<", Diffusion = "<(D)<<"\n"; // only for debugging purposes + //std::cout << "GOAL: Intensity = "<(I_0)<<", Radius = "<(R_0)<<", Diffusion = "<(D_0)<<"\n"; // only for debugging purposes + //std::cout << "OPTIMIZED: Intensity = " << fitalg_structure_factor->getParameter("Intensity") << " Radius = " << fitalg_structure_factor->getParameter("Radius") << " Diffusion = " << fitalg_structure_factor->getParameter("Diffusion") << "\n"; // only for debugging purposes + } -class DiffSphereTest : public CxxTest::TestSuite -{ -public: +private: -void getMockData(Mantid::MantidVec& x, Mantid::MantidVec& y, Mantid::MantidVec& e) -{ -// values extracted from theoretical stucture factor S(Q=0.9,w) of diffusion -// within a sphere of radius 2.66Angstroms and diffusion coefficient 1.45 - unsigned int nvalues=200; - //structure factor - double S[]= + /// save a worskapece to a nexus file + void saveWorkspace( Mantid::DataObjects::Workspace2D_sptr &ws, const std::string &filename ) { - 0.16243,0.162411,0.162353,0.162257,0.162123,0.16195,0.16174,0.161492,0.161207,0.160886, - 0.160528,0.160135,0.159706,0.159243,0.158746,0.158216,0.157653,0.157059,0.156434,0.155779, - 0.155094,0.154382,0.153642,0.152876,0.152084,0.151268,0.150428,0.149566,0.148682,0.147778, - 0.146855,0.145913,0.144953,0.143977,0.142985,0.141979,0.140959,0.139927,0.138883,0.137828, - 0.136764,0.13569,0.134609,0.13352,0.132425,0.131324,0.130219,0.12911,0.127997,0.126882, - 0.125765,0.124647,0.123529,0.122411,0.121293,0.120177,0.119062,0.117951,0.116841,0.115736, - 0.114634,0.113536,0.112444,0.111356,0.110273,0.109197,0.108126,0.107062,0.106005,0.104955, - 0.103912,0.102877,0.101849,0.100829,0.099817,0.0988135,0.0978183,0.0968318,0.0958539,0.0948848, - 0.0939247,0.0929735,0.0920314,0.0910985,0.0901748,0.0892603,0.0883551,0.0874593,0.0865727, - 0.0856956,0.0848278,0.0839694,0.0831203,0.0822806,0.0814502,0.0806291,0.0798172,0.0790146, - 0.0782212,0.0774369,0.0766618,0.0758956,0.0751385,0.0743902,0.0736509,0.0729203,0.0721985, - 0.0714853,0.0707807,0.0700846,0.0693969,0.0687176,0.0680466,0.0673837,0.066729,0.0660823, - 0.0654436,0.0648127,0.0641895,0.0635741,0.0629663,0.0623659,0.061773,0.0611875,0.0606092, - 0.060038,0.059474,0.0589169,0.0583667,0.0578234,0.0572868,0.0567568,0.0562334,0.0557164, - 0.0552059,0.0547016,0.0542036,0.0537118,0.0532259,0.0527461,0.0522722,0.0518041,0.0513417, - 0.050885,0.0504339,0.0499882,0.049548,0.0491132,0.0486837,0.0482593,0.0478401,0.047426, - 0.0470169,0.0466126,0.0462133,0.0458187,0.0454289,0.0450437,0.0446631,0.044287,0.0439154, - 0.0435482,0.0431853,0.0428267,0.0424723,0.0421221,0.0417759,0.0414338,0.0410957,0.0407615, - 0.0404312,0.0401047,0.039782,0.0394629,0.0391475,0.0388358,0.0385276,0.0382228,0.0379216, - 0.0376237,0.0373293,0.0370381,0.0367502,0.0364655,0.036184,0.0359056,0.0356303,0.0353581, - 0.0350888,0.0348225,0.0345592,0.0342987,0.0340411,0.0337862,0.0335342,0.0332848,0.0330382, - 0.0327942,0.0325528,0.032314 - }; - double wi=0.0, dw=0.01; //initial and delta frequency - double w=wi; - double cc=0.1; //estimate errors as ten percent of the structure factor - for(unsigned int i=0; i initialize(); + save -> setProperty( "Filename", filename ); + save -> setProperty( "InputWorkspace", ws ); + save->execute(); } -} // end of void getMockData -//convolve a Gaussian resolution function with a delta-Dirac of type ElasticDiffSphere -void testElasticDiffSphere() -{ - Convolution conv; - //set the resolution function - double h = 3.0; // height - double a = 1.3; // 1/(2*sigma^2) - IFunction_sptr res( new DiffSphereTest_Gauss ); - res->setParameter("c",0); - res->setParameter("h",h); - res->setParameter("s",a); - conv.addFunction(res); - - //set the "structure factor" - double H=1.5, R=2.6, Q=0.7; - IFunction_sptr eds( new ElasticDiffSphere() ); - eds->setParameter("Height",H); - eds->setParameter("Radius",R); - eds->setAttributeValue("Q",Q); - conv.addFunction(eds); - - //set up some frequency values centered around zero - const int N = 117; - double w[N],w0,dw = 0.13; - w0=-dw*int(N/2); - for(int i=0;i(i) - M/2 ) * dw; + + // Evaluate the fitting function. Will work as dataY + Mantid::API::FunctionDomain1DView dataXview( &dataX[0], M ); + Mantid::API::FunctionValues dataYvalues( dataXview ); + Mantid::API::IFunction_sptr fitalg_function = fitalg.getProperty( "Function" ); + fitalg_function -> function( dataXview, dataYvalues ); + + // create the workspace + auto ws = WorkspaceCreationHelper::Create2DWorkspace(1, M ); + double fractional_error = 0.01; // error taken as a percent of the signal + for( size_t i = 0; i < M; i++ ) + { + ws -> dataX( 0 )[ i ] = dataX[ i ] - dw/2; // bin boundaries are shifted by half the bind width + ws -> dataY( 0 )[ i ] = dataYvalues.getCalculated( i ); + ws -> dataE( 0 )[ i ] = fractional_error * dataYvalues.getCalculated( i ); // assume the error is a small percent of the actual value + } + ws -> dataX( 0 )[ M ] = dataX[ M - 1 ] + dw/2; // recall number of bin boundaries is 1 + #bins + + // return now the workspace + return ws; } -}// end of testElasticDiffSphere -void testInelasticDiffSphere() -{ - Fit alg; - TS_ASSERT_THROWS_NOTHING(alg.initialize()); - TS_ASSERT( alg.isInitialized() ); - // create mock data to test against - std::string wsName = "InelasticDiffSphereMockData"; - int histogramNumber = 1; - int timechannels = 200; - - Workspace_sptr ws = WorkspaceFactory::Instance().create("Workspace2D",histogramNumber,timechannels,timechannels); - Workspace2D_sptr ws2D = boost::dynamic_pointer_cast(ws); - - Mantid::MantidVec& x = ws2D->dataX(0); // x-values (frequencies) - Mantid::MantidVec& y = ws2D->dataY(0); // y-values (structure factor) - Mantid::MantidVec& e = ws2D->dataE(0); // error values of the structure factor - getMockData(x, y, e); - - //put this workspace in the data service - TS_ASSERT_THROWS_NOTHING(AnalysisDataService::Instance().addOrReplace(wsName, ws2D)); - - boost::shared_ptr fn(new InelasticDiffSphere); - fn->initialize(); - - //Start with some initial values - fn->setParameter("Intensity",0.4); - fn->setParameter("Radius",1.1); - fn->setParameter("Diffusion",1.2); - fn->setAttributeValue("Q",0.7); - - alg.setProperty( "Function",boost::dynamic_pointer_cast(fn)); - // Set which spectrum to fit against and initial starting values - alg.setPropertyValue("InputWorkspace", wsName); - alg.setPropertyValue("WorkspaceIndex","0"); - alg.setPropertyValue("StartX","0"); - alg.setPropertyValue("EndX","200"); - - // execute fit - TS_ASSERT_THROWS_NOTHING( TS_ASSERT( alg.execute() ) ); - - TS_ASSERT( alg.isExecuted() ); - - // test the output from fit is what you expect - double dummy = alg.getProperty("OutputChi2overDoF"); - TS_ASSERT_DELTA( dummy, 0.001,0.001); - auto out = FunctionFactory::Instance().createInitialized(alg.getPropertyValue("Function")); - TS_ASSERT_DELTA( out->getParameter("Radius"), 2.66 ,0.05); - TS_ASSERT_DELTA( out->getParameter("Diffusion"), 1.45 ,0.05); - AnalysisDataService::Instance().remove(wsName); - -}// end of void testInelastic - -//assert the ties between the elastic and inelastic contributions -void testDiffSphereTies() -{ - double I=2.9, R=2.3, D=0.45, Q=0.7; - DiffSphere ds; - ds.setParameter("f1.Intensity",I); - ds.setParameter("f1.Radius",R); - ds.setParameter("f1.Diffusion",D); - ds.setAttributeValue("Q",Q ); - auto ids = boost::dynamic_pointer_cast(ds.getFunction(1)); - TS_ASSERT_EQUALS( ids->getParameter("Intensity"), I ); - TS_ASSERT_EQUALS( ids->getParameter("Radius"), R ); - TS_ASSERT_EQUALS( ids->getParameter("Diffusion"), D ); - TS_ASSERT_EQUALS( ids->getAttribute("Q").asDouble(), Q ); - - ds.applyTies(); //elastic parameters are tied to inelastic parameters - //check the ties were applied correctly - auto eds = boost::dynamic_pointer_cast( ds.getFunction(0) ); - - TS_ASSERT_EQUALS( eds->getParameter("Height") , I ); - TS_ASSERT_EQUALS( eds->getParameter("Radius") , R ); - TS_ASSERT_EQUALS( eds->getAttribute("Q").asDouble(), Q ); -} - -//Internal consistency test. -//First generate data from convolution of a Gaussian and a DiffSphere function. -//Then reset the DiffSphere parameters and do the fit to recover original parameter values -void testDiffSphere(){ - - //Resolution function is a gaussian - double h = 3.0; // height - double a = 1.3; // 1/(2*sigma^2), sigma=0.62 - boost::shared_ptr res(new DiffSphereTest_Gauss); - res->setParameter("c",0); - res->setParameter("h",h); - res->setParameter("s",a); - - //Structure factor is a DiffSphere model - double I=2.9, Q=0.70, R=2.3, D=0.45; - boost::shared_ptr ds(new DiffSphere); - ds->setParameter("Intensity",I); //f0==elastic, f1==inelastic - ds->setParameter("Radius",R); - ds->setParameter("Diffusion",D); - ds->applyTies(); //elastic parameters are tied to inelastic parameters - ds->setAttributeValue("Q",Q); - - //std:: << "\n(DiffSphereTest.cpp): " << ds->asString() << std::endl; - - //set up some frequency values centered around zero - const int N = 117; - double w[N],w0,dw = 0.13; - w0=-dw*int(N/2); - for(int i=0;i(new Convolution); - conv->addFunction( boost::dynamic_pointer_cast(res) ); - conv->addFunction( boost::dynamic_pointer_cast(ds) ); - conv->function(xView,values); //'values' contains the convolution of 'res' and 'ds' - - //Set up the workspace. Store the result in the convolution as a histogram - std::string wsName = "ConvGaussDiffSphere"; - int histogramNumber = 1; - int timechannels = N; - Workspace_sptr ws = WorkspaceFactory::Instance().create("Workspace2D",histogramNumber,timechannels,timechannels); - Workspace2D_sptr ws2D = boost::dynamic_pointer_cast(ws); - Mantid::MantidVec& x = ws2D->dataX(0); // x-values (frequencies) - Mantid::MantidVec& y = ws2D->dataY(0); // y-values (structure factor) - Mantid::MantidVec& e = ws2D->dataE(0); // error values of the structure factor - const double cc=0.1; - for(int i=0; isetParameter("Intensity",1.3); //target I=2.9 - ds->setParameter("Radius",1.4); //target R=2.3 - ds->setParameter("Diffusion",1.5); //target D=0.45 - - ds->applyTies(); //update the ties between elastic and inelastic parts - - //Initialize the fitting algorithm - Fit alg; - TS_ASSERT_THROWS_NOTHING(alg.initialize()); - TS_ASSERT( alg.isInitialized() ); - - //alg.setProperty( "Function", boost::dynamic_pointer_cast(conv) ); - //std::cout << "(DiffSphereTest.h) " << conv->asString() << std::endl; - //alg.setProperty( "Function", conv->asString() ) ; - std::string problematic = "composite=Convolution;name=DiffSphereTest_Gauss,c=0,h=3,s=1.3,ties=(c=0,h=3,s=1.3);(name=DiffSphere,NumDeriv=true,Q=0.7,Intensity=2.044325,Radius=3.285822,Diffusion=0.338958)"; - alg.setProperty( "Function", problematic ); - //alg.setProperty( "Minimizer", "Simplex"); - - //Set which spectrum to fit against and initial starting values - alg.setPropertyValue("InputWorkspace", wsName); - alg.setPropertyValue("WorkspaceIndex","0"); - alg.setPropertyValue( "StartX", boost::lexical_cast(w[0]) ); - alg.setPropertyValue("EndX", boost::lexical_cast(w[N-1]) ); - - //print the initial parameters. For debugging purposes only - IFunction_sptr algFz = alg.getProperty("Function"); - auto fnConvz = boost::dynamic_pointer_cast( algFz ) ; - auto fnDiffSpherez = boost::dynamic_pointer_cast( fnConvz->getFunction(1) ); - //std::cout << fnDiffSpherez->getParameter("f0.Height") << " " << fnDiffSpherez->getParameter("f0.Height") << " " << fnDiffSpherez->getParameter("f0.Radius") << " " << fnDiffSpherez->getParameter("f1.Intensity") << " " << fnDiffSpherez->getParameter("f1.Radius") << " " << fnDiffSpherez->getParameter("f1.Diffusion") << "\n"; - //std::cout << "(DiffSphereTest.h)" << alg.getPropertyValue("Function") << std::endl; - - - TS_ASSERT_THROWS_NOTHING( TS_ASSERT( alg.execute() ) ); //do the fit - - //Check the original parameters are recovered. - IFunction_sptr algF = alg.getProperty("Function"); - auto fnConv = boost::dynamic_pointer_cast( algF ) ; - IFunction_sptr outGauss = fnConv->getFunction(0); - TS_ASSERT_EQUALS(outGauss->getParameter("c"),0.0); - TS_ASSERT_EQUALS(outGauss->getParameter("h"),h); - TS_ASSERT_EQUALS(outGauss->getParameter("s"),a); - auto fnDiffSphere = boost::dynamic_pointer_cast( fnConv->getFunction(1) ); - double tolerance = 1e-03; - //std::cout << fnDiffSphere->getParameter("f0.Height") << " " << fnDiffSphere->getParameter("f0.Height") << " " << fnDiffSphere->getParameter("f0.Radius") << " " << fnDiffSphere->getParameter("f1.Intensity") << " " << fnDiffSphere->getParameter("f1.Radius") << " " << fnDiffSphere->getParameter("f1.Diffusion") << " " << fnDiffSphere->getAttribute("Q").asDouble() << "\n"; - TS_ASSERT_DELTA( fnDiffSphere->getParameter("f0.Height"), I, I*tolerance); - TS_ASSERT_DELTA( fnDiffSphere->getParameter("f0.Radius"), R, R*tolerance); - TS_ASSERT_DELTA( fnDiffSphere->getParameter("f1.Intensity"), I, I*tolerance); - TS_ASSERT_DELTA( fnDiffSphere->getParameter("f1.Radius"), R, R*tolerance); - TS_ASSERT_DELTA( fnDiffSphere->getParameter("f1.Diffusion"), D, D*tolerance); - TS_ASSERT_DELTA( fnDiffSphere->getParameter("Intensity"), I, I*tolerance); - TS_ASSERT_DELTA( fnDiffSphere->getParameter("Radius"), R, R*tolerance); - TS_ASSERT_DELTA( fnDiffSphere->getParameter("Diffusion"), D, D*tolerance); - - double dummy = alg.getProperty("OutputChi2overDoF"); - TS_ASSERT_DELTA( dummy, tolerance, tolerance); - -} // end of testDiffSphere - -}; +}; // class DiffSphereTest #endif /*DIFFSPHERETEST_H_*/ diff --git a/Code/Mantid/Framework/CurveFitting/test/ProcessBackgroundTest.h b/Code/Mantid/Framework/CurveFitting/test/ProcessBackgroundTest.h index 9726c5c19319..94b58c3fd4b2 100644 --- a/Code/Mantid/Framework/CurveFitting/test/ProcessBackgroundTest.h +++ b/Code/Mantid/Framework/CurveFitting/test/ProcessBackgroundTest.h @@ -176,12 +176,6 @@ class ProcessBackgroundTest : public CxxTest::TestSuite */ void test_SimpleBackgroundGeneration() { - - // 1. Prepare for data - /* - std::string datafile("/home/wzz/Mantid/Code/debug/MyTestData/4862b7.inp"); - DataObjects::Workspace2D_sptr dataws = createWorkspace2D(datafile); - */ // 1. Create Workspace2D DataObjects::Workspace2D_sptr dataws = boost::dynamic_pointer_cast @@ -245,7 +239,7 @@ class ProcessBackgroundTest : public CxxTest::TestSuite DataObjects::Workspace2D_sptr dataws = boost::dynamic_pointer_cast (API::WorkspaceFactory::Instance().create("Workspace2D", 1, 1000, 1000)); - for (size_t i = 0; i < 10; ++i) + for (size_t i = 0; i < 1000; ++i) { dataws->dataX(0)[i] = double(i); dataws->dataY(0)[i] = double(i)*double(i)+sin(double(i)/180.*3.14); @@ -277,6 +271,10 @@ class ProcessBackgroundTest : public CxxTest::TestSuite alg.setProperty("SelectionMode", "UserFunction"); alg.setProperty("BackgroundTableWorkspace", functablews); + alg.setProperty("OutputBackgroundParameterWorkspace", "OutBackgroundParameters"); + alg.setProperty("OutputBackgroundType", "Chebyshev"); + alg.setProperty("OutputBackgroundOrder", 6); + alg.setProperty("NoiseTolerance", 0.25); TS_ASSERT_THROWS_NOTHING(alg.execute()); @@ -290,8 +288,13 @@ class ProcessBackgroundTest : public CxxTest::TestSuite if (bkgdws) { TS_ASSERT(bkgdws->readX(0).size() > 10); + TS_ASSERT_EQUALS(bkgdws->getNumberHistograms(), 3); } + TableWorkspace_sptr bkgdparws = boost::dynamic_pointer_cast( + API::AnalysisDataService::Instance().retrieve("OutBackgroundParameters")); + TS_ASSERT(bkgdparws); + AnalysisDataService::Instance().remove("DiffractionData2"); AnalysisDataService::Instance().remove("SelectedBackgroundPoints2"); AnalysisDataService::Instance().remove("BackgroundParameters"); diff --git a/Code/Mantid/Framework/DataHandling/CMakeLists.txt b/Code/Mantid/Framework/DataHandling/CMakeLists.txt index 24f39e04ac69..b8cd20a8dbaf 100644 --- a/Code/Mantid/Framework/DataHandling/CMakeLists.txt +++ b/Code/Mantid/Framework/DataHandling/CMakeLists.txt @@ -1,8 +1,6 @@ set ( SRC_FILES src/AppendGeometryToSNSNexus.cpp - src/ApplyGroupingFromMuonNexus.cpp src/CompressEvents.cpp - src/ConvertFullprofToXML.cpp src/CreateChopperModel.cpp src/CreateModeratorModel.cpp src/CreateSampleShape.cpp @@ -60,6 +58,7 @@ set ( SRC_FILES src/LoadNexusLogs.cpp src/LoadNexusMonitors.cpp src/LoadNexusProcessed.cpp + src/LoadPDCharacterizations.cpp src/LoadPDFgetNFile.cpp src/LoadParameterFile.cpp src/LoadPreNexus.cpp @@ -131,9 +130,7 @@ set ( SRC_FILES set ( INC_FILES inc/MantidDataHandling/AppendGeometryToSNSNexus.h - inc/MantidDataHandling/ApplyGroupingFromMuonNexus.h inc/MantidDataHandling/CompressEvents.h - inc/MantidDataHandling/ConvertFullprofToXML.h inc/MantidDataHandling/CreateChopperModel.h inc/MantidDataHandling/CreateModeratorModel.h inc/MantidDataHandling/CreateSampleShape.h @@ -191,6 +188,7 @@ set ( INC_FILES inc/MantidDataHandling/LoadNexusLogs.h inc/MantidDataHandling/LoadNexusMonitors.h inc/MantidDataHandling/LoadNexusProcessed.h + inc/MantidDataHandling/LoadPDCharacterizations.h inc/MantidDataHandling/LoadPDFgetNFile.h inc/MantidDataHandling/LoadParameterFile.h inc/MantidDataHandling/LoadPreNexus.h @@ -262,9 +260,7 @@ set ( INC_FILES set ( TEST_FILES AppendGeometryToSNSNexusTest.h - ApplyGroupingFromMuonNexusTest.h CompressEventsTest.h - ConvertFullprofToXMLTest.h CreateChopperModelTest.h CreateModeratorModelTest.h CreateSampleShapeTest.h @@ -279,8 +275,8 @@ set ( TEST_FILES GroupDetectorsTest.h ISISDataArchiveTest.h InstrumentRayTracerTest.h - LoadAsciiTest.h LoadAscii2Test.h + LoadAsciiTest.h LoadCalFileTest.h LoadCanSAS1dTest.h LoadDaveGrpTest.h @@ -304,8 +300,8 @@ set ( TEST_FILES LoadLogTest.h LoadMappingTableTest.h LoadMaskTest.h - LoadMcStasTest.h LoadMcStasNexusTest.h + LoadMcStasTest.h LoadMuonLogTest.h LoadMuonNexus1Test.h LoadMuonNexus2Test.h @@ -314,6 +310,7 @@ set ( TEST_FILES LoadNexusMonitorsTest.h LoadNexusProcessedTest.h LoadNexusTest.h + LoadPDCharacterizationsTest.h LoadPDFgetNFileTest.h LoadParameterFileTest.h LoadPreNexusMonitorsTest.h @@ -348,8 +345,8 @@ set ( TEST_FILES RotateInstrumentComponentTest.h SNSDataArchiveICAT2Test.h SNSDataArchiveTest.h - SaveAsciiTest.h SaveAscii2Test.h + SaveAsciiTest.h SaveCSVTest.h SaveCalFileTest.h SaveCanSAS1dTest.h diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/ConvertFullprofToXML.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/ConvertFullprofToXML.h deleted file mode 100644 index 34c4223c1751..000000000000 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/ConvertFullprofToXML.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef MANTID_DATAHANDLING_CONVERTFULLPROFTOXML_H_ -#define MANTID_DATAHANDLING_CONVERTFULLPROFTOXML_H_ - -#include "MantidAPI/Algorithm.h" -#include "MantidAPI/ITableWorkspace.h" - -#include - -namespace Mantid -{ -namespace DataHandling -{ - - /** ConvertFullprofToXML : Convert a fullprof resolution file to an instrument parameter file - - Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory - - This file is part of Mantid. - - Mantid is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - Mantid is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - File change history is stored at: - Code Documentation is available at: - */ - class DLLExport ConvertFullprofToXML : public API::Algorithm - { - public: - ConvertFullprofToXML(); - virtual ~ConvertFullprofToXML(); - - /// Algorithm's name for identification overriding a virtual method - virtual const std::string name() const { return "ConvertFullprofToXML";} - - /// Algorithm's version for identification overriding a virtual method - virtual int version() const { return 1;} - - /// Algorithm's category for identification overriding a virtual method - virtual const std::string category() const { return "Diffraction";} - - private: - /// Sets documentation strings for this algorithm - virtual void initDocs(); - /// Implement abstract Algorithm methods - void init(); - /// Implement abstract Algorithm methods - void exec(); - - /// Load file to a vector of strings - void loadFile(std::string filename, std::vector& lines); - - /// Add an ALFBE parameter - void addALFBEParameter(const API::Column_const_sptr, Poco::XML::Document* mDoc, Poco::XML::Element* parent, const std::string& paramName); - - /// Add set of Sigma parameters - void addSigmaParameters(const API::Column_const_sptr, Poco::XML::Document* mDoc, Poco::XML::Element* parent ); - - /// Add set of Gamma parameters - void addGammaParameters(const API::Column_const_sptr, Poco::XML::Document* mDoc, Poco::XML::Element* parent ); - - /// Get value for XML eq attribute for parameter - std::string getXMLEqValue( const API::Column_const_sptr, const std::string& name ); - - // Translate a parameter name from as it appears in the table workspace to its name in the XML file - std::string getXMLParameterName( const std::string& name ); - - /// Get row numbers of the parameters in the table workspace - void getTableRowNumbers(const API::ITableWorkspace_sptr & tablews, std::map& parammap); - - /// Place to store the row numbers - std::map m_rowNumbers; - - }; - - -} // namespace DataHandling -} // namespace Mantid - -#endif /* MANTID_DATAHANDLING_CONVERTFULLPROFTOXML_H_ */ diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadAscii.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadAscii.h index 9108cab0a0e6..068dbc125fc8 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadAscii.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadAscii.h @@ -58,8 +58,6 @@ namespace Mantid /// Returns a confidence value that this algorithm can load a file virtual int confidence(Kernel::FileDescriptor & descriptor) const; - static bool isAscii(FILE *file); - protected: /// Process the header information within the file. virtual void processHeader(std::ifstream & file) const; diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadFullprofResolution.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadFullprofResolution.h index bd5ee29c1986..2163d7c474e7 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadFullprofResolution.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadFullprofResolution.h @@ -85,6 +85,30 @@ namespace DataHandling /// Generate bank information workspace DataObjects::TableWorkspace_sptr genInfoTableWorkspace(std::vector banks); + /// Put parameters into a metrix workspace + void putParametersIntoWorkspace( const API::ITableWorkspace_sptr &tws, API::MatrixWorkspace_sptr ws); + + /// Add an ALFBE parameter + void addALFBEParameter(const API::Column_const_sptr, Poco::XML::Document* mDoc, Poco::XML::Element* parent, const std::string& paramName); + + /// Add set of Sigma parameters + void addSigmaParameters(const API::Column_const_sptr, Poco::XML::Document* mDoc, Poco::XML::Element* parent ); + + /// Add set of Gamma parameters + void addGammaParameters(const API::Column_const_sptr, Poco::XML::Document* mDoc, Poco::XML::Element* parent ); + + /// Get value for XML eq attribute for parameter + std::string getXMLEqValue( const API::Column_const_sptr, const std::string& name ); + + // Translate a parameter name from as it appears in the table workspace to its name in the XML file + std::string getXMLParameterName( const std::string& name ); + + /// Get row numbers of the parameters in the table workspace + void getTableRowNumbers(const API::ITableWorkspace_sptr & tablews, std::map& parammap); + + /// Place to store the row numbers + std::map m_rowNumbers; + }; diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadILL.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadILL.h index b46c2cc937fb..2a7ead842a01 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadILL.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadILL.h @@ -78,7 +78,7 @@ namespace DataHandling { int getDetectorElasticPeakPosition(const NeXus::NXInt &data); void loadTimeDetails(NeXus::NXEntry& entry); NeXus::NXData loadNexusFileData(NeXus::NXEntry& entry); - void loadDataIntoTheWorkSpace(NeXus::NXEntry& entry); + void loadDataIntoTheWorkSpace(NeXus::NXEntry& entry, int vanaCalculatedDetectorElasticPeakPosition = -1); void runLoadInstrument(); @@ -86,10 +86,13 @@ namespace DataHandling { static double calculateError(double in) { return sqrt(in); } + int validateVanadium(const std::string &); API::MatrixWorkspace_sptr m_localWorkspace; - std::string m_filename; ///< The file to load +// NeXus::NXRoot m_dataRoot; +// NeXus::NXRoot m_vanaRoot; + std::string m_instrumentName; ///< Name of the instrument std::string m_instrumentPath; ///< Name of the instrument path diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadMuonNexus.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadMuonNexus.h index eca3fbd35c8f..4d1b708b98ce 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadMuonNexus.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadMuonNexus.h @@ -73,7 +73,7 @@ namespace Mantid virtual const std::string category() const { return "DataHandling\\Nexus;Muon"; } /// Returns a confidence value that this algorithm can load a file - virtual int confidence(Kernel::NexusDescriptor &) const; + virtual int confidence(Kernel::NexusDescriptor &descriptor) const; protected: diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadMuonNexus1.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadMuonNexus1.h index 09a90e1c0155..368353fdaa57 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadMuonNexus1.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadMuonNexus1.h @@ -108,6 +108,14 @@ namespace Mantid /// Creates Dead Time Table using all the data between begin and end TableWorkspace_sptr createDeadTimeTable(std::vector::const_iterator begin, std::vector::const_iterator end); + + /// Loads detector grouping information + void loadDetectorGrouping(Mantid::NeXus::NXRoot& root); + + /// Creates Detector Grouping Table using all the data from the range + TableWorkspace_sptr createDetectorGroupingTable(std::vector::const_iterator begin, + std::vector::const_iterator end); + }; } // namespace DataHandling diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/ApplyGroupingFromMuonNexus.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadPDCharacterizations.h similarity index 66% rename from Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/ApplyGroupingFromMuonNexus.h rename to Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadPDCharacterizations.h index 4022af0d42df..5017b147e865 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/ApplyGroupingFromMuonNexus.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadPDCharacterizations.h @@ -1,20 +1,16 @@ -#ifndef MANTID_DATAHANDLING_APPLYGROUPINGFROMMUONNEXUS_H_ -#define MANTID_DATAHANDLING_APPLYGROUPINGFROMMUONNEXUS_H_ +#ifndef MANTID_DATAHANDLING_LOADPDCHARACTERIZATIONS_H_ +#define MANTID_DATAHANDLING_LOADPDCHARACTERIZATIONS_H_ #include "MantidKernel/System.h" #include "MantidAPI/Algorithm.h" -#include "MantidDataObjects/Workspace2D.h" +#include namespace Mantid { namespace DataHandling { - using namespace DataObjects; - /** - Applies grouping information from Muon Nexus file to the workspace. - @author Arturs Bekasovs - @date 10/10/2013 + /** LoadPDCharacterizations : Load a characterization file used in Powder Diffraction Reduction. Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory @@ -36,9 +32,12 @@ namespace DataHandling File change history is stored at: Code Documentation is available at: */ - class DLLExport ApplyGroupingFromMuonNexus : public API::Algorithm + class DLLExport LoadPDCharacterizations : public API::Algorithm { public: + LoadPDCharacterizations(); + virtual ~LoadPDCharacterizations(); + virtual const std::string name() const; virtual int version() const; virtual const std::string category() const; @@ -47,16 +46,12 @@ namespace DataHandling virtual void initDocs(); void init(); void exec(); - - bool checkGroups(); - bool processGroups(); - - /// Applies grouping to a given workspace - Workspace2D_sptr applyGrouping(const std::vector& detectorGrouping, Workspace2D_const_sptr inputWs); + void readFocusInfo(std::ifstream &file); + void readCharInfo(std::ifstream &file, API::ITableWorkspace_sptr &wksp); }; } // namespace DataHandling } // namespace Mantid -#endif /* MANTID_DATAHANDLING_APPLYGROUPINGFROMMUONNEXUS_H_ */ \ No newline at end of file +#endif /* MANTID_DATAHANDLING_LOADPDCHARACTERIZATIONS_H_ */ diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadParameterFile.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadParameterFile.h index 83147785439b..1a0bad10c7f7 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadParameterFile.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadParameterFile.h @@ -84,7 +84,7 @@ namespace Mantid /// Algorithm's category for identification overriding a virtual method virtual const std::string category() const { return "DataHandling\\Instrument";} - static void execManually(std::string filename, Mantid::API::ExperimentInfo_sptr localWorkspace); + static void execManually(bool useString, std::string filename, std::string parameterString, Mantid::API::ExperimentInfo_sptr localWorkspace); private: /// Sets documentation strings for this algorithm diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadSassena.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadSassena.h index 2afa1d3ed4ac..8781f56e4f8e 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadSassena.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadSassena.h @@ -74,7 +74,7 @@ namespace Mantid /// Add a workspace to the group and register in the analysis data service void registerWorkspace(API::WorkspaceGroup_sptr gws, const std::string wsName, DataObjects::Workspace2D_sptr ws, const std::string &description); /// Read info about one HDF5 dataset, log if error - void dataSetInfo( const hid_t& h5file, const std::string setName, hsize_t* dims); + herr_t dataSetInfo( const hid_t& h5file, const std::string setName, hsize_t* dims) const; /// Read dataset data to a buffer ot type double void dataSetDouble( const hid_t& h5file, const std::string setName, double *buf ); /// Load qvectors dataset, calculate modulus of vectors diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/SaveFocusedXYE.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/SaveFocusedXYE.h index 48e4c0983951..81094cd0eaf1 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/SaveFocusedXYE.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/SaveFocusedXYE.h @@ -56,7 +56,7 @@ namespace DataHandling class DLLExport SaveFocusedXYE : public API::Algorithm { public: - enum HeaderType {XYE, MAUD}; + enum HeaderType {XYE, MAUD, TOPAS}; /// (Empty) Constructor SaveFocusedXYE() : API::Algorithm(){} /// Virtual destructor @@ -99,6 +99,8 @@ class DLLExport SaveFocusedXYE : public API::Algorithm /// Header type HeaderType m_headerType; + /// Comment character + std::string m_comment; }; } diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/SetSampleMaterial.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/SetSampleMaterial.h index 7eccbc400848..94a5c8ec8c2b 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/SetSampleMaterial.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/SetSampleMaterial.h @@ -52,7 +52,6 @@ class DLLExport SetSampleMaterial : public Mantid::API::Algorithm virtual int version() const; /// Algorithm's category for identification virtual const std::string category() const; - /// @inheritdocs virtual std::map validateInputs(); private: diff --git a/Code/Mantid/Framework/DataHandling/src/ApplyGroupingFromMuonNexus.cpp b/Code/Mantid/Framework/DataHandling/src/ApplyGroupingFromMuonNexus.cpp deleted file mode 100644 index 56dd105da425..000000000000 --- a/Code/Mantid/Framework/DataHandling/src/ApplyGroupingFromMuonNexus.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/*WIKI* -Given a workspace or workspace group and Muon Nexus file, retrieves grouping information stored in the file and groups input workspace accordingly. -*WIKI*/ - -#include "MantidAPI/FileProperty.h" -#include "MantidDataHandling/ApplyGroupingFromMuonNexus.h" -#include "MantidDataObjects/Workspace2D.h" - -#include -#include - -namespace Mantid -{ -namespace DataHandling -{ - using namespace Kernel; - using namespace API; - using namespace DataObjects; - - // Register the algorithm into the AlgorithmFactory - DECLARE_ALGORITHM(ApplyGroupingFromMuonNexus) - - //---------------------------------------------------------------------------------------------- - /// Algorithm's name for identification. @see Algorithm::name - const std::string ApplyGroupingFromMuonNexus::name() const { return "ApplyGroupingFromMuonNexus";}; - - /// Algorithm's version for identification. @see Algorithm::version - int ApplyGroupingFromMuonNexus::version() const { return 1;}; - - /// Algorithm's category for identification. @see Algorithm::category - const std::string ApplyGroupingFromMuonNexus::category() const { return "DataHandling\\Nexus;Muon";} - - //---------------------------------------------------------------------------------------------- - /// Sets documentation strings for this algorithm - void ApplyGroupingFromMuonNexus::initDocs() - { - this->setWikiSummary("Applies grouping information from Muon Nexus file to the [[workspace]]."); - this->setOptionalMessage("Applies grouping information from Muon Nexus file to the workspace."); - } - - //---------------------------------------------------------------------------------------------- - /** Initialize the algorithm's properties. - */ - void ApplyGroupingFromMuonNexus::init() - { - declareProperty(new WorkspaceProperty("InputWorkspace","",Direction::Input), - "Workspace to group."); - - declareProperty(new FileProperty("Filename", "", FileProperty::Load, ".nxs"), - "Nexus file to load grouping information from." ); - - declareProperty(new WorkspaceProperty("OutputWorkspace","",Direction::Output), - "Workspace with grouping applied."); - } - - //---------------------------------------------------------------------------------------------- - /** Execute the algorithm. - */ - void ApplyGroupingFromMuonNexus::exec() - { - // Load grouping information from the Nexus file - std::string filename = getPropertyValue("Filename"); - - NeXus::File handle(filename, NXACC_READ); - - handle.openData("grouping"); // TODO: what if no? - size_t numDetectors = static_cast(handle.getInfo().dims[0]); - - boost::scoped_array detectorGrouping(new int[numDetectors]); - - handle.getData(detectorGrouping.get()); - handle.closeData(); - - Workspace_sptr inputWs = getProperty("InputWorkspace"); - - std::string outputWsName = getPropertyValue("OutputWorkspace"); - - if(Workspace2D_const_sptr inputWs2D = boost::dynamic_pointer_cast(inputWs)) - { - std::vector grouping(detectorGrouping.get(), detectorGrouping.get() + numDetectors); - - Workspace2D_sptr outputWs = applyGrouping(grouping, inputWs2D); - setProperty("OutputWorkspace", outputWs); - } - else if(WorkspaceGroup_const_sptr inputGroup = boost::dynamic_pointer_cast(inputWs)) - { - WorkspaceGroup_sptr outputWsGroup = WorkspaceGroup_sptr(new WorkspaceGroup); - - int currentOffset = 0; - - for(size_t i = 0; i < inputGroup->size(); i++) - { - Workspace2D_const_sptr memberWs2D = boost::dynamic_pointer_cast(inputGroup->getItem(i)); - - if(!memberWs2D) - throw std::invalid_argument("Specified group contains a workspace which is not a Workspace2D"); - - int* from = detectorGrouping.get() + currentOffset; - int* to = from + memberWs2D->getNumberHistograms(); - - std::vector grouping(from, to); - - Workspace2D_sptr outputWs = applyGrouping(grouping, memberWs2D); - outputWsGroup->addWorkspace(outputWs); - - std::string suffix = "_" + boost::lexical_cast(i + 1); - std::string outWsPropName = "OutputWorkspace" + suffix; - - declareProperty(new WorkspaceProperty(outWsPropName, outputWsName + suffix, Direction::Output)); - setProperty(outWsPropName, boost::dynamic_pointer_cast(outputWs)); - } - - setProperty("OutputWorkspace",boost::dynamic_pointer_cast(outputWsGroup)); - } - else - { - throw std::invalid_argument("Should be whether a Workspace2D or WorkspaceGroup"); - } - } - - bool ApplyGroupingFromMuonNexus::checkGroups() - { - return false; - } - - bool ApplyGroupingFromMuonNexus::processGroups() - { - return true; - } - - /** - * Applies grouping to a given workspace. - * - * @param detectorGrouping :: Grouping info, where index is detector id and value is group number - * @param inputWs :: Workspace to group - * @return Workspace with grouped detectors. All the unrelated parameters are left as in inputWs. - */ - Workspace2D_sptr ApplyGroupingFromMuonNexus::applyGrouping(const std::vector& detectorGrouping, - Workspace2D_const_sptr inputWs) - { - if(inputWs->getNumberHistograms() != detectorGrouping.size()) - throw std::invalid_argument("Invalid number of detectors."); - - std::map< int, std::vector > groups; - std::vector ungrouped; - - for (size_t i = 0; i < detectorGrouping.size(); ++i) - { - int group = detectorGrouping[i]; - - if(group == 0) - ungrouped.push_back(static_cast(i)); - else - groups[group].push_back(static_cast(i)); - } - - if(groups.empty()) - throw std::invalid_argument("No groups specified in the input file"); - - // Number of the last group we've met - int lastGroup = groups.rbegin()->first; - - // Add ungrouped detectors to separate groups - for(size_t i = 0; i < ungrouped.size(); i++) - groups[++lastGroup].push_back(ungrouped[i]); - - Workspace2D_sptr groupedWs = boost::dynamic_pointer_cast( - WorkspaceFactory::Instance().create(inputWs, groups.size(), inputWs->dataX(0).size(), inputWs->blocksize())); - - // Compile the groups - int groupIndex = 0; - for (auto groupIt = groups.begin(); groupIt != groups.end(); groupIt++) - { - std::vector& detectors = groupIt->second; - - for(auto detIt = detectors.begin(); detIt != detectors.end(); detIt++) - { - for(size_t j = 0; j < inputWs->blocksize(); j++) - { - // Sum the y values - groupedWs->dataY(groupIndex)[j] += inputWs->dataY(*detIt)[j]; - - // Sum the errors in quadrature - groupedWs->dataE(groupIndex)[j] = - sqrt(pow(groupedWs->dataE(groupIndex)[j], 2) + pow(inputWs->dataE(*detIt)[j], 2)); - } - - groupedWs->getSpectrum(groupIndex)->addDetectorID(static_cast(*detIt)); - } - - // Using the last detector X values for consistency with LoadMuonNexus1 AutoGroup behavior - groupedWs->dataX(groupIndex) = inputWs->dataX(detectors.back()); - - groupedWs->getSpectrum(groupIndex)->setSpectrumNo(groupIndex+1); - - g_log.information() << "Group " << groupIt->first << ": " - << Kernel::Strings::join(detectors.begin(), detectors.end(), ", ") - << std::endl; - - groupIndex++; - } - - // Set Y axis values - for(size_t i = 0; i < groupedWs->getNumberHistograms(); i++) - groupedWs->getAxis(1)->setValue(i, static_cast(i + 1)); - - return groupedWs; - } - -} // namespace DataHandling -} // namespace Mantid \ No newline at end of file diff --git a/Code/Mantid/Framework/DataHandling/src/ConvertFullprofToXML.cpp b/Code/Mantid/Framework/DataHandling/src/ConvertFullprofToXML.cpp deleted file mode 100644 index 4f882762d526..000000000000 --- a/Code/Mantid/Framework/DataHandling/src/ConvertFullprofToXML.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/*WIKI* - -Convert the initial fitting parameters in a Fullprof file to XML format in an [[InstrumentParameterFile]]. - -*WIKI*/ -#include "MantidDataHandling/ConvertFullprofToXML.h" -#include "MantidDataHandling/LoadFullprofResolution.h" -#include "MantidKernel/MandatoryValidator.h" -#include "MantidAPI/FileProperty.h" -#include "MantidAPI/TableRow.h" - -#include - -// Needed for writing the XML file (will be moved to a child algorithm) -#include -#include -#include -#include - -#include - - -namespace Mantid -{ -namespace DataHandling -{ - using namespace API; - using namespace Kernel; - using namespace Poco::XML; - - DECLARE_ALGORITHM(ConvertFullprofToXML) - - //---------------------------------------------------------------------------------------------- - /** Constructor - */ - ConvertFullprofToXML::ConvertFullprofToXML() - { - } - - //---------------------------------------------------------------------------------------------- - /** Destructor - */ - ConvertFullprofToXML::~ConvertFullprofToXML() - { - } - - //---------------------------------------------------------------------------------------------- - /** Sets documentation strings for this algorithm - */ - void ConvertFullprofToXML::initDocs() - { - setWikiSummary("Convert the initial fitting parameters in a Fullprof file to XML format in an Instrument Parameter File"); - setOptionalMessage("Convert the initial fitting parameters in a Fullprof file to XML format in an Instrument Parameter File"); - - return; - } - - //---------------------------------------------------------------------------------------------- - /** Implement abstract Algorithm methods - */ - void ConvertFullprofToXML::init() - { - // Input file name - std::vector exts; - exts.push_back(".irf"); - exts.push_back(".prf"); - declareProperty(new FileProperty("InputFilename", "", FileProperty::Load, exts), - "Path to an Fullprof file to load."); - - // Instrument name - declareProperty("InstrumentName", "",boost::make_shared>(), "Name of instrument for the input file" ); - - // Output file - std::vector extso; - extso.push_back(".xml"); - declareProperty(new FileProperty("OutputFilename", "", FileProperty::Save, extso), - "The name to give to the parameter file."); - - return; - } - - //---------------------------------------------------------------------------------------------- - /** Implement abstract Algorithm methods - */ - void ConvertFullprofToXML::exec() - { - // Get input - std::string datafile = getProperty("InputFilename"); - - // Get instrument - std::string instrumentName = getProperty("InstrumentName"); - - // Get Output - std::string paramfile = getProperty("OutputFilename"); - - - // We need the instrument name because it is not extracted by LoadFullprofResolution and is - // needed by fitting despite also being available in the IDF. - if ( instrumentName.empty() ) - throw std::runtime_error("The InstrumentName property must be set."); - - // Load with LoadFullprofResolution - auto loader = createChildAlgorithm("LoadFullprofResolution"); - loader->setProperty("Filename",datafile); - loader->executeAsChildAlg(); - - // Write into Parameter File - // This code will later go into a child algorithm to enable it to be used by other algorithms - - // Set up access to table workspace ParamTable - API::ITableWorkspace_sptr paramTable = loader->getProperty("OutputTableWorkspace"); - // get the table workspace row numbers of the parameters and store them for later use - getTableRowNumbers( paramTable, m_rowNumbers); - - // Set up access to Output file - std::ofstream outFile(paramfile.c_str()); - if (!outFile) - { - throw Mantid::Kernel::Exception::FileError("Unable to open file:", paramfile); - } - - // Set up writer to Paremeter file - DOMWriter writer; - writer.setNewLine("\n"); - writer.setOptions(XMLWriter::PRETTY_PRINT); - - // Get current time - Kernel::DateAndTime date = Kernel::DateAndTime::getCurrentTime(); - std::string ISOdate = date.toISO8601String(); - std::string ISOdateShort = ISOdate.substr(0,19); // Remove fraction of seconds - - // Create document - AutoPtr mDoc = new Document(); - AutoPtr rootElem = mDoc->createElement("parameter-file"); - rootElem->setAttribute("date", ISOdateShort); - mDoc->appendChild(rootElem); - - // Add instrument - AutoPtr instrumentElem = mDoc->createElement("component-link"); - instrumentElem->setAttribute("name",instrumentName); - rootElem->appendChild(instrumentElem); - API::Column_const_sptr column1 = paramTable->getColumn( 1 ); - addALFBEParameter( column1, mDoc, instrumentElem, "Alph0"); - addALFBEParameter( column1, mDoc, instrumentElem, "Beta0"); - addALFBEParameter( column1, mDoc, instrumentElem, "Alph1"); - addALFBEParameter( column1, mDoc, instrumentElem, "Beta1"); - - // Add banks - if(paramTable->columnCount() < 2){ - throw std::runtime_error("No banks found"); - } - size_t num_banks = paramTable->columnCount()-1; - - for( size_t i=0; igetColumn( i+1 ); - const double bankNumber = column->cell(0); - std::ostringstream bankName; - bankName << "bank" << bankNumber; - AutoPtr bankElem = mDoc->createElement("component-link"); - bankElem->setAttribute("name",bankName.str()); - addSigmaParameters( column, mDoc, bankElem ); - addGammaParameters( column, mDoc, bankElem ); - rootElem->appendChild(bankElem); - } - - - // Write document structure into file - writer.writeNode(outFile, mDoc); - - return; - } - - /* Add an ALFBE parameter to the XML document according to the table workspace - * - * paramName is the name of the parameter as it appears in the table workspace - */ - void ConvertFullprofToXML::addALFBEParameter(const API::Column_const_sptr column, Poco::XML::Document* mDoc, Element* parent, const std::string& paramName) - { - AutoPtr parameterElem = mDoc->createElement("parameter"); - parameterElem->setAttribute("name", getXMLParameterName(paramName)); - parameterElem->setAttribute("type","fitting"); - - AutoPtr formulaElem = mDoc->createElement("formula"); - formulaElem->setAttribute("eq",getXMLEqValue(column, paramName)); - if(paramName != "Beta1") formulaElem->setAttribute("result-unit","TOF"); - parameterElem->appendChild(formulaElem); - - AutoPtr fixedElem = mDoc->createElement("fixed"); - parameterElem->appendChild(fixedElem); - - parent->appendChild(parameterElem); - } - - /* Add a set of SIGMA paraters to the XML document according to the table workspace - * for the bank at the given column of the table workspace - */ - void ConvertFullprofToXML::addSigmaParameters(const API::Column_const_sptr column, Poco::XML::Document* mDoc, Poco::XML::Element* parent ) - { - AutoPtr parameterElem = mDoc->createElement("parameter"); - parameterElem->setAttribute("name", "IkedaCarpenterPV:SigmaSquared"); - parameterElem->setAttribute("type","fitting"); - - AutoPtr formulaElem = mDoc->createElement("formula"); - std::string eqValue = getXMLEqValue(column, "Sig1")+"*centre^2+"+getXMLEqValue(column, "Sig0"); - formulaElem->setAttribute("eq", eqValue); - formulaElem->setAttribute("unit","dSpacing"); - formulaElem->setAttribute("result-unit","TOF^2"); - parameterElem->appendChild(formulaElem); - - parent->appendChild(parameterElem); - } - - /* Add a set of GAMMA paraters to the XML document according to the table workspace - * for the bank at the given column of the table workspace - */ - void ConvertFullprofToXML::addGammaParameters(const API::Column_const_sptr column, Poco::XML::Document* mDoc, Poco::XML::Element* parent ) - { - AutoPtr parameterElem = mDoc->createElement("parameter"); - parameterElem->setAttribute("name", "IkedaCarpenterPV:Gamma"); - parameterElem->setAttribute("type","fitting"); - - AutoPtr formulaElem = mDoc->createElement("formula"); - std::string eqValue = getXMLEqValue(column, "Gam1" )+"*centre"; - formulaElem->setAttribute("eq", eqValue); - formulaElem->setAttribute("unit","dSpacing"); - formulaElem->setAttribute("result-unit","TOF"); - parameterElem->appendChild(formulaElem); - - parent->appendChild(parameterElem); - } - - - - /* - * Get the XML name of a parameter given its Table Workspace name - */ - std::string ConvertFullprofToXML::getXMLParameterName( const std::string& name ) - { - // Only used for ALFBE parameters - std::string prefix = "IkedaCarpenterPV:"; - if(name == "Alph0") return prefix+"Alpha0"; - if(name == "Beta0") return prefix+"Beta0"; - if(name == "Alph1") return prefix+"Alpha1"; - if(name == "Beta1") return prefix+"Kappa"; - return "?"+name; - } - - /* - * Get the value string to put in the XML eq attribute of the formula element of the paramenter element - * given the name of the parameter in the table workspace. - */ - std::string ConvertFullprofToXML::getXMLEqValue( const API::Column_const_sptr column, const std::string& name ) - { - size_t paramNumber = m_rowNumbers[name]; - //API::Column_const_sptr column = tablews->getColumn( columnIndex ); - double eqValue = column->cell(paramNumber); - if(name.substr(0,3) == "Sig") eqValue = eqValue*eqValue; // Square the sigma values - return boost::lexical_cast(eqValue); - } - - /* This function fills in a list of the row numbers starting 0 of the parameters - in the table workspace, so one can find the position in a column of - the value of the given parameter. - */ - void ConvertFullprofToXML::getTableRowNumbers(const API::ITableWorkspace_sptr & tablews, std::map& parammap) - { - parammap.clear(); - - size_t numrows = tablews->rowCount(); - for (size_t i = 0; i < numrows; ++i) - { - TableRow row = tablews->getRow(i); - std::string name; - row >> name; - parammap.insert(std::make_pair(name, i)); - } - - return; - } - -} // namespace DataHandling -} // namespace Mantid - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Code/Mantid/Framework/DataHandling/src/LoadAscii.cpp b/Code/Mantid/Framework/DataHandling/src/LoadAscii.cpp index aad02577bf20..c0697f85e383 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadAscii.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadAscii.cpp @@ -27,6 +27,7 @@ This algorithm cannot load a file created by [[SaveAscii]] if it has X errors wr #include "MantidAPI/RegisterFileLoader.h" #include "MantidKernel/BoundedValidator.h" #include "MantidKernel/ListValidator.h" +#include "MantidKernel/Strings.h" #include #include @@ -81,32 +82,6 @@ namespace Mantid return confidence; } - /** - * Check if a file is a text file - * @param file :: The file pointer - * @returns true if the file an ascii text file, false otherwise - */ - bool LoadAscii::isAscii(FILE *file) - { - char data[256]; - char *pend = &data[fread(data, 1, sizeof(data), file)]; - fseek(file,0,SEEK_SET); - /* - * Call it a binary file if we find a non-ascii character in the - * first 256 bytes of the file. - */ - for( char *p = data; p < pend; ++p ) - { - unsigned long ch = (unsigned long)*p; - if( !(ch <= 0x7F) ) - { - return false; - } - - } - return true; - } - //-------------------------------------------------------------------------- // Protected methods //-------------------------------------------------------------------------- @@ -311,9 +286,7 @@ namespace Mantid */ void LoadAscii::peekLine(std::ifstream & is, std::string & str) const { - getline(is, str); - is.seekg(-(int)str.length(),std::ios::cur); - boost::trim(str); + str = Kernel::Strings::peekLine(is); } /** @@ -323,8 +296,7 @@ namespace Mantid */ bool LoadAscii::skipLine(const std::string & line) const { - // Empty or comment - return ( line.empty() || boost::starts_with(line, "#") ); + return Kernel::Strings::skipLine(line); } /** diff --git a/Code/Mantid/Framework/DataHandling/src/LoadAscii2.cpp b/Code/Mantid/Framework/DataHandling/src/LoadAscii2.cpp index 23d37d218c19..310a15f23bfe 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadAscii2.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadAscii2.cpp @@ -149,7 +149,6 @@ namespace Mantid * Check the start of the file for the first data set, then set the number of columns that hsould be expected thereafter * @param[in] line : The current line of data * @param[in] columns : the columns of values in the current line of data - * @param[in] lineNo : the current line number */ void LoadAscii2::parseLine(const std::string & line, std::list & columns) { @@ -500,7 +499,6 @@ namespace Mantid /** * Check if the file has been found to incosistantly include spectra IDs - * @param[in] spectraSize : the number of spectra recorded so far */ void LoadAscii2::inconsistantIDCheck() const { @@ -554,6 +552,7 @@ namespace Mantid /** * Return true if the line is to be skipped. * @param[in] line :: The line to be checked + * @param[in] header :: Flag for if this is header material * @return True if the line should be skipped */ bool LoadAscii2::skipLine(const std::string & line, bool header) const diff --git a/Code/Mantid/Framework/DataHandling/src/LoadEventNexus.cpp b/Code/Mantid/Framework/DataHandling/src/LoadEventNexus.cpp index e9ec91e827c9..863c2f9452a5 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadEventNexus.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadEventNexus.cpp @@ -139,10 +139,12 @@ class ProcessBankData : public Task * @param event_time_of_flight :: array with event TOFS * @param numEvents :: how many events in the arrays * @param startAt :: index of the first event from event_index - * @param event_index_ptr :: ptr to a vector of event index (length of # of pulses) + * @param event_index :: vector of event index (length of # of pulses) * @param thisBankPulseTimes :: ptr to the pulse times for this particular bank. * @param have_weight :: flag for handling simulated files * @param event_weight :: array with weights for events + * @param min_event_id ;: minimum detector ID to load + * @param max_event_id :: maximum detector ID to load * @return */ ProcessBankData(LoadEventNexus * alg, std::string entry_name, @@ -452,7 +454,7 @@ class LoadBankFromDiskTask : public Task */ LoadBankFromDiskTask(LoadEventNexus * alg, const std::string& entry_name, const std::string & entry_type, const std::size_t numEvents, const bool oldNeXusFileNames, - Progress * prog, Mutex * ioMutex, ThreadScheduler * scheduler) + Progress * prog, boost::shared_ptr ioMutex, ThreadScheduler * scheduler) : Task(), alg(alg), entry_name(entry_name), entry_type(entry_type), pixelID_to_wi_vector(alg->pixelID_to_wi_vector), pixelID_to_wi_offset(alg->pixelID_to_wi_offset), @@ -1425,11 +1427,6 @@ void LoadEventNexus::loadEvents(API::Progress * const prog, const bool monitors) // get the number of events std::size_t num = numEvents(file, hasTotalCounts, oldNeXusFileNames); - if (num == 0) - { - file.closeGroup(); - continue; - } bankNames.push_back( entry_name ); bankNumEvents.push_back(num); total_events += num; @@ -1606,7 +1603,7 @@ void LoadEventNexus::loadEvents(API::Progress * const prog, const bool monitors) // Make the thread pool ThreadScheduler * scheduler = new ThreadSchedulerMutexes(); ThreadPool pool(scheduler); - Mutex * diskIOMutex = new Mutex(); + auto diskIOMutex = boost::make_shared(); size_t bank0 = 0; size_t bankn = bankNames.size(); @@ -1680,7 +1677,7 @@ void LoadEventNexus::loadEvents(API::Progress * const prog, const bool monitors) } // Start and end all threads pool.joinAll(); - delete diskIOMutex; + diskIOMutex.reset(); delete prog2; @@ -2616,7 +2613,7 @@ void LoadEventNexus::loadTimeOfFlightData(::NeXus::File& file, DataObjects::Even * * @note: It does essentially the same thing of the method: LoadISISNexus2::loadSampleData * - * @param nexusfilename : path for the nexus file + * @param file : handle to the nexus file * @param WS : pointer to the workspace */ void LoadEventNexus::loadSampleDataISIScompatibility(::NeXus::File& file, Mantid::API::MatrixWorkspace_sptr WS){ diff --git a/Code/Mantid/Framework/DataHandling/src/LoadEventPreNexus2.cpp b/Code/Mantid/Framework/DataHandling/src/LoadEventPreNexus2.cpp index f166816aff8a..74d8baabef12 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadEventPreNexus2.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadEventPreNexus2.cpp @@ -99,14 +99,16 @@ static const string EVENT_EXTS[] = {"_neutron_event.dat", "_neutron1_event.dat", "_neutron2_event.dat", "_neutron3_event.dat", + "_neutron4_event.dat", "_live_neutron_event.dat"}; static const string PULSE_EXTS[] = {"_pulseid.dat", "_pulseid0.dat", "_pulseid1.dat", "_pulseid2.dat", "_pulseid3.dat", + "_pulseid4.dat", "_live_pulseid.dat"}; -static const int NUM_EXT = 6; +static const int NUM_EXT = 7; //----------------------------------------------------------------------------- //Statistic Functions diff --git a/Code/Mantid/Framework/DataHandling/src/LoadFullprofResolution.cpp b/Code/Mantid/Framework/DataHandling/src/LoadFullprofResolution.cpp index 7cd9dfa0128e..e5933a32d18d 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadFullprofResolution.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadFullprofResolution.cpp @@ -1,6 +1,6 @@ /*WIKI* -Load Fullprof resolution (.irf) file to TableWorkspace(s) +Load Fullprof resolution (.irf) file to TableWorkspace(s) and optionally into the instrument of a matrix workspace. *WIKI*/ #include "MantidDataHandling/LoadFullprofResolution.h" @@ -8,6 +8,14 @@ Load Fullprof resolution (.irf) file to TableWorkspace(s) #include "MantidKernel/ArrayProperty.h" #include "MantidAPI/WorkspaceProperty.h" #include "MantidAPI/TableRow.h" +#include "MantidGeometry/Instrument.h" +#include "MantidAPI/InstrumentDataService.h" +#include "MantidGeometry/Instrument/InstrumentDefinitionParser.h" +#include "MantidDataHandling/LoadParameterFile.h" + +#include +#include +#include #include #include @@ -22,6 +30,12 @@ using namespace Mantid::API; using namespace Mantid::DataObjects; using namespace Mantid::Kernel; using namespace std; +using namespace Poco::XML; + +using Geometry::Instrument; +using Geometry::Instrument_sptr; +using Geometry::Instrument_const_sptr; +using Mantid::Geometry::InstrumentDefinitionParser; namespace Mantid { @@ -67,7 +81,7 @@ namespace DataHandling "Path to an Fullprof .irf file to load."); // Output table workspace - auto wsprop = new WorkspaceProperty("OutputTableWorkspace", "", Direction::Output); + auto wsprop = new WorkspaceProperty("OutputTableWorkspace", "", Direction::Output, PropertyMode::Optional); declareProperty(wsprop, "Name of the output TableWorkspace containing profile parameters or bank information. "); // Bank to import @@ -76,6 +90,9 @@ namespace DataHandling // declareProperty("Bank", EMPTY_INT(), "ID of a specific bank to load. Default is all banks in .irf file."); + declareProperty(new WorkspaceProperty<>("Workspace","",Direction::InOut, PropertyMode::Optional), + "Optional: A matrix workspace with the instrument to which we add the parameters from the Fullprof .irf file."); + return; } @@ -87,6 +104,7 @@ namespace DataHandling // Get input string datafile = getProperty("Filename"); vector outputbankids = getProperty("Banks"); + MatrixWorkspace_sptr workspace = getProperty("Workspace"); // Import data vector lines; @@ -169,8 +187,23 @@ namespace DataHandling // Generate output table workspace API::ITableWorkspace_sptr outTabWs = genTableWorkspace(bankparammap); - // 6. Output - setProperty("OutputTableWorkspace", outTabWs); + if( getPropertyValue("OutputTableWorkspace") != "") + { + // Output the output table workspace + setProperty("OutputTableWorkspace", outTabWs); + } + + + // If workspace, put parameters there + if(workspace) + { + putParametersIntoWorkspace( outTabWs, workspace ); + } + else if( getPropertyValue("OutputTableWorkspace") == "") + { + // We don't know where to output + throw std::runtime_error("Either the OutputTableWorkspace or Workspace property must be set."); + } return; } @@ -304,6 +337,7 @@ namespace DataHandling * @param bankid :: [input] ID of the bank to get parsed * @param startlineindex :: [input] index of the first line of the bank in vector of lines * @param endlineindex :: [input] index of the last line of the bank in vector of lines + * @param profNumber :: [input] index of the profile number */ void LoadFullprofResolution::parseResolutionStrings(map& parammap, const vector& lines, int bankid, int startlineindex, int endlineindex, int profNumber) @@ -733,6 +767,183 @@ namespace DataHandling return tablews; } + void LoadFullprofResolution::putParametersIntoWorkspace( const API::ITableWorkspace_sptr &tws, API::MatrixWorkspace_sptr ws) + { + + // Get instrument name from matrix workspace + std::string instrumentName = ws->getInstrument()->getName(); + + // Convert table workspace into DOM XML document + // Set up writer to Paremeter file + DOMWriter writer; + writer.setNewLine("\n"); + writer.setOptions(XMLWriter::PRETTY_PRINT); + + // Get current time + Kernel::DateAndTime date = Kernel::DateAndTime::getCurrentTime(); + std::string ISOdate = date.toISO8601String(); + std::string ISOdateShort = ISOdate.substr(0,19); // Remove fraction of seconds + + // Create document + AutoPtr mDoc = new Document(); + AutoPtr rootElem = mDoc->createElement("parameter-file"); + rootElem->setAttribute("date", ISOdateShort); + mDoc->appendChild(rootElem); + + // Add instrument + getTableRowNumbers( tws, m_rowNumbers); + AutoPtr instrumentElem = mDoc->createElement("component-link"); + instrumentElem->setAttribute("name",instrumentName); + rootElem->appendChild(instrumentElem); + API::Column_const_sptr column1 = tws->getColumn( 1 ); + addALFBEParameter( column1, mDoc, instrumentElem, "Alph0"); + addALFBEParameter( column1, mDoc, instrumentElem, "Beta0"); + addALFBEParameter( column1, mDoc, instrumentElem, "Alph1"); + addALFBEParameter( column1, mDoc, instrumentElem, "Beta1"); + + // Add banks + if(tws->columnCount() < 2){ + throw std::runtime_error("No banks found"); + } + size_t num_banks = tws->columnCount()-1; + + for( size_t i=0; igetColumn( i+1 ); + const double bankNumber = column->cell(0); + std::ostringstream bankName; + bankName << "bank" << bankNumber; + AutoPtr bankElem = mDoc->createElement("component-link"); + bankElem->setAttribute("name",bankName.str()); + addSigmaParameters( column, mDoc, bankElem ); + addGammaParameters( column, mDoc, bankElem ); + rootElem->appendChild(bankElem); + } + + // Convert DOM XML document into string + std::ostringstream outFile; + writer.writeNode(outFile, mDoc); + std::string parameterXMLString = outFile.str(); + + //std::ofstream outfileDebug("C:/Temp/test2_fullprof.xml"); + //outfileDebug << parameterXMLString; + //outfileDebug.close(); + + + // Load the string into the workspace + LoadParameterFile::execManually(true, "", parameterXMLString, ws); + + } + + /* Add an ALFBE parameter to the XML document according to the table workspace + * + * paramName is the name of the parameter as it appears in the table workspace + */ + void LoadFullprofResolution::addALFBEParameter(const API::Column_const_sptr column, Poco::XML::Document* mDoc, Element* parent, const std::string& paramName) + { + AutoPtr parameterElem = mDoc->createElement("parameter"); + parameterElem->setAttribute("name", getXMLParameterName(paramName)); + parameterElem->setAttribute("type","fitting"); + + AutoPtr formulaElem = mDoc->createElement("formula"); + formulaElem->setAttribute("eq",getXMLEqValue(column, paramName)); + if(paramName != "Beta1") formulaElem->setAttribute("result-unit","TOF"); + parameterElem->appendChild(formulaElem); + + AutoPtr fixedElem = mDoc->createElement("fixed"); + parameterElem->appendChild(fixedElem); + + parent->appendChild(parameterElem); + } + + /* Add a set of SIGMA paraters to the XML document according to the table workspace + * for the bank at the given column of the table workspace + */ + void LoadFullprofResolution::addSigmaParameters(const API::Column_const_sptr column, Poco::XML::Document* mDoc, Poco::XML::Element* parent ) + { + AutoPtr parameterElem = mDoc->createElement("parameter"); + parameterElem->setAttribute("name", "IkedaCarpenterPV:SigmaSquared"); + parameterElem->setAttribute("type","fitting"); + + AutoPtr formulaElem = mDoc->createElement("formula"); + std::string eqValue = getXMLEqValue(column, "Sig1")+"*centre^2+"+getXMLEqValue(column, "Sig0"); + formulaElem->setAttribute("eq", eqValue); + formulaElem->setAttribute("unit","dSpacing"); + formulaElem->setAttribute("result-unit","TOF^2"); + parameterElem->appendChild(formulaElem); + + parent->appendChild(parameterElem); + } + + /* Add a set of GAMMA paraters to the XML document according to the table workspace + * for the bank at the given column of the table workspace + */ + void LoadFullprofResolution::addGammaParameters(const API::Column_const_sptr column, Poco::XML::Document* mDoc, Poco::XML::Element* parent ) + { + AutoPtr parameterElem = mDoc->createElement("parameter"); + parameterElem->setAttribute("name", "IkedaCarpenterPV:Gamma"); + parameterElem->setAttribute("type","fitting"); + + AutoPtr formulaElem = mDoc->createElement("formula"); + std::string eqValue = getXMLEqValue(column, "Gam1" )+"*centre"; + formulaElem->setAttribute("eq", eqValue); + formulaElem->setAttribute("unit","dSpacing"); + formulaElem->setAttribute("result-unit","TOF"); + parameterElem->appendChild(formulaElem); + + parent->appendChild(parameterElem); + } + + + + /* + * Get the XML name of a parameter given its Table Workspace name + */ + std::string LoadFullprofResolution::getXMLParameterName( const std::string& name ) + { + // Only used for ALFBE parameters + std::string prefix = "IkedaCarpenterPV:"; + if(name == "Alph0") return prefix+"Alpha0"; + if(name == "Beta0") return prefix+"Beta0"; + if(name == "Alph1") return prefix+"Alpha1"; + if(name == "Beta1") return prefix+"Kappa"; + return "?"+name; + } + + /* + * Get the value string to put in the XML eq attribute of the formula element of the paramenter element + * given the name of the parameter in the table workspace. + */ + std::string LoadFullprofResolution::getXMLEqValue( const API::Column_const_sptr column, const std::string& name ) + { + size_t paramNumber = m_rowNumbers[name]; + //API::Column_const_sptr column = tablews->getColumn( columnIndex ); + double eqValue = column->cell(paramNumber); + if(name.substr(0,3) == "Sig") eqValue = eqValue*eqValue; // Square the sigma values + return boost::lexical_cast(eqValue); + } + + /* This function fills in a list of the row numbers starting 0 of the parameters + in the table workspace, so one can find the position in a column of + the value of the given parameter. + */ + void LoadFullprofResolution::getTableRowNumbers(const API::ITableWorkspace_sptr & tablews, std::map& parammap) + { + parammap.clear(); + + size_t numrows = tablews->rowCount(); + for (size_t i = 0; i < numrows; ++i) + { + TableRow row = tablews->getRow(i); + std::string name; + row >> name; + parammap.insert(std::make_pair(name, i)); + } + + return; + } + + } // namespace DataHandling } // namespace Mantid diff --git a/Code/Mantid/Framework/DataHandling/src/LoadHelper.cpp b/Code/Mantid/Framework/DataHandling/src/LoadHelper.cpp index 8c156bc6c56e..6a3e692bcd83 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadHelper.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadHelper.cpp @@ -20,7 +20,7 @@ LoadHelper::~LoadHelper() { /** * Finds the path for the instrument name in the nexus file - * Usually of the form: entry0//name + * Usually of the form: entry0/\/name */ std::string LoadHelper::findInstrumentNexusPath( const NeXus::NXEntry &firstEntry) { @@ -81,6 +81,7 @@ double LoadHelper::calculateEnergy(double wavelength) { /** * Calculate TOF from distance * @param distance :: distance in meters + * @param wavelength :: wavelength to calculate TOF from * @return tof in seconds */ double LoadHelper::calculateTOF(double distance,double wavelength) { diff --git a/Code/Mantid/Framework/DataHandling/src/LoadIDFFromNexus.cpp b/Code/Mantid/Framework/DataHandling/src/LoadIDFFromNexus.cpp index 9e81ce5b3132..63188bc98028 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadIDFFromNexus.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadIDFFromNexus.cpp @@ -90,7 +90,7 @@ void LoadIDFFromNexus::runLoadParameterFile(const MatrixWorkspace_sptr & workspa const std::string paramFile = directory + instrumentName + "_Parameters.xml"; try { - LoadParameterFile::execManually(paramFile, workspace); + LoadParameterFile::execManually(false, paramFile,"", workspace); } catch ( std::runtime_error& ) { g_log.notice() << "File " << paramFile << " not found or un-parsable. " "However, the instrument has been loaded successfully.\n"; diff --git a/Code/Mantid/Framework/DataHandling/src/LoadILL.cpp b/Code/Mantid/Framework/DataHandling/src/LoadILL.cpp index c1a7edf00e91..1e7ac2758a10 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadILL.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadILL.cpp @@ -1,9 +1,13 @@ /*WIKI* -Loads an ILL nexus file into a [[Workspace2D]] with the given name. +Loads an ILL TOF NeXus file into a [[Workspace2D]] with the given name. -To date this algorithm only supports: IN5 +This loader calculates the elastic peak position (EPP) on the fly. +In cases where the dispersion peak might be higher than the EPP, it is good practice to load a Vanadium file. +The property FilenameVanadium is optional. If it is present the EPP will be loaded from the Vanadium data. + +To date this algorithm only supports: IN4, IN5 and IN6 *WIKI*/ //--------------------------------------------------- @@ -79,14 +83,19 @@ namespace Mantid //--------------------------------------------------- LoadILL::LoadILL() : + API::IFileLoader() { - m_instrumentName = ""; m_wavelength = 0; m_channelWidth = 0; m_numberOfChannels = 0; m_numberOfHistograms = 0; + m_numberOfTubes = 0; + m_numberOfPixelsPerTube = 0; + m_monitorElasticPeakPosition = 0; + m_l1 = 0; + m_l2 = 0; m_supportedInstruments.push_back("IN4"); m_supportedInstruments.push_back("IN5"); m_supportedInstruments.push_back("IN6"); @@ -100,36 +109,48 @@ namespace Mantid { declareProperty( new FileProperty("Filename", "", FileProperty::Load, ".nxs"), - "Name of the SPE file to load"); + "File path of the Data file to load"); + declareProperty( + new FileProperty("FilenameVanadium", "", FileProperty::OptionalLoad, ".nxs"), + "File path of the Vanadium file to load (Optional)"); + declareProperty( new WorkspaceProperty<>("OutputWorkspace", "", Direction::Output), "The name to use for the output workspace"); } + /** * Execute the algorithm */ void LoadILL::exec() { // Retrieve filename - m_filename = getPropertyValue("Filename"); + std::string filenameData = getPropertyValue("Filename"); + std::string filenameVanadium = getPropertyValue("FilenameVanadium"); + // open the root node - NXRoot root(m_filename); - // find the first entry - NXEntry entry = root.openFirstEntry(); + NeXus::NXRoot dataRoot(filenameData); + NXEntry dataFirstEntry = dataRoot.openFirstEntry(); - loadInstrumentDetails(entry); - loadTimeDetails(entry); - initWorkSpace(entry); + loadInstrumentDetails(dataFirstEntry); + loadTimeDetails(dataFirstEntry); + initWorkSpace(dataFirstEntry); runLoadInstrument(); // just to get IDF contents initInstrumentSpecific(); - loadDataIntoTheWorkSpace(entry); + int calculatedDetectorElasticPeakPosition = -1; + if (filenameVanadium != "") { + g_log.information() << "Calculating the elastic peak position from the Vanadium." << std::endl; + calculatedDetectorElasticPeakPosition = validateVanadium(filenameVanadium); + } + + loadDataIntoTheWorkSpace(dataFirstEntry,calculatedDetectorElasticPeakPosition); - loadRunDetails(entry); - loadExperimentDetails(entry); + loadRunDetails(dataFirstEntry); + loadExperimentDetails(dataFirstEntry); // load the instrument from the IDF if it exists runLoadInstrument(); @@ -141,20 +162,27 @@ namespace Mantid /** * */ - void LoadILL::loadInstrumentDetails(NeXus::NXEntry& firstEntry) - { + void LoadILL::loadInstrumentDetails(NeXus::NXEntry& firstEntry) { - m_instrumentPath = m_loader.findInstrumentNexusPath(firstEntry); + m_instrumentPath = m_loader.findInstrumentNexusPath(firstEntry); - if (m_instrumentPath == "") - { - throw std::runtime_error("Cannot set the instrument name from the Nexus file!"); - } - m_instrumentName = m_loader.getStringFromNexusPath(firstEntry, - m_instrumentPath + "/name"); - g_log.debug() << "Instrument name set to: " + m_instrumentName << std::endl; + if (m_instrumentPath == "") { + throw std::runtime_error( + "Cannot set the instrument name from the Nexus file!"); + } - } + m_instrumentName = m_loader.getStringFromNexusPath(firstEntry, + m_instrumentPath + "/name"); + + if (std::find(m_supportedInstruments.begin(), m_supportedInstruments.end(), + m_instrumentName) == m_supportedInstruments.end()) { + std::string message = "The instrument " + m_instrumentName + " is not valid for this loader!"; + throw std::runtime_error(message); + } + + g_log.debug() << "Instrument name set to: " + m_instrumentName << std::endl; + + } /** * Creates the workspace and initialises member variables with @@ -407,12 +435,50 @@ namespace Mantid return calculatedDetectorElasticPeakPosition; } + + /** + * It loads the vanadium nexus file and cross checks it against the + * data file already loaded (same wavelength and same instrument configuration). + * If matches looks for the elastic peak in the vanadium file and returns + * it position. + * + * @param filenameVanadium :: The path for the vanadium nexus file. + * @return The elastic peak position inside the tof channels. + */ + int LoadILL::validateVanadium(const std::string &filenameVanadium) { + NeXus::NXRoot vanaRoot(filenameVanadium); + NXEntry vanaFirstEntry = vanaRoot.openFirstEntry(); + + double wavelength = vanaFirstEntry.getFloat("wavelength"); + + // read in the data + NXData dataGroup = vanaFirstEntry.openNXData("data"); + NXInt data = dataGroup.openIntData(); + + size_t numberOfTubes = static_cast(data.dim0()); + size_t numberOfPixelsPerTube = static_cast(data.dim1()); + size_t numberOfChannels = static_cast(data.dim2()); + + if (wavelength != m_wavelength || numberOfTubes != m_numberOfTubes + || numberOfPixelsPerTube != m_numberOfPixelsPerTube + || numberOfChannels != m_numberOfChannels) { + throw std::runtime_error( + "Vanadium and Data were not collected in the same conditions!"); + } + + data.load(); + int calculatedDetectorElasticPeakPosition = getDetectorElasticPeakPosition( + data); + return calculatedDetectorElasticPeakPosition; + } + /** * Loads all the spectra into the workspace, including that from the monitor * * @param entry :: The Nexus entry + * @param vanaCalculatedDetectorElasticPeakPosition :: If -1 uses this value as the elastic peak position at the detector. */ - void LoadILL::loadDataIntoTheWorkSpace(NeXus::NXEntry& entry) + void LoadILL::loadDataIntoTheWorkSpace(NeXus::NXEntry& entry, int vanaCalculatedDetectorElasticPeakPosition) { // read in the data @@ -424,8 +490,12 @@ namespace Mantid * Detector: Find real elastic peak in the detector. * Looks for a few elastic peaks on the equatorial line of the detector. */ - int calculatedDetectorElasticPeakPosition = getDetectorElasticPeakPosition( - data); + int calculatedDetectorElasticPeakPosition; + if (vanaCalculatedDetectorElasticPeakPosition == -1) + calculatedDetectorElasticPeakPosition = getDetectorElasticPeakPosition(data); + else + calculatedDetectorElasticPeakPosition = vanaCalculatedDetectorElasticPeakPosition; + double theoreticalElasticTOF = (m_loader.calculateTOF(m_l1,m_wavelength) + m_loader.calculateTOF(m_l2,m_wavelength)) * 1e6; //microsecs @@ -485,29 +555,23 @@ namespace Mantid /** * Run the Child Algorithm LoadInstrument. */ - void LoadILL::runLoadInstrument() - { + void LoadILL::runLoadInstrument() { - IAlgorithm_sptr loadInst = createChildAlgorithm("LoadInstrument"); - - // Now execute the Child Algorithm. Catch and log any error, but don't stop. - try - { - - // TODO: depending on the m_numberOfPixelsPerTube we might need to load a different IDF - - loadInst->setPropertyValue("InstrumentName", m_instrumentName); - loadInst->setProperty("Workspace", - m_localWorkspace); - loadInst->execute(); - } catch (...) - { - g_log.information("Cannot load the instrument definition."); - } - } + IAlgorithm_sptr loadInst = createChildAlgorithm("LoadInstrument"); + // Now execute the Child Algorithm. Catch and log any error, but don't stop. + try { + // TODO: depending on the m_numberOfPixelsPerTube we might need to load a different IDF + loadInst->setPropertyValue("InstrumentName", m_instrumentName); + loadInst->setProperty("Workspace", + m_localWorkspace); + loadInst->execute(); + } catch (...) { + g_log.information("Cannot load the instrument definition."); + } + } diff --git a/Code/Mantid/Framework/DataHandling/src/LoadInstrument.cpp b/Code/Mantid/Framework/DataHandling/src/LoadInstrument.cpp index f79f227b0f30..4aec499f0b65 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadInstrument.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadInstrument.cpp @@ -237,7 +237,7 @@ namespace Mantid try { // To allow the use of ExperimentInfo instead of workspace, we call it manually - LoadParameterFile::execManually(fullPathParamIDF, m_workspace); + LoadParameterFile::execManually(false, fullPathParamIDF, "", m_workspace); g_log.debug("Parameters loaded successfully."); } catch (std::invalid_argument& e) { diff --git a/Code/Mantid/Framework/DataHandling/src/LoadLog.cpp b/Code/Mantid/Framework/DataHandling/src/LoadLog.cpp index 13d05522df7b..6387a2bb9f34 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadLog.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadLog.cpp @@ -541,6 +541,7 @@ namespace Mantid /** * Count the number of columns in the first line of the text file * @param logFileStream :: stream to the file + * @param logFileName :: name for the log file */ int LoadLog::countNumberColumns(std::ifstream& logFileStream, const std::string& logFileName) { diff --git a/Code/Mantid/Framework/DataHandling/src/LoadMcStas.cpp b/Code/Mantid/Framework/DataHandling/src/LoadMcStas.cpp index ef1eb68fab33..d208d9fa3b79 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadMcStas.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadMcStas.cpp @@ -211,8 +211,8 @@ namespace DataHandling /** * Return the confidence with with this algorithm can load the file - * @param descriptor A descriptor for the file - * @param descriptor A descriptor for the file + * @param eventEntries map of the file entries that have events + * @param outputGroup pointer to the workspace group * @param nxFile Reads data from inside first first top entry */ void LoadMcStas::readEventData(const std::map& eventEntries, WorkspaceGroup_sptr& outputGroup, ::NeXus::File& nxFile) @@ -389,7 +389,9 @@ namespace DataHandling /** * Return the confidence with with this algorithm can load the file - * @param descriptor A descriptor for the file + * @param histogramEntries map of the file entries that have histogram + * @param outputGroup pointer to the workspace group + * @param nxFile Reads data from inside first first top entry * @returns An integer specifying the confidence level. 0 indicates it will not be used */ void LoadMcStas::readHistogramData(const std::map& histogramEntries, WorkspaceGroup_sptr& outputGroup, ::NeXus::File& nxFile) diff --git a/Code/Mantid/Framework/DataHandling/src/LoadMuonNexus.cpp b/Code/Mantid/Framework/DataHandling/src/LoadMuonNexus.cpp index f13efb2dff3e..69023c301538 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadMuonNexus.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadMuonNexus.cpp @@ -91,6 +91,9 @@ namespace Mantid declareProperty(new WorkspaceProperty("DeadTimeTable", "", Direction::Output, PropertyMode::Optional), "Table or a group of tables containing detector dead times"); + + declareProperty(new WorkspaceProperty("DetectorGroupingTable", "", Direction::Output, PropertyMode::Optional), + "Table or a group of tables with information about the detector grouping stored in the file (if any)"); } /// Validates the optional 'spectra to read' properties, if they have been set @@ -166,8 +169,9 @@ namespace Mantid * @param descriptor A descriptor for the file * @returns An integer specifying the confidence level. 0 indicates it will not be used */ - int LoadMuonNexus::confidence(Kernel::NexusDescriptor &) const + int LoadMuonNexus::confidence(Kernel::NexusDescriptor &descriptor) const { + UNUSED_ARG(descriptor); return 0; // Not to be used but LoadMuonNexus2, which inherits from this will } diff --git a/Code/Mantid/Framework/DataHandling/src/LoadMuonNexus1.cpp b/Code/Mantid/Framework/DataHandling/src/LoadMuonNexus1.cpp index 725e583c4cd8..e92dfff35a9b 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadMuonNexus1.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadMuonNexus1.cpp @@ -32,6 +32,9 @@ To determine if a file contains data from more than one period the field ''switc If this value is greater than one it is taken to be the number of periods, N_p of the data. In this case the N_s spectra in the ''histogram_data'' field are split with N_s/N_p assigned to each period. +===Dead times and detector grouping=== +Muon Nexus v1 files might contain dead time and detector grouping informationl. These are loaded as TableWorkspaces of the format accepted by ApplyDeadTimeCorr and MuonGroupDetectors accordingly. These are returned if and only if names are specified for the properties. For multi-period data workspace groups might be returned, if information in the Nexus files contains this information for each period. + ===ChildAlgorithms used=== The ChildAlgorithms used by LoadMuonNexus are: @@ -170,9 +173,12 @@ namespace Mantid m_numberOfPeriods = nxload.t_nper; } - // When we know number of periods and spectra - we can load dead times + // Try to load dead time info loadDeadTimes(root); + // Try to load detector grouping info + loadDetectorGrouping(root); + // Need to extract the user-defined output workspace name Property *ws = getProperty("OutputWorkspace"); std::string localWSName = ws->value(); @@ -520,6 +526,75 @@ namespace Mantid // error } + /** + * Loads detector grouping. + * @param root :: Root entry of the Nexus file to read from + */ + void LoadMuonNexus1::loadDetectorGrouping(NXRoot& root) + { + if ( getPropertyValue("DetectorGroupingTable").empty() ) + return; + + NXEntry dataEntry = root.openEntry("run/histogram_data_1"); + + NXInfo infoGrouping = dataEntry.getDataSetInfo("grouping"); + if ( infoGrouping.stat != NX_ERROR ) + { + NXInt groupingData = dataEntry.openNXInt("grouping"); + groupingData.load(); + + int numGroupingEntries = groupingData.dim0(); + + std::vector grouping; + grouping.reserve(numGroupingEntries); + + for ( int i = 0; i < numGroupingEntries; i++ ) + grouping.push_back(groupingData[i]); + + if ( numGroupingEntries < m_numberOfSpectra ) + { + throw Exception::FileError("Number of grouping entries is less than number of spectra", + m_filename); + } + else if ( numGroupingEntries == m_numberOfSpectra) + { + // Simpliest case - one grouping entry per spectra + TableWorkspace_sptr table = createDetectorGroupingTable( grouping.begin(), grouping.end() ); + + if ( table->rowCount() != 0 ) + setProperty("DetectorGroupingTable", table); + } + else + { + // More complex case - grouping information for every period + + if ( numGroupingEntries != m_numberOfSpectra * m_numberOfPeriods ) + { + throw Exception::FileError("Number of grouping entries doesn't cover every spectra in every period", + m_filename); + } + + WorkspaceGroup_sptr tableGroup = boost::make_shared(); + + for ( auto it = grouping.begin(); it != grouping.end(); it += m_numberOfSpectra ) + { + TableWorkspace_sptr table = createDetectorGroupingTable(it, it + m_numberOfSpectra); + + if ( table->rowCount() != 0 ) + tableGroup->addWorkspace(table); + } + + if ( tableGroup->size() != 0 ) + { + if ( tableGroup->size() != static_cast(m_numberOfPeriods) ) + throw Exception::FileError("Zero grouping for some of the periods", m_filename); + + setProperty("DetectorGroupingTable", tableGroup); + } + } + } + } + /** * Creates Dead Time Table using all the data between begin and end. * @@ -547,6 +622,42 @@ namespace Mantid return deadTimeTable; } + /** + * Creates Detector Grouping Table using all the data between begin and end. + * + * @param begin :: Iterator to the first element of the data to use + * @param end :: Iterator to the last element of the data to use + * @return Detector Grouping Table create using the data + */ + TableWorkspace_sptr LoadMuonNexus1::createDetectorGroupingTable( + std::vector::const_iterator begin, std::vector::const_iterator end) + { + auto detectorGroupingTable = boost::dynamic_pointer_cast( + WorkspaceFactory::Instance().createTable("TableWorkspace") ); + + detectorGroupingTable->addColumn("vector_int", "Detectors"); + + std::map> grouping; + + for ( auto it = begin; it != end; ++it ) + { + // Add detector ID to the list of group detectors. Detector ID is always + // spectra index + 1 + grouping[*it].push_back( static_cast( std::distance(begin,it) ) + 1 ); + } + + for ( auto it = grouping.begin(); it != grouping.end(); ++it ) + { + if ( it->first != 0) // Skip 0 group + { + TableRow newRow = detectorGroupingTable->appendRow(); + newRow << it->second; + } + } + + return detectorGroupingTable; + } + /** Load in a single spectrum taken from a NeXus file * @param tcbs :: The vector containing the time bin boundaries * @param hist :: The workspace index diff --git a/Code/Mantid/Framework/DataHandling/src/LoadNexusProcessed.cpp b/Code/Mantid/Framework/DataHandling/src/LoadNexusProcessed.cpp index 8d7986c1b3d4..11baf15df9ca 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadNexusProcessed.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadNexusProcessed.cpp @@ -339,7 +339,7 @@ bool LoadNexusProcessed::checkForCommonNameStem(NXRoot & root, std::vector +#include + +using namespace Mantid::API; +using namespace Mantid::Kernel; + +namespace Mantid +{ +namespace DataHandling +{ + + // Register the algorithm into the AlgorithmFactory + DECLARE_ALGORITHM(LoadPDCharacterizations) + + /// key for a instrument parameter file being listed + static const std::string IPARM_KEY("Instrument parameter file:"); + static const std::string L1_KEY("L1"); + static const std::string ZERO("0."); + + //---------------------------------------------------------------------------------------------- + /** Constructor + */ + LoadPDCharacterizations::LoadPDCharacterizations() + { + } + + //---------------------------------------------------------------------------------------------- + /** Destructor + */ + LoadPDCharacterizations::~LoadPDCharacterizations() + { + } + + + //---------------------------------------------------------------------------------------------- + /// Algorithm's name for identification. @see Algorithm::name + const std::string LoadPDCharacterizations::name() const { return "LoadPDCharacterizations";}; + + /// Algorithm's version for identification. @see Algorithm::version + int LoadPDCharacterizations::version() const { return 1;}; + + /// Algorithm's category for identification. @see Algorithm::category + const std::string LoadPDCharacterizations::category() const { return "Workflow\\DataHandling";} + + //---------------------------------------------------------------------------------------------- + /// Sets documentation strings for this algorithm + void LoadPDCharacterizations::initDocs() + { + std::string descr("Load a characterization file used in Powder Diffraction Reduction."); + this->setWikiSummary(descr); + this->setOptionalMessage(descr); + } + + //---------------------------------------------------------------------------------------------- + /** Initialize the algorithm's properties. + */ + void LoadPDCharacterizations::init() + { + std::vector exts; + exts.push_back(".txt"); + declareProperty(new FileProperty("Filename", "", FileProperty::Load, exts), + "Characterizations file"); + + declareProperty(new WorkspaceProperty("OutputWorkspace", "", + Direction::Output), + "Output for the information of characterizations and runs"); + + declareProperty("IParmFilename", std::string(""), "Name of the gsas instrument parameter file.", + Direction::Output); + declareProperty("PrimaryFlightPath", EMPTY_DBL(), "Primary flight path L1 of the powder diffractomer. ", + Direction::Output); + declareProperty(new ArrayProperty("SpectrumIDs", Direction::Output), + "Spectrum IDs (note that it is not detector ID or workspace indices). The list must be either empty or have a size equal to input workspace's histogram number. "); + declareProperty(new ArrayProperty("L2", Direction::Output), + "Seconary flight (L2) paths for each detector. Number of L2 given must be same as number of histogram."); + declareProperty(new ArrayProperty("Polar", Direction::Output), + "Polar angles (two thetas) for detectors. Number of 2theta given must be same as number of histogram."); + declareProperty(new ArrayProperty("Azimuthal", Direction::Output), + "Azimuthal angles (out-of-plane) for detectors. " + "Number of azimuthal angles given must be same as number of histogram."); + } + + //---------------------------------------------------------------------------------------------- + /** Execute the algorithm. + */ + void LoadPDCharacterizations::exec() + { + // open the file for reading + std::string filename = this->getProperty("Filename"); + std::ifstream file(filename.c_str()); + if (!file) + { + throw Exception::FileError("Unable to open file", filename); + } + + // read the first line and decide what to do + std::string firstLine = Strings::getLine(file); + if (firstLine.substr(0, IPARM_KEY.size()) == IPARM_KEY) + { + firstLine = Strings::strip(firstLine.substr(IPARM_KEY.size())); + this->setProperty("IParmFilename", firstLine); + this->readFocusInfo(file); + } + else + { + // things expect the L1 to be zero if it isn't set + this->setProperty("PrimaryFlightPath", 0.); + } + + // now the rest of the file + // setup the default table workspace for the characterization runs + ITableWorkspace_sptr wksp = WorkspaceFactory::Instance().createTable(); + wksp->addColumn("double", "frequency"); + wksp->addColumn("double", "wavelength"); + wksp->addColumn("int", "bank"); + wksp->addColumn("int", "vanadium"); + wksp->addColumn("int", "container"); + wksp->addColumn("int", "empty"); + wksp->addColumn("str", "d_min"); // b/c it is an array for NOMAD + wksp->addColumn("str", "d_max"); // b/c it is an array for NOMAD + wksp->addColumn("double", "tof_min"); + wksp->addColumn("double", "tof_max"); + this->readCharInfo(file, wksp); + this->setProperty("OutputWorkspace", wksp); + } + + /** + * Parse the stream for the focus positions and instrument parameter filename. + * + * @param file The stream to parse. + */ + void LoadPDCharacterizations::readFocusInfo(std::ifstream &file) + { + // end early if already at the end of the file + if (file.eof()) return; + + std::vector specIds; + std::vector l2; + std::vector polar; + + // parse the file + for (std::string line = Strings::getLine(file); !file.eof(); line = Strings::getLine(file)) + { + line = Strings::strip(line); + // skip empty lines and "comments" + if (line.empty()) continue; + if (line.substr(0,1) == "#") continue; + + std::vector splitted; + boost::split(splitted, line, boost::is_any_of("\t "), boost::token_compress_on); + if (splitted[0] == L1_KEY) + { + this->setProperty("PrimaryFlightPath", boost::lexical_cast(splitted[1])); + break; + } + else if (splitted.size() >= 3) // specid, L2, theta + { + specIds.push_back(boost::lexical_cast(splitted[0])); + l2.push_back(boost::lexical_cast(splitted[1])); + polar.push_back(boost::lexical_cast(splitted[2])); + } + } + if (specIds.size() != l2.size() || specIds.size() != polar.size()) + throw std::runtime_error("Found different number of spectra, L2 and polar angles"); + + // azimuthal angles are all zero + std::vector azi(polar.size(), 0.); + + // set the values + this->setProperty("SpectrumIDs", specIds); + this->setProperty("L2", l2); + this->setProperty("Polar", polar); + this->setProperty("Azimuthal", azi); + } + + /** + * Parse the stream for the characterization file information. + * + * @param file The stream to parse. + * @param wksp The table workspace to fill in. + */ + void LoadPDCharacterizations::readCharInfo(std::ifstream &file, ITableWorkspace_sptr &wksp) + { + // end early if already at the end of the file + if (file.eof()) return; + + // parse the file + for (std::string line = Strings::getLine(file); !file.eof(); line = Strings::getLine(file)) + { + line = Strings::strip(line); + + // skip empty lines and "comments" + if (line.empty()) continue; + if (line.substr(0,1) == "#") continue; + + + // parse the line + std::vector splitted; + boost::split(splitted, line, boost::is_any_of("\t "), boost::token_compress_on); + while (splitted.size() < 10) + splitted.push_back(ZERO); // extra values default to zero + + // add the row + API::TableRow row = wksp->appendRow(); + row << boost::lexical_cast(splitted[0]); // frequency + row << boost::lexical_cast(splitted[1]); // wavelength + row << boost::lexical_cast(splitted[2]); // bank + row << boost::lexical_cast(splitted[3]); // vanadium + row << boost::lexical_cast(splitted[4]); // container + row << boost::lexical_cast(splitted[5]); // empty + row << splitted[6]; // d_min + row << splitted[7]; // d_max + row << boost::lexical_cast(splitted[8]); // tof_min + row << boost::lexical_cast(splitted[9]); // tof_max + } + } + +} // namespace DataHandling +} // namespace Mantid diff --git a/Code/Mantid/Framework/DataHandling/src/LoadParameterFile.cpp b/Code/Mantid/Framework/DataHandling/src/LoadParameterFile.cpp index fa30cca9b7b5..128ff57d5ba7 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadParameterFile.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadParameterFile.cpp @@ -35,6 +35,7 @@ This algorithm allows instrument parameters to be specified in a separate file f #include #include #include +#include #include #include #include "MantidGeometry/Instrument/InstrumentDefinitionParser.h" @@ -46,6 +47,7 @@ using Poco::XML::Node; using Poco::XML::NodeList; using Poco::XML::NodeIterator; using Poco::XML::NodeFilter; +using Poco::XML::AutoPtr; using Mantid::Geometry::InstrumentDefinitionParser; @@ -59,8 +61,8 @@ DECLARE_ALGORITHM(LoadParameterFile) /// Sets documentation strings for this algorithm void LoadParameterFile::initDocs() { - this->setWikiSummary("Loads instrument parameters into a [[workspace]]. where these parameters are associated component names as defined in Instrument Definition File ([[InstrumentDefinitionFile|IDF]])."); - this->setOptionalMessage("Loads instrument parameters into a workspace. where these parameters are associated component names as defined in Instrument Definition File (IDF)."); + this->setWikiSummary("Loads instrument parameters into a [[workspace]]. where these parameters are associated component names as defined in Instrument Definition File ([[InstrumentDefinitionFile|IDF]]) or a string consisting of the contents of such.."); + this->setOptionalMessage("Loads instrument parameters into a workspace. where these parameters are associated component names as defined in Instrument Definition File (IDF) or a string consisting of the contents of such."); } @@ -80,8 +82,9 @@ void LoadParameterFile::init() declareProperty( new WorkspaceProperty("Workspace","Anonymous",Direction::InOut), "The name of the workspace to load the instrument parameters into." ); - declareProperty(new FileProperty("Filename","", FileProperty::Load, ".xml"), - "The filename (including its full or relative path) of a parameter defintion file. The file extension must either be .xml or .XML."); + declareProperty(new FileProperty("Filename","", FileProperty::OptionalLoad, ".xml"), + "The filename (including its full or relative path) of a parameter definition file. The file extension must either be .xml or .XML."); + declareProperty("ParameterXML","","The parameter definition XML as a string."); } /** Executes the algorithm. Reading in the file and creating and populating @@ -95,31 +98,58 @@ void LoadParameterFile::exec() // Retrieve the filename from the properties std::string filename = getPropertyValue("Filename"); + // Retrieve the parameter XML string from the properties + const Property * const parameterXMLProperty = getProperty("ParameterXML"); // to check whether it is default + std::string parameterXML = getPropertyValue("ParameterXML"); + + // Check the two properties (at least one must be set) + if( filename.empty() && parameterXMLProperty->isDefault()){ + throw Kernel::Exception::FileError("Either the Filename or ParameterXML property of LoadParameterFile most be specified to load an IDF" , filename); + } + // Get the input workspace const MatrixWorkspace_sptr localWorkspace = getProperty("Workspace"); - execManually(filename, localWorkspace); + execManually(!parameterXMLProperty->isDefault(), filename, parameterXML, localWorkspace); } -void LoadParameterFile::execManually(std::string filename, Mantid::API::ExperimentInfo_sptr localWorkspace) +void LoadParameterFile::execManually(bool useString, std::string filename, std::string parameterXML, Mantid::API::ExperimentInfo_sptr localWorkspace) { - // TODO: Refactor to remove the need for the const cast + // TODO: Refactor to remove the need for the const cast (ticket #8521) Instrument_sptr instrument = boost::const_pointer_cast(localWorkspace->getInstrument()->baseInstrument()); // Set up the DOM parser and parse xml file DOMParser pParser; - Document* pDoc; - try - { - pDoc = pParser.parse(filename); - } - catch(Poco::Exception& exc) - { - throw Kernel::Exception::FileError(exc.displayText() + ". Unable to parse File:", filename); - } - catch(...) + AutoPtr pDoc; + + if(useString){ + try + { + pDoc = pParser.parseString(parameterXML); + } + catch(Poco::Exception& exc) + { + throw Kernel::Exception::FileError (exc.displayText() + ". Unable to parse parameter XML string","ParameterXML"); + } + catch(...) + { + throw Kernel::Exception::FileError("Unable to parse parameter XML string","ParameterXML"); + } + } + else { - throw Kernel::Exception::FileError("Unable to parse File:" , filename); + try + { + pDoc = pParser.parse(filename); + } + catch(Poco::Exception& exc) + { + throw Kernel::Exception::FileError(exc.displayText() + ". Unable to parse File:", filename); + } + catch(...) + { + throw Kernel::Exception::FileError("Unable to parse File:" , filename); + } } // Get pointer to root element @@ -136,7 +166,6 @@ void LoadParameterFile::execManually(std::string filename, Mantid::API::Experime // populate parameter map of workspace localWorkspace->populateInstrumentParameters(); - pDoc->release(); } } // namespace DataHandling diff --git a/Code/Mantid/Framework/DataHandling/src/LoadRawHelper.cpp b/Code/Mantid/Framework/DataHandling/src/LoadRawHelper.cpp index 13e615b49560..3acc90ad254d 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadRawHelper.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadRawHelper.cpp @@ -455,7 +455,7 @@ namespace Mantid */ bool LoadRawHelper::isAscii(FILE* file) const { - return LoadAscii::isAscii(file); + return Kernel::FileDescriptor::isAscii(file); } diff --git a/Code/Mantid/Framework/DataHandling/src/LoadSassena.cpp b/Code/Mantid/Framework/DataHandling/src/LoadSassena.cpp index d3d566e54f6c..cbfc6466ed77 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadSassena.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadSassena.cpp @@ -56,9 +56,12 @@ void LoadSassena::initDocs() * @param descriptor A descriptor for the file * @returns An integer specifying the confidence level. 0 indicates it will not be used */ -int LoadSassena::confidence(Kernel::NexusDescriptor & descriptor) const +int LoadSassena::confidence( Kernel::NexusDescriptor & descriptor ) const { - if(descriptor.hasRootAttr("sassena_version")) return 99; + if( descriptor.hasRootAttr( "sassena_version" ) || descriptor.pathExists("/qvectors") ) + { + return 99; + } return 0; } @@ -82,15 +85,18 @@ void LoadSassena::registerWorkspace( API::WorkspaceGroup_sptr gws, const std::st * @param setName string name of dataset * @param dims storing dimensionality */ -void LoadSassena::dataSetInfo( const hid_t& h5file, const std::string setName, hsize_t* dims) + +herr_t LoadSassena::dataSetInfo( const hid_t& h5file, const std::string setName, hsize_t* dims ) const { H5T_class_t class_id; size_t type_size; - if( H5LTget_dataset_info( h5file, setName.c_str(), dims, &class_id, &type_size ) < 0 ) + herr_t errorcode; + errorcode = H5LTget_dataset_info( h5file, setName.c_str(), dims, &class_id, &type_size ); + if( errorcode < 0 ) { - this->g_log.error("Unable to read "+setName+" dataset info"); - throw Kernel::Exception::FileError("Unable to read "+setName+" dataset info:" , m_filename); + g_log.error( "Unable to read " + setName + " dataset info" ); } + return errorcode; } /** @@ -119,10 +125,13 @@ void LoadSassena::dataSetDouble( const hid_t& h5file, const std::string setName, const MantidVec LoadSassena::loadQvectors(const hid_t& h5file, API::WorkspaceGroup_sptr gws) { const std::string gwsName = this->getPropertyValue("OutputWorkspace"); - std::string setName("qvectors"); + const std::string setName("qvectors"); hsize_t dims[3]; - this->dataSetInfo(h5file, setName, dims); + if ( dataSetInfo(h5file, setName, dims) < 0 ) + { + throw Kernel::Exception::FileError( "Unable to read " + setName + " dataset info:" , m_filename ); + } int nq = static_cast( dims[0] ); //number of q-vectors double* buf = new double[nq*3]; this->dataSetDouble(h5file,"qvectors",buf); @@ -201,7 +210,10 @@ void LoadSassena::loadFQT(const hid_t& h5file, API::WorkspaceGroup_sptr gws, con int nq = static_cast( qvmod.size() ); //number of q-vectors const double dt = getProperty("TimeUnit"); //time unit increment, in picoseconds; hsize_t dims[3]; - this->dataSetInfo(h5file, setName, dims); + if( dataSetInfo( h5file, setName, dims ) < 0 ) + { + throw Kernel::Exception::FileError( "Unable to read " + setName + " dataset info:" , m_filename ); + } int nnt = static_cast( dims[1] ); //number of non-negative time points int nt = 2*nnt - 1; //number of time points int origin = nnt-1; @@ -334,7 +346,7 @@ void LoadSassena::exec() char cversion[16]; if ( H5LTget_attribute_string( h5file, "/", "sassena_version", cversion ) < 0 ) { - throw Kernel::Exception::FileError("Unable to read Sassena version" , m_filename); + this->g_log.error("Unable to read Sassena version"); } //const std::string version(cversion); //determine which loader protocol to use based on the version diff --git a/Code/Mantid/Framework/DataHandling/src/LoadTOFRawNexus.cpp b/Code/Mantid/Framework/DataHandling/src/LoadTOFRawNexus.cpp index 501d588dd794..6272eea8aaa7 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadTOFRawNexus.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadTOFRawNexus.cpp @@ -316,6 +316,7 @@ void LoadTOFRawNexus::countPixels(const std::string &nexusfilename, const std::s * @param entry_name :: NXentry name * @param bankName :: NXdata bank name * @param WS :: workspace to modify + * @param id_to_wi :: det ID to workspace index mapping */ void LoadTOFRawNexus::loadBank(const std::string &nexusfilename, const std::string & entry_name, const std::string &bankName, API::MatrixWorkspace_sptr WS, const detid2index_map& id_to_wi) diff --git a/Code/Mantid/Framework/DataHandling/src/SaveFocusedXYE.cpp b/Code/Mantid/Framework/DataHandling/src/SaveFocusedXYE.cpp index 15a0e4080ce4..5265015dc43d 100644 --- a/Code/Mantid/Framework/DataHandling/src/SaveFocusedXYE.cpp +++ b/Code/Mantid/Framework/DataHandling/src/SaveFocusedXYE.cpp @@ -24,6 +24,7 @@ #include #include #include +#include using namespace Mantid::DataHandling; @@ -57,9 +58,10 @@ void SaveFocusedXYE::init() "The bank number in the file will be the workspace index + StartAtBankNumber."); declareProperty("Append", false, "If true and Filename already exists, append, else overwrite"); declareProperty("IncludeHeader", true, "Whether to include the header lines (default: true)"); - std::vector header(2); + std::vector header(3); header[0] = "XYE"; header[1] = "MAUD"; + header[2] = "TOPAS"; declareProperty("Format", "XYE", boost::make_shared(header), "A type of the header: XYE (default) or MAUD."); } @@ -98,15 +100,27 @@ void SaveFocusedXYE::exec() using std::ios_base; ios_base::openmode mode = (append ? (ios_base::out | ios_base::app) : ios_base::out); + m_comment = "#"; std::string headerType = getProperty("Format"); if ( headerType == "XYE" ) { m_headerType = XYE; } - else + else if (headerType == "MAUD") { m_headerType = MAUD; } + else if (headerType == "TOPAS") + { + m_headerType = TOPAS; + m_comment = "'"; + } + else + { + std::stringstream msg; + msg << "Unrecognized format \"" << m_headerType << "\""; + throw std::runtime_error(msg.str()); + } Progress progress(this, 0.0, 1.0, nHist); for (size_t i = 0; i < nHist; i++) @@ -232,11 +246,11 @@ void SaveFocusedXYE::setOtherProperties(IAlgorithm* alg, const std::string& prop */ void SaveFocusedXYE::writeHeaders(std::ostream& os, Mantid::API::MatrixWorkspace_const_sptr& workspace) const { - if ( m_headerType == XYE ) + if ( m_headerType == XYE || m_headerType == TOPAS ) { writeXYEHeaders( os, workspace ); } - else + else // MAUD { writeMAUDHeaders( os, workspace ); } @@ -249,10 +263,10 @@ void SaveFocusedXYE::writeHeaders(std::ostream& os, Mantid::API::MatrixWorkspace */ void SaveFocusedXYE::writeXYEHeaders(std::ostream& os,Mantid::API::MatrixWorkspace_const_sptr& workspace) const { - os << "# File generated by Mantid:" << std::endl; - os << "# Instrument: " << workspace->getInstrument()->getName() << std::endl; - os << "# The X-axis unit is: " << workspace->getAxis(0)->unit()->caption() << std::endl; - os << "# The Y-axis unit is: " << workspace->YUnitLabel() << std::endl; + os << m_comment << " File generated by Mantid:" << std::endl; + os << m_comment << " Instrument: " << workspace->getInstrument()->getName() << std::endl; + os << m_comment << " The X-axis unit is: " << workspace->getAxis(0)->unit()->caption() << std::endl; + os << m_comment << " The Y-axis unit is: " << workspace->YUnitLabel() << std::endl; } /** @@ -274,11 +288,11 @@ void SaveFocusedXYE::writeMAUDHeaders(std::ostream& os,Mantid::API::MatrixWorksp void SaveFocusedXYE::writeSpectraHeader(std::ostream& os, size_t index1, size_t index2, double flightPath, double tth, const std::string& caption) { - if ( m_headerType == XYE ) + if ( m_headerType == XYE || m_headerType == TOPAS ) { writeXYESpectraHeader( os, index1, index2, flightPath, tth, caption ); } - else + else // MAUD { writeMAUDSpectraHeader( os, index1, index2, flightPath, tth, caption ); } @@ -291,8 +305,8 @@ void SaveFocusedXYE::writeXYESpectraHeader(std::ostream& os, size_t index1, size UNUSED_ARG(index2); UNUSED_ARG(flightPath); UNUSED_ARG(tth); - os << "# Data for spectra :" << index1 << std::endl; - os << "# " << caption << " Y E" << std::endl; + os << m_comment << " Data for spectra :" << index1 << std::endl; + os << m_comment << " " << caption << " Y E" << std::endl; } /// Write spectra MAUD header diff --git a/Code/Mantid/Framework/DataHandling/src/SaveSPE.cpp b/Code/Mantid/Framework/DataHandling/src/SaveSPE.cpp index 101e214aec79..08b2c95d4365 100644 --- a/Code/Mantid/Framework/DataHandling/src/SaveSPE.cpp +++ b/Code/Mantid/Framework/DataHandling/src/SaveSPE.cpp @@ -124,7 +124,7 @@ namespace Mantid } /** Write the data to the SPE file - * @param outFile :: the file object to write to + * @param outSPEFile :: the file object to write to * @param inputWS :: the workspace to be saved */ void SaveSPE::writeSPEFile(FILE * outSPEFile, const API::MatrixWorkspace_const_sptr &inputWS) diff --git a/Code/Mantid/Framework/DataHandling/test/ApplyGroupingFromMuonNexusTest.h b/Code/Mantid/Framework/DataHandling/test/ApplyGroupingFromMuonNexusTest.h deleted file mode 100644 index 813db4e3ac01..000000000000 --- a/Code/Mantid/Framework/DataHandling/test/ApplyGroupingFromMuonNexusTest.h +++ /dev/null @@ -1,152 +0,0 @@ -#ifndef MANTID_DATAHANDLING_ApplyGroupingFromMuonNexusTEST_H_ -#define MANTID_DATAHANDLING_ApplyGroupingFromMuonNexusTEST_H_ - -#include - -#include "MantidAPI/AnalysisDataService.h" -#include "MantidDataHandling/ApplyGroupingFromMuonNexus.h" -#include "MantidDataHandling/LoadMuonNexus1.h" -#include "MantidDataObjects/Workspace2D.h" -#include - -#include // std::accumulate - -using namespace Mantid::API; -using namespace Mantid::Kernel; -using namespace Mantid::DataHandling; -using namespace Mantid::DataObjects; - -class ApplyGroupingFromMuonNexusTest : public CxxTest::TestSuite -{ -public: - // This pair of boilerplate methods prevent the suite being created statically - // This means the constructor isn't called when running other tests - static ApplyGroupingFromMuonNexusTest *createSuite() { return new ApplyGroupingFromMuonNexusTest(); } - static void destroySuite( ApplyGroupingFromMuonNexusTest *suite ) { delete suite; } - - - void test_init() - { - ApplyGroupingFromMuonNexus alg; - TS_ASSERT_THROWS_NOTHING( alg.initialize() ) - TS_ASSERT( alg.isInitialized() ) - } - - void test_execSingle() - { - AnalysisDataService::Instance().clear(); - - // Name of the output workspace. - std::string loadedWsName("ApplyGroupingFromMuonNexusTest_LoadedWS"); - std::string outWsName("ApplyGroupingFromMuonNexusTest_OutputWS"); - std::string dataFileName("emu00006473.nxs"); - - // Load the data we will group - LoadMuonNexus1 loadAlg; - loadAlg.initialize(); - loadAlg.setPropertyValue("Filename", dataFileName); - loadAlg.setPropertyValue("OutputWorkspace", loadedWsName); - loadAlg.execute(); - - ApplyGroupingFromMuonNexus alg; - alg.initialize(); - - TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("InputWorkspace", loadedWsName) ); - TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", dataFileName) ); - TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWsName) ); - TS_ASSERT_THROWS_NOTHING( alg.execute(); ); - TS_ASSERT( alg.isExecuted() ); - - // Retrieve the workspace from data service. - Workspace2D_sptr ws; - TS_ASSERT_THROWS_NOTHING( ws = AnalysisDataService::Instance().retrieveWS(outWsName) ); - TS_ASSERT(ws); - - TS_ASSERT_EQUALS(ws->getNumberHistograms(), 2); - TS_ASSERT_EQUALS(ws->readY(0).size(), 2000); - - TS_ASSERT_EQUALS(ws->readX(0), ws->readX(1)); - TS_ASSERT_DELTA(std::accumulate(ws->readX(0).begin(), ws->readX(0).end(), 0.0), 31507.736, 0.001); - - TS_ASSERT_EQUALS(std::accumulate(ws->readY(0).begin(), ws->readY(0).end(), 0), 32571161); - TS_ASSERT_EQUALS(std::accumulate(ws->readY(1).begin(), ws->readY(1).end(), 0), 18184711); - - TS_ASSERT_DELTA(std::accumulate(ws->readE(0).begin(), ws->readE(0).end(), 0.0), 133292.1, 0.1); - TS_ASSERT_DELTA(std::accumulate(ws->readE(1).begin(), ws->readE(1).end(), 0.0), 101157.1, 0.1); - - AnalysisDataService::Instance().clear(); - } - - void test_execGroup() - { - AnalysisDataService::Instance().clear(); - - // Name of the output workspace. - std::string loadedWsName("ApplyGroupingFromMuonNexusTest_LoadedWS"); - std::string outWsName("ApplyGroupingFromMuonNexusTest_OutputWS"); - std::string dataFileName("MUSR00015189.nxs"); - - // Load the data we will group - LoadMuonNexus1 loadAlg; - loadAlg.initialize(); - loadAlg.setPropertyValue("Filename", dataFileName); - loadAlg.setPropertyValue("OutputWorkspace", loadedWsName); - loadAlg.execute(); - - ApplyGroupingFromMuonNexus alg; - alg.initialize(); - - TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("InputWorkspace", loadedWsName) ); - TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", dataFileName) ); - TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWsName) ); - TS_ASSERT_THROWS_NOTHING( alg.execute(); ); - TS_ASSERT( alg.isExecuted() ); - - // Retrieve the workspace from data service. - WorkspaceGroup_sptr ws; - TS_ASSERT_THROWS_NOTHING( ws = AnalysisDataService::Instance().retrieveWS(outWsName) ); - TS_ASSERT(ws); - - TS_ASSERT_EQUALS(ws->size(), 2); - - // Check the first workspace in group ---------------------------------------------------------- - - Workspace2D_sptr ws1 = boost::dynamic_pointer_cast(ws->getItem(0)); - TS_ASSERT(ws1); - - TS_ASSERT_EQUALS(ws1->getNumberHistograms(), 2); - TS_ASSERT_EQUALS(ws1->readY(0).size(), 2000); - - TS_ASSERT_EQUALS(ws1->readX(0), ws1->readX(1)); - TS_ASSERT_DELTA(std::accumulate(ws1->readX(0).begin(), ws1->readX(0).end(), 0.0), 30915.451, 0.001); - - TS_ASSERT_EQUALS(std::accumulate(ws1->readY(0).begin(), ws1->readY(0).end(), 0), 355655); - TS_ASSERT_EQUALS(std::accumulate(ws1->readY(1).begin(), ws1->readY(1).end(), 0), 262852); - - TS_ASSERT_DELTA(std::accumulate(ws1->readE(0).begin(), ws1->readE(0).end(), 0.0), 14046.9, 0.1); - TS_ASSERT_DELTA(std::accumulate(ws1->readE(1).begin(), ws1->readE(1).end(), 0.0), 12079.8, 0.1); - - // Check the second workspace in group --------------------------------------------------------- - - Workspace2D_sptr ws2 = boost::dynamic_pointer_cast(ws->getItem(1)); - TS_ASSERT(ws2); - - TS_ASSERT_EQUALS(ws2->getNumberHistograms(), 2); - TS_ASSERT_EQUALS(ws2->readY(0).size(), 2000); - - TS_ASSERT_EQUALS(ws1->readX(0), ws2->readX(0)); - TS_ASSERT_EQUALS(ws2->readX(0), ws2->readX(1)); - - TS_ASSERT_EQUALS(std::accumulate(ws2->readY(0).begin(), ws2->readY(0).end(), 0), 359076); - TS_ASSERT_EQUALS(std::accumulate(ws2->readY(1).begin(), ws2->readY(1).end(), 0), 258629); - - TS_ASSERT_DELTA(std::accumulate(ws2->readE(0).begin(), ws2->readE(0).end(), 0.0), 14054.2, 0.1); - TS_ASSERT_DELTA(std::accumulate(ws2->readE(1).begin(), ws2->readE(1).end(), 0.0), 11976.0, 0.1); - - AnalysisDataService::Instance().clear(); - } - -}; - - -#endif /* MANTID_DATAHANDLING_ApplyGroupingFromMuonNexusTEST_H_ */ diff --git a/Code/Mantid/Framework/DataHandling/test/ConvertFullprofToXMLTest.h b/Code/Mantid/Framework/DataHandling/test/ConvertFullprofToXMLTest.h deleted file mode 100644 index 7f5c09811d85..000000000000 --- a/Code/Mantid/Framework/DataHandling/test/ConvertFullprofToXMLTest.h +++ /dev/null @@ -1,357 +0,0 @@ -#ifndef MANTID_DATAHANDLING_CONVERTFULLPROFTOXMLTEST_H_ -#define MANTID_DATAHANDLING_CONVERTFULLPROFTOXMLTEST_H_ - -#include - -#include "MantidDataHandling/ConvertFullprofToXML.h" -#include -#include - -#include -#include -#include -#include -#include - -using Mantid::DataHandling::ConvertFullprofToXML; - -using namespace Mantid; -using namespace Mantid::Kernel; -using namespace Mantid::API; -using namespace Poco::XML; - -using namespace std; - -class ConvertFullprofToXMLTest : public CxxTest::TestSuite -{ -public: - // This pair of boilerplate methods prevent the suite being created statically - // This means the constructor isn't called when running other tests - static ConvertFullprofToXMLTest *createSuite() { return new ConvertFullprofToXMLTest(); } - static void destroySuite( ConvertFullprofToXMLTest *suite ) { delete suite; } - - //---------------------------------------------------------------------------------------------- - /** Test conversion - */ - void testInit() - { - - // Init LoadFullprofResolution - ConvertFullprofToXML alg; - TS_ASSERT_THROWS_NOTHING(alg.initialize()); - - return; - } - - //---------------------------------------------------------------------------------------------- - /** Test conversion - */ - void testExec() - { - // Generate file - string inputFilename("TestConvertFullprofToXMLInput.irf"); - string outputFilename("TestConvertFullprofToXMLOutput.xml"); - generate2BankIrfFile(inputFilename); - - // Init LoadFullprofResolution - ConvertFullprofToXML alg; - alg.initialize(); - - // Set up - alg.setProperty("InputFilename", inputFilename); - alg.setProperty("InstrumentName","POWGEN"); - alg.setProperty("OutputFileName", outputFilename); - - // Execute - TS_ASSERT_THROWS_NOTHING(alg.execute()); - TS_ASSERT(alg.isExecuted()); - - // has the algorithm written a file to disk? - outputFilename = alg.getPropertyValue("outputFilename"); //Get absolute path - TS_ASSERT( Poco::File(outputFilename).exists() ); - - // Check output file - std::string xmlText = Strings::loadFile(outputFilename); - Poco::XML::DOMParser pParser; - AutoPtr doc; - TS_ASSERT_THROWS_NOTHING(doc = pParser.parseString(xmlText)); - TS_ASSERT(doc); - if(doc) - { - Element *rootElem = doc->documentElement(); - TS_ASSERT(rootElem->hasChildNodes()); - AutoPtr componentLinkNodeList = rootElem->getElementsByTagName("component-link");; // get component-link elements - size_t numComponentLinks = componentLinkNodeList->length(); - TS_ASSERT_EQUALS(numComponentLinks,3); // Three component-link elements expected - if( numComponentLinks == 3) // We only check the component-links if there are the expected number of them. - { - // Whole Instrument - Poco::XML::Element* componentLinkElem1 = static_cast(componentLinkNodeList->item(0)); - TS_ASSERT(componentLinkElem1); - if(componentLinkElem1) - { - TS_ASSERT_EQUALS(componentLinkElem1->getAttribute("name"),"POWGEN"); - - AutoPtr parameterNodeList = componentLinkElem1->getElementsByTagName("parameter"); // get parameter elements - size_t numParameters = parameterNodeList->length(); - TS_ASSERT_EQUALS(numParameters,4); // Four parameter elements expected - - if( numParameters == 4) // We only check parameters if there are the expected number of them. - { - Poco::XML::Element* paramElem1 = static_cast(parameterNodeList->item(0)); - do_test_paramemeter( paramElem1, "IkedaCarpenterPV:Alpha0", 0.000008, 0.0,"TOF", "", true ); - - //TS_FAIL("About to get Beta0"); - Poco::XML::Element* paramElem2 = static_cast(parameterNodeList->item(1)); - //TS_FAIL("Got Beta0"); - do_test_paramemeter( paramElem2, "IkedaCarpenterPV:Beta0", 6.251096, 0.0, "TOF", "", true ); - - Poco::XML::Element* paramElem3 = static_cast(parameterNodeList->item(2)); - do_test_paramemeter( paramElem3, "IkedaCarpenterPV:Alpha1", 0.1, 0.0, "TOF", "", true ); - - Poco::XML::Element* paramElem4 = static_cast(parameterNodeList->item(3)); - do_test_paramemeter( paramElem4, "IkedaCarpenterPV:Kappa", -0.1, 0.0, "", "", true ); - } - - //parameterNodeList->release(); // Finished with parameter list - } - - // Bank1 - Poco::XML::Element* componentLinkElem2 = static_cast(componentLinkNodeList->item(1)); - TS_ASSERT(componentLinkElem2); - if(componentLinkElem2) - { - TS_ASSERT_EQUALS(componentLinkElem2->getAttribute("name"),"bank1"); - - AutoPtr parameterNodeList = componentLinkElem2->getElementsByTagName("parameter"); // get parameter elements - size_t numParameters = parameterNodeList->length(); - TS_ASSERT_EQUALS(numParameters,2); // Two parameter elements expected - - if(numParameters== 2) // We only check parameters if there are the expected number of them. - { - Poco::XML::Element* paramElem1 = static_cast(parameterNodeList->item(0)); - do_test_paramemeter( paramElem1, "IkedaCarpenterPV:SigmaSquared", 0.00044, 0.355, "TOF^2", "dSpacing", false ); - - Poco::XML::Element* paramElem2 = static_cast(parameterNodeList->item(1)); - do_test_paramemeter( paramElem2, "IkedaCarpenterPV:Gamma", 0.0, 0.0, "TOF", "dSpacing", false ); - } - - //parameterNodeList->release(); // Finished with parameter list - } - - // Bank3 - Poco::XML::Element* componentLinkElem3 = static_cast(componentLinkNodeList->item(2)); - TS_ASSERT(componentLinkElem3); - if(componentLinkElem3) - { - TS_ASSERT_EQUALS(componentLinkElem3->getAttribute("name"),"bank3"); - - AutoPtr parameterNodeList = componentLinkElem3->getElementsByTagName("parameter"); // get parameter elements - size_t numParameters = parameterNodeList->length(); - TS_ASSERT_EQUALS(numParameters,2); // Two parameter elements expected - - if(numParameters== 2) // We only check parameters if there are the expected number of them. - { - Poco::XML::Element* paramElem1 = static_cast(parameterNodeList->item(0)); - do_test_paramemeter( paramElem1, "IkedaCarpenterPV:SigmaSquared", 10.0, 0.0, "TOF^2", "dSpacing", false ); - - Poco::XML::Element* paramElem2 = static_cast(parameterNodeList->item(1)); - do_test_paramemeter( paramElem2, "IkedaCarpenterPV:Gamma", 2.742, 0.0, "TOF", "dSpacing", false ); - } - } - } - } - - //Clean up - Poco::File(inputFilename).remove(); - Poco::File(outputFilename).remove(); - - return; - } - - //--------------------------------------------------------------------------------------------- - /** Test missing instrument parameter - */ - void testMissingInstrument() - { - // Generate file - string inputFilename("TestConvertFullprofToXMLInput.irf"); - string outputFilename("TestConvertFullprofToXMLMissingInstrumentOutput.xml"); - generate2BankIrfFile(inputFilename); - - // Init LoadFullprofResolution - ConvertFullprofToXML alg; - alg.initialize(); - - // Set up - alg.setProperty("InputFilename", inputFilename); - TS_ASSERT_THROWS(alg.setProperty("InstrumentName",""), std::invalid_argument); - alg.setProperty("OutputFileName", outputFilename); - - //Clean up - Poco::File(inputFilename).remove(); - } - - //---------------------------------------------------------------------------------------------- - /** Do test on a parameter element - ** parameElem: parameter element to be tested - ** name: expected name of parameter element to be tested - ** eq1: expected value of first double - ** eq2: expected value of second double, if expected - ** resultUnit: expected value of result-unit - ** unit: expected value of unit - ** fixed: true if parameter is expected to be fixed - */ - void do_test_paramemeter(const Poco::XML::Element* paramElem, const std::string& name, const double eq1, const double eq2, const std::string& resultUnit, const std::string& unit, bool fixed ) - { - TS_ASSERT(paramElem); - if(paramElem) - { - TS_ASSERT_EQUALS(paramElem->getAttribute("type"),"fitting"); - TS_ASSERT_EQUALS(paramElem->getAttribute("name"),name); - Poco::XML::Element* formulaElem = paramElem->getChildElement("formula"); - TS_ASSERT(formulaElem); - if(formulaElem) - { - std::string eqString = formulaElem->getAttribute("eq"); - do_test_eq_value (eqString, name, eq1, eq2 ); - TS_ASSERT_EQUALS(formulaElem->getAttribute("result-unit"),resultUnit); - TS_ASSERT_EQUALS(formulaElem->getAttribute("unit"),unit); - } - Poco::XML::Element* fixedElem = paramElem->getChildElement("fixed"); - if(fixed) - { - TS_ASSERT(fixedElem); - } - else - { - TS_ASSERT(!fixedElem); - } - } - } - - //---------------------------------------------------------------------------------------------- - /** Do test on the eq value of given parameter element. - ** eqValue: value to be tested - ** name: name of parameter element to be tested (determines format of eqValue) - ** eq1: expected value of first double in eqValue - ** eq2: expected value of second double in eqValue, if expected - */ - void do_test_eq_value (const std::string& eqValue, const std::string& name, const double eq1, const double eq2) - { - if(name == "IkedaCarpenterPV:SigmaSquared") - { - // eqValue string expected to be something like "0.00043999999999999996*centre^2+0.35499999999999993" - size_t endEq1 = eqValue.find("*centre^2+",1); - if(endEq1 == std::string::npos) TS_FAIL("'*centre^2+' not found in the value of 'eq' for Sigma squared."); - else - { - std::string eq1Value = eqValue.substr(0,endEq1); - std::string eq2Value = eqValue.substr(endEq1+10,std::string::npos); - double eq1v = parse_double(eq1Value); - TS_ASSERT_DELTA( eq1v, eq1, 0.0000001); - double eq2v = parse_double(eq2Value); - TS_ASSERT_DELTA( eq2v, eq2, 0.0000001); - } - } - else if (name == "IkedaCarpenterPV:Gamma") - { - // eqValue string expected to be something like "2.742*centre" - size_t endEq1 = eqValue.find("*centre",1); - if(endEq1 == std::string::npos) TS_FAIL("'*centre' not found in the value of 'eq' for Gamma."); - else - { - std::string eq1Value = eqValue.substr(0,endEq1); - double eq1v = parse_double(eq1Value); - TS_ASSERT_DELTA( eq1v, eq1, 0.0000001) - } - } - else - { - // eqValue string expected to be just a double - double eqv = parse_double(eqValue); - TS_ASSERT_DELTA( eqv, eq1, 0.0000001) - } - } - - //---------------------------------------------------------------------------------------------- - /** Read a double value from a string and test success of this - */ - double parse_double ( const std::string& value ) - { - try - { - return boost::lexical_cast( value ) ; - } - catch(boost::bad_lexical_cast&) - { - std::string errorMessage = "Can't read double from '"+value+"'."; - TS_FAIL(errorMessage); - return 0.0; - } - } - - //---------------------------------------------------------------------------------------------- - /** Generate a 2 bank .irf file - */ - void generate2BankIrfFile(string filename) - { - ofstream ofile; - ofile.open(filename.c_str()); - - if (ofile.is_open()) - { - ofile << " Instrumental resolution function for POWGEN/SNS A Huq 2013-12-03 ireso: 6 \n"; - ofile << "! For use in testing ConvertFullprofToXML (Res=6) \n"; - ofile << "! ---------------------------------------------- Bank 1 CWL = 0.5330A \n"; - ofile << "! Type of profile function: back-to-back exponentials * pseudo-Voigt \n"; - ofile << "! Tof-min(us) step Tof-max(us) \n"; - ofile << "TOFRG 5000.2300 4.0002 51000.0000 \n"; - ofile << "! Zero Dtt1 \n"; - ofile << "ZD2TOF -1.00 22580.59157 \n"; - ofile << "! Zerot Dtt1t Dtt2t x-cross Width \n"; - ofile << "ZD2TOT 933.50214 22275.21084 1.0290 0.0000002 5.0957 \n"; - ofile << "! TOF-TWOTH of the bank \n"; - ofile << "TWOTH 90.00 \n"; - ofile << "! Sig-2 Sig-1 Sig-0 \n"; - ofile << "SIGMA 514.546 0.00044 0.355 \n"; - ofile << "! Gam-2 Gam-1 Gam-0 \n"; - ofile << "GAMMA 0.000 0.000 0.000 \n"; - ofile << "! alph0 beta0 alph1 beta1 \n"; - ofile << "ALFBE 0.000008 6.251096 0.100000 -0.100000 \n"; - ofile << "! alph0t beta0t alph1t beta1t \n"; - ofile << "ALFBT 0.010156 85.918922 0.000000 0.000000 \n"; - ofile << "END \n"; - ofile << "! ---------------------------------------------- Bank 3 \n"; - ofile << "! Type of profile function: back-to-back exponentials * pseudo-Voigt \n"; - ofile << "! Tof-min(us) step Tof-max(us) \n"; - ofile << "TOFRG 9800.0000 5.0000 86000.0000 \n"; - ofile << "! Zero Dtt1 \n"; - ofile << "ZD2TOF 0.00 22586.10156 \n"; - ofile << "! Zerot Dtt1t Dtt2t x-cross Width \n"; - ofile << "ZD2TOT -42.76068 22622.76953 0.30 0.3560 2.4135 \n"; - ofile << "! TOF-TWOTH of the bank \n"; - ofile << "TWOTH 90.000 \n"; - ofile << "! Sig-2 Sig-1 Sig-0 \n"; - ofile << "SIGMA 72.366 10.000 0.000 \n"; - ofile << "! Gam-2 Gam-1 Gam-0 \n"; - ofile << "GAMMA 0.000 2.742 0.000 \n"; - ofile << "! alph0 beta0 alph1 beta1 \n"; - ofile << "ALFBE 0.000008 6.251096 0.100000 -0.100000 \n"; - ofile << "! alph0t beta0t alph1t beta1t \n"; - ofile << "ALFBT 86.059 96.487 13.445 3.435 \n"; - - ofile.close(); - } - else - { - throw runtime_error("Unable to open file to write."); - } - - return; - } - -}; - - -#endif /* MANTID_DATAHANDLING_CONVERTFULLPROFTOXMLTEST_H_ */ diff --git a/Code/Mantid/Framework/DataHandling/test/LoadEventNexusTest.h b/Code/Mantid/Framework/DataHandling/test/LoadEventNexusTest.h index adaa3a006e3d..b9aaf2aecbe7 100644 --- a/Code/Mantid/Framework/DataHandling/test/LoadEventNexusTest.h +++ b/Code/Mantid/Framework/DataHandling/test/LoadEventNexusTest.h @@ -332,6 +332,24 @@ class LoadEventNexusTest : public CxxTest::TestSuite doTestSingleBank(false, false, "bankDoesNotExist", true); } + void test_SingleBank_with_no_events() + { + LoadEventNexus load; + TS_ASSERT_THROWS_NOTHING( load.initialize() ); + TS_ASSERT_THROWS_NOTHING( load.setPropertyValue("Filename", "HYSA_12509.nxs.h5") ); + TS_ASSERT_THROWS_NOTHING( load.setPropertyValue("BankName", "bank10") ); + const std::string outws("AnEmptyWS"); + TS_ASSERT_THROWS_NOTHING( load.setPropertyValue("OutputWorkspace", outws) ); + if ( !load.execute() ) + { + TS_FAIL("LoadEventNexus shouldn't fail to load an empty bank"); + return; + } + + auto ws = AnalysisDataService::Instance().retrieveWS(outws); + TS_ASSERT_EQUALS( ws->getNumberEvents(), 0 ); + } + void test_instrument_inside_nexus_file() { LoadEventNexus load; diff --git a/Code/Mantid/Framework/DataHandling/test/LoadFullprofResolutionTest.h b/Code/Mantid/Framework/DataHandling/test/LoadFullprofResolutionTest.h index 42ce8caf3c6e..0296bd3ec88f 100644 --- a/Code/Mantid/Framework/DataHandling/test/LoadFullprofResolutionTest.h +++ b/Code/Mantid/Framework/DataHandling/test/LoadFullprofResolutionTest.h @@ -6,12 +6,18 @@ #include "MantidDataHandling/LoadFullprofResolution.h" #include "MantidDataObjects/TableWorkspace.h" #include "MantidAPI/TableRow.h" +#include "MantidDataHandling/LoadInstrument.h" +#include "MantidDataObjects/Workspace2D.h" +#include "MantidGeometry/Instrument.h" +#include "MantidGeometry/Instrument/Component.h" +#include "MantidGeometry/Instrument/FitParameter.h" #include #include using Mantid::DataHandling::LoadFullprofResolution; using namespace Mantid; +using namespace Mantid::DataHandling; using namespace Mantid::DataObjects; using namespace Mantid::Kernel; using namespace Mantid::API; @@ -267,6 +273,125 @@ class LoadFullprofResolutionTest : public CxxTest::TestSuite return; } + //---------------------------------------------------------------------------------------------- + /** Test that when the workspace property is used + ** that parameters are correctly loaded into this workspace + * The GEM instrument is used + */ + void test_workspace() + { + // Generate file + string filename("TestWorskpace.irf"); + generate1BankIrfFile(filename); + + // Load workspace wsName + load_GEM(); + + // Set up algorithm to load into the workspace + LoadFullprofResolution alg; + alg.initialize(); + + alg.setProperty("Filename", filename); + alg.setPropertyValue("Banks", "1"); + alg.setProperty("Workspace", wsName); + + // Execute + TS_ASSERT_THROWS_NOTHING(alg.execute()); + TS_ASSERT(alg.isExecuted()); + + // Check parameters in workspace + MatrixWorkspace_sptr ws; + ws = AnalysisDataService::Instance().retrieveWS(wsName); + Mantid::Geometry::ParameterMap& paramMap = ws->instrumentParameters(); + boost::shared_ptr instr = ws->getInstrument(); + + + Mantid::Geometry::Parameter_sptr alpha0Param = paramMap.get(&(*instr), "Alpha0", "fitting"); + TS_ASSERT(alpha0Param); + if(alpha0Param) + { + const Mantid::Geometry::FitParameter& fitParam = alpha0Param->value(); + TS_ASSERT_DELTA( boost::lexical_cast(fitParam.getFormula()), 0.000008, 0.0000001); + } + + Mantid::Geometry::Parameter_sptr beta0Param = paramMap.get(&(*instr), "Beta0", "fitting"); + TS_ASSERT(beta0Param); + if(beta0Param) + { + const Mantid::Geometry::FitParameter& fitParam = beta0Param->value(); + TS_ASSERT_DELTA( boost::lexical_cast(fitParam.getFormula()), 6.251096, 0.0000001); + } + + Mantid::Geometry::Parameter_sptr alpha1Param = paramMap.get(&(*instr), "Alpha1", "fitting"); + TS_ASSERT(alpha1Param); + if(alpha1Param) + { + const Mantid::Geometry::FitParameter& fitParam = alpha1Param->value(); + TS_ASSERT_DELTA( boost::lexical_cast(fitParam.getFormula()), 0.0, 0.0000001); + } + + Mantid::Geometry::Parameter_sptr beta1Param = paramMap.get(&(*instr), "Kappa", "fitting"); + TS_ASSERT(beta1Param); + if(beta0Param) + { + const Mantid::Geometry::FitParameter& fitParam = beta1Param->value(); + TS_ASSERT_DELTA( boost::lexical_cast(fitParam.getFormula()), 0.0, 0.0000001); + } + + + boost::shared_ptr bank = instr->getComponentByName("bank1"); + Mantid::Geometry::Parameter_sptr sigmaSqParam = paramMap.get(&(*bank), "SigmaSquared", "fitting"); + TS_ASSERT(sigmaSqParam); + if(sigmaSqParam) + { + const Mantid::Geometry::FitParameter& fitParam = sigmaSqParam->value(); + double formulaValueCantreAt0 = fitParam.getValue( 0.0 ); // Value for centre=0.0 + TS_ASSERT_DELTA( formulaValueCantreAt0, 0.355, 0.0000001); + double formulaValueCantreAt10 = fitParam.getValue( 10.0 ); // Value for centre=10.0 + TS_ASSERT_DELTA( formulaValueCantreAt10, 0.399, 0.0000001); + } + + Mantid::Geometry::Parameter_sptr gammaParam = paramMap.get(&(*bank), "Gamma", "fitting"); + TS_ASSERT(gammaParam); + if(gammaParam) + { + const Mantid::Geometry::FitParameter& fitParam = gammaParam->value(); + double formulaValueCantreAt0 = fitParam.getValue( 0.0 ); // Value for centre=0.0 + TS_ASSERT_DELTA( formulaValueCantreAt0, 0.0, 0.0000001); + double formulaValueCantreAt10 = fitParam.getValue( 10.0 ); // Value for centre=10.0 + TS_ASSERT_DELTA( formulaValueCantreAt10, 0.0, 0.0000001); + } + + // Clean + Poco::File("TestWorskpace.irf").remove(); + } + + //---------------------------------------------------------------------------------------------- + /** Test that algorithm does not run, + * if neither the OutputTableWorkspace nor Workspace + ** property is set. + */ + void test_no_output() + { + // Generate file + string filename("TestNoOutput.irf"); + generate1BankIrfFile(filename); + + // Set up algorithm without specifying OutputTableWorkspace or Workspace + LoadFullprofResolution alg; + alg.initialize(); + + alg.setProperty("Filename", filename); + alg.setPropertyValue("Banks", "1"); + + // Execute and check that execution failed + alg.execute(); + TS_ASSERT(!alg.isExecuted()); + + // Clean + Poco::File("TestNoOutput.irf").remove(); + } + //---------------------------------------------------------------------------------------------- /** Test that the number from the NPROF is read correctly ** and has correct name in table. @@ -380,6 +505,33 @@ class LoadFullprofResolutionTest : public CxxTest::TestSuite return; } + + //---------------------------------------------------------------------------------------------- + /** Generate a GEM workspace + */ + void load_GEM() + { + LoadInstrument loaderGEM; + + TS_ASSERT_THROWS_NOTHING(loaderGEM.initialize()); + + //create a workspace with some sample data + wsName = "LoadFullprofResolutionWorkspace"; + Workspace_sptr ws = WorkspaceFactory::Instance().create("Workspace2D",1,1,1); + Workspace2D_sptr ws2D = boost::dynamic_pointer_cast(ws); + + //put this workspace in the data service + TS_ASSERT_THROWS_NOTHING(AnalysisDataService::Instance().add(wsName, ws2D)); + + // Path to test input file + loaderGEM.setPropertyValue("Filename", "GEM_Definition.xml"); + //inputFile = loaderIDF2.getPropertyValue("Filename"); + loaderGEM.setPropertyValue("Workspace", wsName); + TS_ASSERT_THROWS_NOTHING(loaderGEM.execute()); + TS_ASSERT( loaderGEM.isExecuted() ); + + } + //---------------------------------------------------------------------------------------------- /** Generate a 1 bank .irf file */ @@ -573,6 +725,9 @@ class LoadFullprofResolutionTest : public CxxTest::TestSuite return 29; // Change this value if you add or remove any rows from the OutputTableWorkspace } + private: + std::string wsName; // For Workspace property + }; diff --git a/Code/Mantid/Framework/DataHandling/test/LoadILLTest.h b/Code/Mantid/Framework/DataHandling/test/LoadILLTest.h index 5c1ae4558c38..17de7adb578b 100644 --- a/Code/Mantid/Framework/DataHandling/test/LoadILLTest.h +++ b/Code/Mantid/Framework/DataHandling/test/LoadILLTest.h @@ -17,8 +17,7 @@ class LoadILLTest: public CxxTest::TestSuite static void destroySuite( LoadILLTest *suite ) { delete suite; } LoadILLTest() : - m_testFile("ILLIN5_104007.nxs") - { + m_dataFile("ILLIN5_104007.nxs") { } void testName() { @@ -37,35 +36,32 @@ class LoadILLTest: public CxxTest::TestSuite TS_ASSERT( loader.isInitialized()); } -// void testFileCheck() { -// std::cerr << loader.fileCheck(testFile); -// } - - void testExec() { + /* + * This test only loads the Sample Data + * The elastic peak is obtained on the fly from the sample data. + */ + void testExecJustSample() { LoadILL loader; loader.initialize(); - loader.setPropertyValue("Filename", m_testFile); + loader.setPropertyValue("Filename", m_dataFile); std::string outputSpace = "LoadILLTest_out"; loader.setPropertyValue("OutputWorkspace", outputSpace); TS_ASSERT_THROWS_NOTHING( loader.execute()); - // test workspace, copied from LoadMuonNexusTest.h - MatrixWorkspace_sptr output; - - (output = AnalysisDataService::Instance().retrieveWS( - outputSpace)); - MatrixWorkspace_sptr output2D = boost::dynamic_pointer_cast< - MatrixWorkspace>(output); + MatrixWorkspace_sptr output = AnalysisDataService::Instance().retrieveWS(outputSpace); + MatrixWorkspace_sptr output2D = boost::dynamic_pointer_cast(output); TS_ASSERT_EQUALS( output2D->getNumberHistograms(), 98304); AnalysisDataService::Instance().clear(); } + + private: + std::string m_dataFile; - std::string m_testFile; }; //------------------------------------------------------------------------------ @@ -74,13 +70,21 @@ class LoadILLTest: public CxxTest::TestSuite class LoadILLTestPerformance: public CxxTest::TestSuite { public: + LoadILLTestPerformance() : + m_dataFile("ILLIN5_104007.nxs"){ + } + void testDefaultLoad() { Mantid::DataHandling::LoadILL loader; loader.initialize(); - loader.setPropertyValue("Filename", "ILLIN5_104007.nxs"); + loader.setPropertyValue("Filename", m_dataFile); loader.setPropertyValue("OutputWorkspace", "ws"); - TS_ASSERT( loader.execute()); + TS_ASSERT(loader.execute()); } + +private: + std::string m_dataFile; + }; #endif /*LoadILLTEST_H_*/ diff --git a/Code/Mantid/Framework/DataHandling/test/LoadMuonNexus1Test.h b/Code/Mantid/Framework/DataHandling/test/LoadMuonNexus1Test.h index 7bee9c72985a..931a036e2344 100644 --- a/Code/Mantid/Framework/DataHandling/test/LoadMuonNexus1Test.h +++ b/Code/Mantid/Framework/DataHandling/test/LoadMuonNexus1Test.h @@ -492,7 +492,134 @@ class LoadMuonNexus1Test : public CxxTest::TestSuite AnalysisDataService::Instance().deepRemoveGroup(outWSName); AnalysisDataService::Instance().deepRemoveGroup(deadTimesWSName); } + + void test_loadingDetectorGrouping_singlePeriod() + { + const std::string outWSName = "LoadMuonNexus1Test_OutputWS"; + const std::string detectorGroupingWSName = "LoadMuonNexus1Test_DetectorGrouping"; + + LoadMuonNexus1 alg; + + TS_ASSERT_THROWS_NOTHING( alg.initialize() ); + TS_ASSERT( alg.isInitialized() ); + + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", "emu00006473.nxs") ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWSName) ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("DetectorGroupingTable", detectorGroupingWSName) ); + + TS_ASSERT_THROWS_NOTHING( alg.execute() ); + TS_ASSERT( alg.isExecuted() ); + + TableWorkspace_sptr detectorGrouping; + + TS_ASSERT_THROWS_NOTHING( detectorGrouping = + AnalysisDataService::Instance().retrieveWS( detectorGroupingWSName ) ); + + TS_ASSERT( detectorGrouping ); + + if ( detectorGrouping ) + { + TS_ASSERT_EQUALS( detectorGrouping->columnCount(), 1 ); + TS_ASSERT_EQUALS( detectorGrouping->rowCount(), 2 ); + + TS_ASSERT_EQUALS( detectorGrouping->getColumn(0)->type(), "vector_int" ); + TS_ASSERT_EQUALS( detectorGrouping->getColumn(0)->name(), "Detectors" ); + + std::vector e1, e2; + TS_ASSERT_THROWS_NOTHING( e1 = detectorGrouping->cell< std::vector >(0,0) ); + TS_ASSERT_THROWS_NOTHING( e2 = detectorGrouping->cell< std::vector >(1,0) ); + + TS_ASSERT_EQUALS( e1.size(), 16); + TS_ASSERT_EQUALS( e2.size(), 16); + + TS_ASSERT_EQUALS( e1[0], 1 ); + TS_ASSERT_EQUALS( e1[15], 16); + + TS_ASSERT_EQUALS( e2[0], 17 ); + TS_ASSERT_EQUALS( e2[15], 32 ); + } + + AnalysisDataService::Instance().remove(outWSName); + AnalysisDataService::Instance().remove(detectorGroupingWSName); + } + void test_loadingDetectorGrouping_multiPeriod() + { + const std::string outWSName = "LoadMuonNexus1Test_OutputWS"; + const std::string detectorGroupingWSName = "LoadMuonNexus1Test_DetectorGrouping"; + + LoadMuonNexus1 alg; + + TS_ASSERT_THROWS_NOTHING( alg.initialize() ); + TS_ASSERT( alg.isInitialized() ); + + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", "MUSR00015189.nxs") ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", outWSName) ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("DetectorGroupingTable", detectorGroupingWSName) ); + + TS_ASSERT_THROWS_NOTHING( alg.execute() ); + TS_ASSERT( alg.isExecuted() ); + + WorkspaceGroup_sptr detectorGrouping; + + TS_ASSERT_THROWS_NOTHING( detectorGrouping = + AnalysisDataService::Instance().retrieveWS( detectorGroupingWSName ) ); + + TS_ASSERT( detectorGrouping ); + + if ( detectorGrouping ) + { + TS_ASSERT_EQUALS( detectorGrouping->size(), 2 ); + + TableWorkspace_sptr table1 = boost::dynamic_pointer_cast( detectorGrouping->getItem(0) ); + TS_ASSERT( table1 ); + + if ( table1 ) + { + TS_ASSERT_EQUALS( table1->columnCount(), 1 ); + TS_ASSERT_EQUALS( table1->rowCount(), 2 ); + + std::vector e1, e2; + TS_ASSERT_THROWS_NOTHING( e1 = table1->cell< std::vector >(0,0) ); + TS_ASSERT_THROWS_NOTHING( e2 = table1->cell< std::vector >(1,0) ); + + TS_ASSERT_EQUALS( e1.size(), 32); + TS_ASSERT_EQUALS( e2.size(), 32); + + TS_ASSERT_EQUALS( e1[0], 33 ); + TS_ASSERT_EQUALS( e1[31], 64 ); + + TS_ASSERT_EQUALS( e2[0], 1 ); + TS_ASSERT_EQUALS( e2[31], 32 ); + } + + TableWorkspace_sptr table2 = boost::dynamic_pointer_cast( detectorGrouping->getItem(1) ); + TS_ASSERT( table2 ); + + if ( table2 ) + { + TS_ASSERT_EQUALS( table2->columnCount(), 1 ); + TS_ASSERT_EQUALS( table2->rowCount(), 2 ); + + std::vector e1, e2; + TS_ASSERT_THROWS_NOTHING( e1 = table2->cell< std::vector >(0,0) ); + TS_ASSERT_THROWS_NOTHING( e2 = table2->cell< std::vector >(1,0) ); + + TS_ASSERT_EQUALS( e1.size(), 32); + TS_ASSERT_EQUALS( e2.size(), 32); + + TS_ASSERT_EQUALS( e1[0], 33 ); + TS_ASSERT_EQUALS( e1[31], 64 ); + + TS_ASSERT_EQUALS( e2[0], 1 ); + TS_ASSERT_EQUALS( e2[31], 32); + + } + } + + AnalysisDataService::Instance().deepRemoveGroup(outWSName); + AnalysisDataService::Instance().deepRemoveGroup(detectorGroupingWSName); + } private: LoadMuonNexus1 nxLoad,nxload2,nxload3; std::string outputSpace; diff --git a/Code/Mantid/Framework/DataHandling/test/LoadPDCharacterizationsTest.h b/Code/Mantid/Framework/DataHandling/test/LoadPDCharacterizationsTest.h new file mode 100644 index 000000000000..169df3a3350e --- /dev/null +++ b/Code/Mantid/Framework/DataHandling/test/LoadPDCharacterizationsTest.h @@ -0,0 +1,236 @@ +#ifndef MANTID_DATAHANDLING_LOADPDCHARACTERIZATIONSTEST_H_ +#define MANTID_DATAHANDLING_LOADPDCHARACTERIZATIONSTEST_H_ + +#include +#include + +#include "MantidDataHandling/LoadPDCharacterizations.h" +using Mantid::API::ITableWorkspace; +using Mantid::API::ITableWorkspace_sptr; + +using Mantid::DataHandling::LoadPDCharacterizations; +class LoadPDCharacterizationsTest : public CxxTest::TestSuite +{ +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static LoadPDCharacterizationsTest *createSuite() { return new LoadPDCharacterizationsTest(); } + static void destroySuite( LoadPDCharacterizationsTest *suite ) { delete suite; } + + void runAlg(LoadPDCharacterizations &alg, ITableWorkspace_sptr &wksp, const std::string &filename) + { + TS_ASSERT_THROWS_NOTHING( alg.initialize() ); + TS_ASSERT( alg.isInitialized() ); + + // run the algorithm + alg.setProperty("Filename", filename); + alg.setPropertyValue("OutputWorkspace", filename); + TS_ASSERT(alg.execute()); + TS_ASSERT(alg.isExecuted()); + + // test the table workspace + wksp = boost::dynamic_pointer_cast + (Mantid::API::AnalysisDataService::Instance().retrieve(filename)); + TS_ASSERT(wksp); + } + + // checks the focus positions for NOMAD + void checkNOMAD(LoadPDCharacterizations &alg) + { + TS_ASSERT_EQUALS(alg.getPropertyValue("IParmFilename"), std::string("NOMAD_11_22_11.prm")); + double l1 = alg.getProperty("PrimaryFlightPath"); + TS_ASSERT_EQUALS(l1, 19.5); + + const int NUM_SPEC = 6; + + std::vector specIds = alg.getProperty("SpectrumIDs"); + TS_ASSERT_EQUALS(specIds.size(), NUM_SPEC); + if (!specIds.empty()) + { + for (int i = 0; i < NUM_SPEC; ++i) + TS_ASSERT_EQUALS(specIds[i], i+1); + } + + std::vector l2 = alg.getProperty("L2"); + TS_ASSERT_EQUALS(l2.size(), NUM_SPEC); + if (!l2.empty()) + { + for (int i = 0; i < NUM_SPEC; ++i) + TS_ASSERT_EQUALS(l2[i], 2.); + } + + std::vector polar = alg.getProperty("Polar"); + TS_ASSERT_EQUALS(polar.size(), NUM_SPEC); + if (!polar.empty()) + { + TS_ASSERT_EQUALS(polar[0], 15.); + TS_ASSERT_EQUALS(polar[1], 31.); + TS_ASSERT_EQUALS(polar[2], 67.); + TS_ASSERT_EQUALS(polar[3], 122.); + TS_ASSERT_EQUALS(polar[4], 154.); + TS_ASSERT_EQUALS(polar[5], 7.); + } + + std::vector azi = alg.getProperty("Azimuthal"); + TS_ASSERT_EQUALS(azi.size(), NUM_SPEC); + if (!azi.empty()) + { + for (int i = 0; i < NUM_SPEC; ++i) + TS_ASSERT_EQUALS(azi[0], 0.); + } + } + + void checkPG3(ITableWorkspace_sptr &wksp) + { + TS_ASSERT_EQUALS(wksp->columnCount(), 10); + TS_ASSERT_EQUALS(wksp->rowCount(), 6); + + // check all of the contents of row 0 + TS_ASSERT_EQUALS(wksp->Double(0,0), 60.); + TS_ASSERT_EQUALS(wksp->Double(0,1), 0.900); + TS_ASSERT_EQUALS(wksp->Int(0,2), 1); + TS_ASSERT_EQUALS(wksp->Int(0,3), 15030); + TS_ASSERT_EQUALS(wksp->Int(0,4), 15039); + TS_ASSERT_EQUALS(wksp->Int(0,5), 0); + TS_ASSERT_EQUALS(wksp->String(0,6), "0.20"); + TS_ASSERT_EQUALS(wksp->String(0,7), "4.12"); + TS_ASSERT_EQUALS(wksp->Double(0,8), 4700.); + TS_ASSERT_EQUALS(wksp->Double(0,9), 21200.); + + // check all of the contents of row 5 + TS_ASSERT_EQUALS(wksp->Double(5,0), 10.); + TS_ASSERT_EQUALS(wksp->Double(5,1), 3.198); + TS_ASSERT_EQUALS(wksp->Int(5,2), 1); + TS_ASSERT_EQUALS(wksp->Int(5,3), 15033); + TS_ASSERT_EQUALS(wksp->Int(5,4), 15042); + TS_ASSERT_EQUALS(wksp->Int(5,5), 0); + TS_ASSERT_EQUALS(wksp->String(5,6), "0.05"); + TS_ASSERT_EQUALS(wksp->String(5,7), "15.40"); + TS_ASSERT_EQUALS(wksp->Double(5,8), 0.); + TS_ASSERT_EQUALS(wksp->Double(5,9), 100000.); + } + + void test_Init() + { + LoadPDCharacterizations alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + } + + void test_FocusAndChar() + { + const std::string CHAR_FILE("Test_characterizations_focus_and_char.txt"); + ITableWorkspace_sptr wksp; + + // initialize and run the algorithm + LoadPDCharacterizations alg; + runAlg(alg, wksp, CHAR_FILE); + + // test the table workspace + checkPG3(wksp); + + // test the other output properties + TS_ASSERT_EQUALS(alg.getPropertyValue("IParmFilename"), std::string("dummy.iparm")); + double l1 = alg.getProperty("PrimaryFlightPath"); + TS_ASSERT_EQUALS(l1, 60.); + + std::vector specIds = alg.getProperty("SpectrumIDs"); + TS_ASSERT_EQUALS(specIds.size(), 1); + if (!specIds.empty()) + TS_ASSERT_EQUALS(specIds[0], 1); + + std::vector l2 = alg.getProperty("L2"); + TS_ASSERT_EQUALS(l2.size(), 1); + if (!l2.empty()) + TS_ASSERT_EQUALS(l2[0], 3.18); + + std::vector polar = alg.getProperty("Polar"); + TS_ASSERT_EQUALS(polar.size(), 1); + if (!polar.empty()) + TS_ASSERT_EQUALS(polar[0], 90.); + + std::vector azi = alg.getProperty("Azimuthal"); + TS_ASSERT_EQUALS(azi.size(), 1); + if (!azi.empty()) + TS_ASSERT_EQUALS(azi[0], 0.); + } + + void test_FocusAndChar2() + { + const std::string CHAR_FILE("Test_characterizations_focus_and_char2.txt"); + ITableWorkspace_sptr wksp; + + // initialize and run the algorithm + LoadPDCharacterizations alg; + runAlg(alg, wksp, CHAR_FILE); + + // test the table workspace + TS_ASSERT_EQUALS(wksp->columnCount(), 10); + TS_ASSERT_EQUALS(wksp->rowCount(), 1); + + // check all of the contents of row 0 + TS_ASSERT_EQUALS(wksp->Double(0,0), 60.); + TS_ASSERT_EQUALS(wksp->Double(0,1), 1.4); + TS_ASSERT_EQUALS(wksp->Int(0,2), 1); + TS_ASSERT_EQUALS(wksp->Int(0,3), 0); + TS_ASSERT_EQUALS(wksp->Int(0,4), 0); + TS_ASSERT_EQUALS(wksp->Int(0,5), 0); + TS_ASSERT_EQUALS(wksp->String(0,6), ".31,.25,.13,.13,.13,.42"); + TS_ASSERT_EQUALS(wksp->String(0,7), "13.66,5.83,3.93,2.09,1.57,31.42"); + TS_ASSERT_EQUALS(wksp->Double(0,8), 300.00); + TS_ASSERT_EQUALS(wksp->Double(0,9), 16666.67); + + // test the other output properties + checkNOMAD(alg); + } + + void test_Focus() + { + const std::string CHAR_FILE("Test_characterizations_focus.txt"); + ITableWorkspace_sptr wksp; + + // initialize and run the algorithm + LoadPDCharacterizations alg; + runAlg(alg, wksp, CHAR_FILE); + + // test the table workspace + TS_ASSERT_EQUALS(wksp->columnCount(), 10); + TS_ASSERT_EQUALS(wksp->rowCount(), 0); + + // test the other output properties + checkNOMAD(alg); + } + + void test_Char() + { + const std::string CHAR_FILE("Test_characterizations_char.txt"); + ITableWorkspace_sptr wksp; + + // initialize and run the algorithm + LoadPDCharacterizations alg; + runAlg(alg, wksp, CHAR_FILE); + + // test the table workspace + checkPG3(wksp); + + // test the other output properties + TS_ASSERT_EQUALS(alg.getPropertyValue("IParmFilename"), std::string("")); + double l1 = alg.getProperty("PrimaryFlightPath"); + TS_ASSERT_EQUALS(l1, 0.); + + std::vector specIds = alg.getProperty("SpectrumIDs"); + TS_ASSERT_EQUALS(specIds.size(), 0); + + std::vector l2 = alg.getProperty("L2"); + TS_ASSERT_EQUALS(l2.size(), 0); + + std::vector polar = alg.getProperty("Polar"); + TS_ASSERT_EQUALS(polar.size(), 0); + + std::vector azi = alg.getProperty("Azimuthal"); + TS_ASSERT_EQUALS(azi.size(), 0); + } +}; + + +#endif /* MANTID_DATAHANDLING_LOADPDCHARACTERIZATIONSTEST_H_ */ diff --git a/Code/Mantid/Framework/DataHandling/test/LoadParameterFileTest.h b/Code/Mantid/Framework/DataHandling/test/LoadParameterFileTest.h index 5c16406ed974..e6efa9b134b0 100644 --- a/Code/Mantid/Framework/DataHandling/test/LoadParameterFileTest.h +++ b/Code/Mantid/Framework/DataHandling/test/LoadParameterFileTest.h @@ -30,24 +30,9 @@ class LoadParameterFileTest : public CxxTest::TestSuite void testExecIDF_for_unit_testing2() // IDF stands for Instrument Definition File { - LoadInstrument loaderIDF2; - - TS_ASSERT_THROWS_NOTHING(loaderIDF2.initialize()); - - //create a workspace with some sample data - wsName = "LoadParameterFileTestIDF2"; - Workspace_sptr ws = WorkspaceFactory::Instance().create("Workspace2D",1,1,1); - Workspace2D_sptr ws2D = boost::dynamic_pointer_cast(ws); - - //put this workspace in the data service - TS_ASSERT_THROWS_NOTHING(AnalysisDataService::Instance().add(wsName, ws2D)); - // Path to test input file assumes Test directory checked out from SVN - loaderIDF2.setPropertyValue("Filename", "IDFs_for_UNIT_TESTING/IDF_for_UNIT_TESTING2.xml"); - //inputFile = loaderIDF2.getPropertyValue("Filename"); - loaderIDF2.setPropertyValue("Workspace", wsName); - TS_ASSERT_THROWS_NOTHING(loaderIDF2.execute()); - TS_ASSERT( loaderIDF2.isExecuted() ); + // Create workspace wsName + load_IDF2(); // load in additional parameters LoadParameterFile loaderPF; @@ -91,6 +76,101 @@ class LoadParameterFileTest : public CxxTest::TestSuite } + void testExec_withIDFString() // Test use of string instead of file + { + + // Create workspace + load_IDF2(); + + // Define parameter XML string + std::string parameterXML = + "" + "" + " " + " " + " " + " " + " " + " " + ""; + + // load in additional parameters + LoadParameterFile loaderPF; + TS_ASSERT_THROWS_NOTHING(loaderPF.initialize()); + loaderPF.setPropertyValue("ParameterXML", parameterXML); + loaderPF.setPropertyValue("Workspace", wsName); + TS_ASSERT_THROWS_NOTHING(loaderPF.execute()); + TS_ASSERT( loaderPF.isExecuted() ); + + // Get back the saved workspace + MatrixWorkspace_sptr output; + TS_ASSERT_THROWS_NOTHING(output = AnalysisDataService::Instance().retrieveWS(wsName)); + + ParameterMap& paramMap = output->instrumentParameters(); + boost::shared_ptr i = output->getInstrument(); + boost::shared_ptr ptrDet = i->getDetector(1008); + TS_ASSERT_EQUALS( ptrDet->getID(), 1008); + TS_ASSERT_EQUALS( ptrDet->getName(), "combined translation6"); + Parameter_sptr param = paramMap.get(&(*ptrDet), "fjols"); + TS_ASSERT_DELTA( param->value(), 20.0, 0.0001); + param = paramMap.get(&(*ptrDet), "nedtur"); + TS_ASSERT_DELTA( param->value(), 77.0, 0.0001); + param = paramMap.get(&(*ptrDet), "fjols-test-paramfile"); + TS_ASSERT_DELTA( param->value(), 52.0, 0.0001); + + std::vector dummy = paramMap.getDouble("nickel-holder", "klovn"); + TS_ASSERT_DELTA( dummy[0], 1.0, 0.0001); + dummy = paramMap.getDouble("nickel-holder", "pos"); + TS_ASSERT_EQUALS (dummy.size(), 0); + dummy = paramMap.getDouble("nickel-holder", "rot"); + TS_ASSERT_EQUALS (dummy.size(), 0); + dummy = paramMap.getDouble("nickel-holder", "taabe"); + TS_ASSERT_DELTA (dummy[0], 200.0, 0.0001); + dummy = paramMap.getDouble("nickel-holder", "mistake"); + TS_ASSERT_EQUALS (dummy.size(), 0); + dummy = paramMap.getDouble("nickel-holder", "fjols-test-paramfile"); + TS_ASSERT_DELTA (dummy[0], 2010.0, 0.0001); + + AnalysisDataService::Instance().remove(wsName); + + } + + void test_failure_if_no_file_or_string() + { + // Create workspace + load_IDF2(); + + // Run algorithm without file or string properties set + LoadParameterFile loaderPF; + TS_ASSERT_THROWS_NOTHING(loaderPF.initialize()); + loaderPF.setPropertyValue("Workspace", wsName); + TS_ASSERT_THROWS_NOTHING(loaderPF.execute()); + TS_ASSERT( ! loaderPF.execute() ) + } + + void load_IDF2() + { + LoadInstrument loaderIDF2; + + TS_ASSERT_THROWS_NOTHING(loaderIDF2.initialize()); + + //create a workspace with some sample data + wsName = "LoadParameterFileTestIDF2"; + Workspace_sptr ws = WorkspaceFactory::Instance().create("Workspace2D",1,1,1); + Workspace2D_sptr ws2D = boost::dynamic_pointer_cast(ws); + + //put this workspace in the data service + TS_ASSERT_THROWS_NOTHING(AnalysisDataService::Instance().add(wsName, ws2D)); + + // Path to test input file assumes Test directory checked out from git + loaderIDF2.setPropertyValue("Filename", "IDFs_for_UNIT_TESTING/IDF_for_UNIT_TESTING2.xml"); + //inputFile = loaderIDF2.getPropertyValue("Filename"); + loaderIDF2.setPropertyValue("Workspace", wsName); + TS_ASSERT_THROWS_NOTHING(loaderIDF2.execute()); + TS_ASSERT( loaderIDF2.isExecuted() ); + + } + private: std::string inputFile; diff --git a/Code/Mantid/Framework/DataObjects/CMakeLists.txt b/Code/Mantid/Framework/DataObjects/CMakeLists.txt index 0b3939ea3c68..eab9aa0810c4 100644 --- a/Code/Mantid/Framework/DataObjects/CMakeLists.txt +++ b/Code/Mantid/Framework/DataObjects/CMakeLists.txt @@ -22,6 +22,7 @@ set ( SRC_FILES src/SplittersWorkspace.cpp src/TableColumn.cpp src/TableWorkspace.cpp + src/VectorColumn.cpp src/Workspace2D.cpp src/WorkspaceSingleValue.cpp ) @@ -57,6 +58,7 @@ set ( INC_FILES inc/MantidDataObjects/SplittersWorkspace.h inc/MantidDataObjects/TableColumn.h inc/MantidDataObjects/TableWorkspace.h + inc/MantidDataObjects/VectorColumn.h inc/MantidDataObjects/Workspace2D.h inc/MantidDataObjects/WorkspaceSingleValue.h ) @@ -64,9 +66,9 @@ set ( INC_FILES set ( TEST_FILES CompressedWorkspace2DTest.h EventListTest.h - EventsTest.h EventWorkspaceMRUTest.h EventWorkspaceTest.h + EventsTest.h GroupingWorkspaceTest.h Histogram1DTest.h LibraryManagerTest.h @@ -86,6 +88,7 @@ set ( TEST_FILES TableWorkspacePropertyTest.h TableWorkspaceTest.h TofEventTest.h + VectorColumnTest.h WeightedEventNoTimeTest.h WeightedEventTest.h Workspace2DTest.h diff --git a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/EventList.h b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/EventList.h index ecb52326a9ca..567dec745313 100644 --- a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/EventList.h +++ b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/EventList.h @@ -14,7 +14,7 @@ #include "MantidKernel/System.h" #include "MantidKernel/TimeSplitter.h" #include -#include +#include #include #include #include "MantidKernel/MultiThreaded.h" @@ -281,6 +281,9 @@ class DLLExport EventList : public Mantid::API::IEventList void splitByFullTime(Kernel::TimeSplitterType & splitter, std::map outputs, double tofcorrection, bool docorrection) const; + /// Split events by pulse time + void splitByPulseTime(Kernel::TimeSplitterType & splitter, std::map outputs) const; + void multiply(const double value, const double error = 0.0); EventList& operator*=(const double value); @@ -375,6 +378,10 @@ class DLLExport EventList : public Mantid::API::IEventList template< class T > void splitByFullTimeHelper(Kernel::TimeSplitterType & splitter, std::map outputs, typename std::vector & events, double tofcorrection, bool docorrection) const; + /// Split events by pulse time + template< class T > + void splitByPulseTimeHelper(Kernel::TimeSplitterType & splitter, std::map outputs, + typename std::vector & events) const; template< class T> static void multiplyHelper(std::vector & events, const double value, const double error = 0.0); template diff --git a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/Events.h b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/Events.h index 3705b3c40172..b2a22256e656 100644 --- a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/Events.h +++ b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/Events.h @@ -5,7 +5,7 @@ #include #endif #include -#include +#include #include #include "MantidAPI/MatrixWorkspace.h" // get MantidVec declaration #include "MantidKernel/cow_ptr.h" diff --git a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/VectorColumn.h b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/VectorColumn.h new file mode 100644 index 000000000000..47f4de4b7c6e --- /dev/null +++ b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/VectorColumn.h @@ -0,0 +1,216 @@ +#ifndef MANTID_DATAOBJECTS_VECTORCOLUMN_H_ +#define MANTID_DATAOBJECTS_VECTORCOLUMN_H_ + +#include "MantidAPI/Column.h" +#include "MantidKernel/System.h" + +#include +#include +#include +#include + +namespace Mantid +{ +namespace DataObjects +{ + + using namespace Kernel; + using namespace API; + + /** VectorColumn : table column type capable of storing vectors of primitive types. + + Plese add more specializations to VectorColumn.cpp as you need them. I don't guarantee + it will work correctly with complex or user types, but it might. + + Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: + Code Documentation is available at: + */ + + template + class DLLExport VectorColumn : public Column + { + public: + VectorColumn() + { + m_type = typeName(); + } + + virtual ~VectorColumn() + {} + + /// Number of individual elements in the column + virtual size_t size() const + { + return m_data.size(); + } + + /// Returns typeid for the data in the column + virtual const std::type_info& get_type_info() const + { + return typeid( std::vector ); + } + + /// Returns typeid for the pointer type to the data element in the column + virtual const std::type_info& get_pointer_type_info() const + { + return typeid( std::vector* ); + } + + /// Print specified item to the stream + virtual void print(size_t index, std::ostream& s) const + { + const std::vector& values = m_data.at(index); + + auto it = values.begin(); + + if ( it != values.end() ) + { + s << *it; + ++it; + } + + for ( ; it != values.end(); ++it ) + { + s << ','; + s << *it; + } + } + + /// Set item from a string value + virtual void read(size_t index, const std::string & text) + { + std::vector newValues; + + boost::char_separator delim(","); + boost::tokenizer< boost::char_separator > elements(text, delim); + + for ( auto it = elements.begin(); it != elements.end(); it++ ) + { + std::string element(*it); + + boost::trim(element); + + try + { + newValues.push_back( boost::lexical_cast(element) ); + } + catch(boost::bad_lexical_cast&) + { + throw std::invalid_argument("Unable to convert one of the elements: " + element); + } + } + + m_data.at(index) = newValues; + } + + /// Specialized type check + virtual bool isBool() const + { + return false; + } + + /// Overall memory size taken by the column (bytes) + virtual long int sizeOfData() const + { + long int size(0); + + for ( auto elemIt = m_data.begin(); elemIt != m_data.end(); ++elemIt ) + { + size += static_cast( elemIt->size() * sizeof(Type) ); + } + + return size; + } + + /// Create another copy of the column + virtual VectorColumn* clone() const + { + VectorColumn* newColumn = new VectorColumn(); + newColumn->m_data = m_data; + newColumn->setName(m_name); + newColumn->setPlotType(m_plotType); + return newColumn; + } + + /// Cast to double + virtual double toDouble(size_t i) const + { + UNUSED_ARG(i); + throw std::runtime_error("VectorColumn is not convertible to double."); + } + + /// Assign from double + virtual void fromDouble(size_t i, double value) + { + UNUSED_ARG(i); + UNUSED_ARG(value); + throw std::runtime_error("VectorColumn is not assignable from double."); + } + + protected: + /// Sets the new column size. + virtual void resize(size_t count) + { + m_data.resize(count); + } + + /// Inserts an item. + virtual void insert(size_t index) + { + // Insert empty vector at the given position + m_data.insert( m_data.begin() + index, std::vector() ); + } + + /// Removes an item. + virtual void remove(size_t index) + { + m_data.erase(m_data.begin() + index); + } + + /// Pointer to a data element + virtual void* void_pointer(size_t index) + { + return &m_data.at(index); + } + + /// Pointer to a data element + virtual const void* void_pointer(size_t index) const + { + return &m_data.at(index); + } + + private: + /// All the vectors stored + std::vector< std::vector > m_data; + + /// Returns the name of the column with the given type. Is specialized using DECLARE_VECTORCOLUMN + std::string typeName(); + }; + + +} // namespace DataObjects +} // namespace Mantid + +#define DECLARE_VECTORCOLUMN(Type,TypeName) \ + template<> std::string VectorColumn::typeName() { return #TypeName; }; \ + Kernel::RegistrationHelper register_column_##TypeName( \ + ( API::ColumnFactory::Instance().subscribe< VectorColumn >(#TypeName), 0) ); \ + +#endif /* MANTID_DATAOBJECTS_VECTORCOLUMN_H_ */ diff --git a/Code/Mantid/Framework/DataObjects/src/EventList.cpp b/Code/Mantid/Framework/DataObjects/src/EventList.cpp index 4ee29fa8a00c..f4b89d6680f4 100644 --- a/Code/Mantid/Framework/DataObjects/src/EventList.cpp +++ b/Code/Mantid/Framework/DataObjects/src/EventList.cpp @@ -7,6 +7,7 @@ #include "MantidKernel/MultiThreaded.h" #include #include +#include #include #include #include @@ -3757,6 +3758,7 @@ namespace DataObjects * be big enough to accommodate the indices. * @param events :: either this->events or this->weightedEvents. * @param tofcorrection :: a correction for each TOF to multiply with. + * @param docorrection :: flag to determine whether or not to apply correction */ template< class T > void EventList::splitByFullTimeHelper(Kernel::TimeSplitterType & splitter, std::map outputs, @@ -3895,6 +3897,133 @@ namespace DataObjects return; } + + //------------------------------------------- -------------------------------------------------- + /** Split the event list into n outputs by each event's pulse time only + */ + template< class T > + void EventList::splitByPulseTimeHelper(Kernel::TimeSplitterType & splitter, std::map outputs, + typename std::vector & events) const + { + // Prepare to TimeSplitter Iterate through the splitter at the same time + Kernel::TimeSplitterType::iterator itspl = splitter.begin(); + Kernel::TimeSplitterType::iterator itspl_end = splitter.end(); + Kernel::DateAndTime start, stop; + + // Prepare to Events Iterate through all events (sorted by tof) + typename std::vector::iterator itev = events.begin(); + typename std::vector::iterator itev_end = events.end(); + + // Iterate (loop) on all splitters + while (itspl != itspl_end) + { + // Get the splitting interval times and destination group + start = itspl->start().totalNanoseconds(); + stop = itspl->stop().totalNanoseconds(); + const int index = itspl->index(); + + // Skip the events before the start of the time and put to 'unfiltered' EventList + EventList* myOutput = outputs[-1]; + while (itev != itev_end) + { + if (itev->m_pulsetime < start) + { + // Record to index = -1 space + const T eventCopy(*itev); + myOutput->addEventQuickly(eventCopy); + ++ itev; + } + else + { + // Event within a splitter interval + break; + } + } + + // Go through all the events that are in the interval (if any) + while (itev != itev_end) + { + + if (itev->m_pulsetime < stop) + { + // Duplicate event + const T eventCopy(*itev); + EventList * myOutput = outputs[index]; + //Add the copy to the output + myOutput->addEventQuickly(eventCopy); + ++itev; + } + else + { + // Out of interval + break; + } + } + + //Go to the next interval + ++itspl; + //But if we reached the end, then we are done. + if (itspl==itspl_end) + break; + + //No need to keep looping through the filter if we are out of events + if (itev == itev_end) + break; + } // END-WHILE Splitter + + return; + } + + //---------------------------------------------------------------------------------------------- + /** Split the event list by pulse time + */ + void EventList::splitByPulseTime(Kernel::TimeSplitterType & splitter, std::map outputs) const + { + // Check for supported event type + if (eventType == WEIGHTED_NOTIME) + throw std::runtime_error("EventList::splitByTime() called on an EventList that no longer has time information."); + + // Start by sorting the event list by pulse time. + this->sortPulseTimeTOF(); + + // Initialize all the output event lists + std::map::iterator outiter; + for (outiter = outputs.begin(); outiter != outputs.end(); ++outiter) + { + EventList* opeventlist = outiter->second; + opeventlist->clear(); + opeventlist->detectorIDs = this->detectorIDs; + opeventlist->refX = this->refX; + // Match the output event type. + opeventlist->switchTo(eventType); + } + + // Split + if (splitter.size() <= 0) + { + // No splitter: copy all events to group workspace = -1 + (*outputs[-1]) = (*this); + } + else + { + // Split + switch (eventType) + { + case TOF: + splitByPulseTimeHelper(splitter, outputs, this->events); + break; + case WEIGHTED: + splitByPulseTimeHelper(splitter, outputs, this->weightedEvents); + break; + case WEIGHTED_NOTIME: + break; + } + } + + return; + } + + //-------------------------------------------------------------------------- /** Get the vector of events contained in an EventList; * this is overloaded by event type. diff --git a/Code/Mantid/Framework/DataObjects/src/Events.cpp b/Code/Mantid/Framework/DataObjects/src/Events.cpp index 3dee986f6349..17d35ae4b423 100644 --- a/Code/Mantid/Framework/DataObjects/src/Events.cpp +++ b/Code/Mantid/Framework/DataObjects/src/Events.cpp @@ -4,6 +4,7 @@ #include "MantidKernel/Exception.h" #include "MantidKernel/DateAndTime.h" #include +#include #include using std::ostream; diff --git a/Code/Mantid/Framework/DataObjects/src/VectorColumn.cpp b/Code/Mantid/Framework/DataObjects/src/VectorColumn.cpp new file mode 100644 index 000000000000..2a7f77657d13 --- /dev/null +++ b/Code/Mantid/Framework/DataObjects/src/VectorColumn.cpp @@ -0,0 +1,17 @@ +#include "MantidAPI/ColumnFactory.h" +#include "MantidDataObjects/VectorColumn.h" + +namespace Mantid +{ +namespace DataObjects +{ + // Please feel free to declare new types as you need them. Syntax is: + // DECLARE_VECTORCOLUMN(type, name-of-the-type); + + // However, when you do that, please don't forget to add new type to the ITableWorkspace.cpp + // so that it can be converted to Python list + + DECLARE_VECTORCOLUMN(int, vector_int) + DECLARE_VECTORCOLUMN(double, vector_double) +} // namespace DataObjects +} // namespace Mantid diff --git a/Code/Mantid/Framework/DataObjects/test/VectorColumnTest.h b/Code/Mantid/Framework/DataObjects/test/VectorColumnTest.h new file mode 100644 index 000000000000..43aa7a42850a --- /dev/null +++ b/Code/Mantid/Framework/DataObjects/test/VectorColumnTest.h @@ -0,0 +1,113 @@ +#ifndef MANTID_DATAOBJECTS_VECTORCOLUMNTEST_H_ +#define MANTID_DATAOBJECTS_VECTORCOLUMNTEST_H_ + +#include + +#include "MantidDataObjects/VectorColumn.h" + +using Mantid::DataObjects::VectorColumn; + +template +class VectorColumnTestHelper : public VectorColumn +{ +public: + using VectorColumn::resize; + using VectorColumn::insert; + using VectorColumn::remove; +}; + +class VectorColumnTest : public CxxTest::TestSuite +{ +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static VectorColumnTest *createSuite() { return new VectorColumnTest(); } + static void destroySuite( VectorColumnTest *suite ) { delete suite; } + + void test_construction() + { + VectorColumn col; + TS_ASSERT_EQUALS( col.type(), "vector_int"); + + VectorColumn col2; + TS_ASSERT_EQUALS( col2.type(), "vector_double"); + } + + void test_read() + { + VectorColumnTestHelper col; + + col.resize(5); + + // Simple case + TS_ASSERT_THROWS_NOTHING( col.read(0,"1,2,3") ); + std::vector v1; + v1.push_back(1); v1.push_back(2); v1.push_back(3); + TS_ASSERT_EQUALS( col.cell< std::vector >(0), v1 ); + + // Check if trimming works + TS_ASSERT_THROWS_NOTHING( col.read(1," 4, 5, 6") ); + std::vector v2; + v2.push_back(4); v2.push_back(5); v2.push_back(6); + TS_ASSERT_EQUALS( col.cell< std::vector >(1), v2 ); + + // Single element + TS_ASSERT_THROWS_NOTHING( col.read(2,"7") ); + std::vector v3; + v3.push_back(7); + TS_ASSERT_EQUALS( col.cell< std::vector >(2), v3 ); + + // Empty string + TS_ASSERT_THROWS_NOTHING( col.read(3,"") ); + std::vector v4; + TS_ASSERT_EQUALS( col.cell< std::vector >(3), v4 ); + TS_ASSERT_EQUALS( col.cell< std::vector >(4), v4 ); + + // Non-convertable characters + TS_ASSERT_THROWS( col.read(4,"1,2,a,3"), std::invalid_argument); + } + + void test_print() + { + VectorColumnTestHelper col; + + col.resize(3); + + // Simple case + std::vector v1; + v1.push_back(11); v1.push_back(22); v1.push_back(33); v1.push_back(44); v1.push_back(55); + col.cell< std::vector >(0) = v1; + std::ostringstream s1; + TS_ASSERT_THROWS_NOTHING( col.print(0, s1) ); + TS_ASSERT_EQUALS( s1.str(), "11,22,33,44,55" ); + + // Single element + std::vector v2; + v2.push_back(9876); + col.cell< std::vector >(1) = v2; + std::ostringstream s2; + TS_ASSERT_THROWS_NOTHING( col.print(1, s2) ); + TS_ASSERT_EQUALS( s2.str(), "9876" ); + + // Unset element + std::ostringstream s3; + TS_ASSERT_THROWS_NOTHING( col.print(2, s3) ); + TS_ASSERT( s3.str().empty() ); + } + + void test_sizeOfData() + { + VectorColumnTestHelper col; + + col.resize(3); + + col.read(0, "1,2,3"); + col.read(1, "3,4,5"); + col.read(2, "7,8,9,10"); + + TS_ASSERT_EQUALS( col.sizeOfData(), 10 * sizeof(int) ); + } +}; + + +#endif /* MANTID_DATAOBJECTS_VECTORCOLUMNTEST_H_ */ diff --git a/Code/Mantid/Framework/Doxygen/Mantid_template.doxyfile b/Code/Mantid/Framework/Doxygen/Mantid_template.doxyfile index 0b06739d7af5..38459889e687 100644 --- a/Code/Mantid/Framework/Doxygen/Mantid_template.doxyfile +++ b/Code/Mantid/Framework/Doxygen/Mantid_template.doxyfile @@ -127,8 +127,8 @@ INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../Algorithms/inc \ @CMAKE_CURRENT_SOURCE_DIR@/../../MantidQt/MantidWidgets/src \ @CMAKE_CURRENT_SOURCE_DIR@/../../MantidQt/SliceViewer/inc \ @CMAKE_CURRENT_SOURCE_DIR@/../../MantidQt/SliceViewer/src \ - @CMAKE_CURRENT_SOURCE_DIR@/../../MantidQt/ImageViewer/inc \ - @CMAKE_CURRENT_SOURCE_DIR@/../../MantidQt/ImageViewer/src \ + @CMAKE_CURRENT_SOURCE_DIR@/../../MantidQt/SpectrumViewer/inc \ + @CMAKE_CURRENT_SOURCE_DIR@/../../MantidQt/SpectrumViewer/src \ @CMAKE_CURRENT_SOURCE_DIR@/../../MantidQt/RefDetectorViewer/inc \ @CMAKE_CURRENT_SOURCE_DIR@/../../MantidQt/RefDetectorViewer/src \ @CMAKE_CURRENT_SOURCE_DIR@/../../Vates/ParaviewPlugins \ diff --git a/Code/Mantid/Framework/Geometry/CMakeLists.txt b/Code/Mantid/Framework/Geometry/CMakeLists.txt index 78f507b3f5bb..8d6e8d0e1eba 100644 --- a/Code/Mantid/Framework/Geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/Geometry/CMakeLists.txt @@ -3,6 +3,7 @@ set ( SRC_FILES src/ComponentParser.cpp src/Crystal/ConventionalCell.cpp src/Crystal/IndexingUtils.cpp + src/Crystal/NiggliCell.cpp src/Crystal/OrientedLattice.cpp src/Crystal/PointGroup.cpp src/Crystal/ReducedCell.cpp @@ -101,6 +102,7 @@ set ( INC_FILES inc/MantidGeometry/ComponentParser.h inc/MantidGeometry/Crystal/ConventionalCell.h inc/MantidGeometry/Crystal/IndexingUtils.h + inc/MantidGeometry/Crystal/NiggliCell.h inc/MantidGeometry/Crystal/OrientedLattice.h inc/MantidGeometry/Crystal/PointGroup.h inc/MantidGeometry/Crystal/ReducedCell.h @@ -233,6 +235,7 @@ set ( TEST_FILES ObjCompAssemblyTest.h ObjComponentTest.h ObjectTest.h + NiggliCellTest.h OrientedLatticeTest.h ParCompAssemblyTest.h ParComponentFactoryTest.h diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/IndexingUtils.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/IndexingUtils.h index 36f8b20cec97..c253550879ad 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/IndexingUtils.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/IndexingUtils.h @@ -51,10 +51,6 @@ namespace Geometry class MANTID_GEOMETRY_DLL IndexingUtils { public: - - /// Convenience method for sorting list of V3D objects based on magnitude - static bool CompareMagnitude( const Kernel::V3D & v1, - const Kernel::V3D & v2 ); /// Find the UB matrix that most nearly indexes the specified qxyz values /// given the lattice parameters @@ -270,18 +266,6 @@ class MANTID_GEOMETRY_DLL IndexingUtils double plane_spacing, double required_tolerance ); - /// Get the UB matix corresponding to the real space edge vectors a, b, c - static bool GetUB( Kernel::DblMatrix & UB, - const Kernel::V3D & a_dir, - const Kernel::V3D & b_dir, - const Kernel::V3D & c_dir ); - - /// Get the real space edge vectors a, b, c corresponding to the UB matrix - static bool GetABC( const Kernel::DblMatrix & UB, - Kernel::V3D & a_dir, - Kernel::V3D & b_dir, - Kernel::V3D & c_dir ); - /// Get the lattice parameters for the specified orientation matrix static bool GetLatticeParameters( const Kernel::DblMatrix & UB, std::vector & lattice_par ); @@ -290,15 +274,7 @@ class MANTID_GEOMETRY_DLL IndexingUtils static std::string GetLatticeParameterString( const Kernel::DblMatrix & UB ); - /// Check if a,b,c cell has angles satifying Niggli condition within epsilon - static bool HasNiggliAngles( const Kernel::V3D & a_dir, - const Kernel::V3D & b_dir, - const Kernel::V3D & c_dir, - double epsilon ); - /// Construct a newUB corresponding to a Niggli cell from the given UB - static bool MakeNiggliUB( const Kernel::DblMatrix & UB, - Kernel::DblMatrix & newUB ); private: /// Static reference to the logger class diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/NiggliCell.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/NiggliCell.h new file mode 100644 index 000000000000..a0cf0b981506 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/NiggliCell.h @@ -0,0 +1,77 @@ +#ifndef MANTID_GEOMETRY_NiggliCell_H_ +#define MANTID_GEOMETRY_NiggliCell_H_ + +#include "MantidGeometry/Crystal/UnitCell.h" +#include + +namespace Mantid +{ +namespace Geometry +{ + /** @class NiggliCell NiggliCell.h Geometry/Crystal/NiggliCell.h + Class to implement UB matrix. + See documentation about UB matrix in the Mantid repository.\n + + @author Andrei Savici, SNS, ORNL + @date 2011-04-15 + Copyright © 2007-8 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: . + Code Documentation is available at: + */ + class MANTID_GEOMETRY_DLL NiggliCell: public UnitCell + { + public: + // Default constructor. a = b = c = 1, alpha = beta = gamma = 90 degrees + NiggliCell(const Kernel::DblMatrix & Umatrix = Kernel::DblMatrix(3,3,true)); + //Copy constructor + NiggliCell(const NiggliCell& other); + // a,b,c constructor + NiggliCell(const double _a,const double _b,const double _c, + const Kernel::DblMatrix & Umatrix = Kernel::DblMatrix(3,3,true)); + //a,b,c,alpha,beta,gamma constructor + NiggliCell(const double _a,const double _b,const double _c,const double _alpha,const double _beta, + const double _gamma, const Kernel::DblMatrix & Umatrix = Kernel::DblMatrix(3,3,true), + const int angleunit=angDegrees); + //UnitCell constructor + NiggliCell(const UnitCell & uc , const Kernel::DblMatrix & Umatrix = Kernel::DblMatrix(3,3,true)); + //UnitCell constructor + NiggliCell(const UnitCell * uc , const Kernel::DblMatrix & Umatrix = Kernel::DblMatrix(3,3,true)); + // Destructor + virtual ~NiggliCell(); + + // Access private variables + /// Check if a,b,c cell has angles satifying Niggli condition within epsilon + static bool HasNiggliAngles( const Kernel::V3D & a_dir, + const Kernel::V3D & b_dir, + const Kernel::V3D & c_dir, + double epsilon ); + + /// Construct a newUB corresponding to a Niggli cell from the given UB + static bool MakeNiggliUB( const Kernel::DblMatrix & UB, + Kernel::DblMatrix & newUB ); + + private: + Kernel::DblMatrix U; + Kernel::DblMatrix UB; + + + }; +} // namespace Mantid +} // namespace Geometry +#endif /* MANTID_GEOMETRY_UNITCELL_H_ */ diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/OrientedLattice.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/OrientedLattice.h index 01de89ea4e44..837a9fa453d4 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/OrientedLattice.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/OrientedLattice.h @@ -73,6 +73,17 @@ namespace Geometry void saveNexus(::NeXus::File * file, const std::string & group) const; /// Load the lattice to from an open NeXus file void loadNexus(::NeXus::File * file, const std::string & group); + /// Get the UB matix corresponding to the real space edge vectors a, b, c + static bool GetUB( Kernel::DblMatrix & UB, + const Kernel::V3D & a_dir, + const Kernel::V3D & b_dir, + const Kernel::V3D & c_dir ); + + /// Get the real space edge vectors a, b, c corresponding to the UB matrix + static bool GetABC( const Kernel::DblMatrix & UB, + Kernel::V3D & a_dir, + Kernel::V3D & b_dir, + Kernel::V3D & c_dir ); private: Kernel::DblMatrix U; diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/Component.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/Component.h index cdbd62681837..a9ddc583a852 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/Component.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/Component.h @@ -8,7 +8,6 @@ #include "MantidGeometry/DllConfig.h" #include "MantidGeometry/Instrument/ParameterMap.h" #include -#include #include #include #include diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/FitParameter.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/FitParameter.h index 0bd580c9c218..ed5c9973a0ad 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/FitParameter.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/FitParameter.h @@ -113,8 +113,8 @@ namespace Mantid std::string m_formulaUnit; ///< the unit that the formula expects std::string m_resultUnit; ///> diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/ParComponentFactory.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/ParComponentFactory.h index 195916812099..50f45fbb52d6 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/ParComponentFactory.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/ParComponentFactory.h @@ -7,7 +7,6 @@ #include #include #include -#include namespace Mantid { diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/ConventionalCell.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/ConventionalCell.cpp index 12c51dd374fa..ebea990bf8a1 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/ConventionalCell.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/ConventionalCell.cpp @@ -7,6 +7,7 @@ #include "MantidKernel/V3D.h" #include "MantidGeometry/Crystal/ConventionalCell.h" #include "MantidGeometry/Crystal/IndexingUtils.h" +#include "MantidGeometry/Crystal/OrientedLattice.h" namespace Mantid @@ -217,13 +218,13 @@ namespace Geometry V3D a_dir; V3D b_dir; V3D c_dir; - IndexingUtils::GetABC( UB, a_dir, b_dir, c_dir ); + OrientedLattice::GetABC( UB, a_dir, b_dir, c_dir ); std::vector edges; edges.push_back( a_dir ); edges.push_back( b_dir ); edges.push_back( c_dir ); - std::sort( edges.begin(), edges.end(), IndexingUtils::CompareMagnitude ); + std::sort( edges.begin(), edges.end(), V3D::CompareMagnitude ); V3D a = edges[0]; V3D b = edges[1]; @@ -234,7 +235,7 @@ namespace Geometry { c = c * (-1); } - IndexingUtils::GetUB( UB, a, b, c ); + OrientedLattice::GetUB( UB, a, b, c ); } @@ -253,7 +254,7 @@ namespace Geometry V3D a; V3D b; V3D c; - IndexingUtils::GetABC( UB, a, b, c ); + OrientedLattice::GetABC( UB, a, b, c ); double a_b_diff = fabs( a.norm() - b.norm() ) / std::min( a.norm(), b.norm() ); @@ -268,11 +269,11 @@ namespace Geometry // equal sides first. if ( a_c_diff <= a_b_diff && a_c_diff <= b_c_diff ) { - IndexingUtils::GetUB( UB, c, a, b ); + OrientedLattice::GetUB( UB, c, a, b ); } else if ( b_c_diff <= a_b_diff && b_c_diff <= a_c_diff ) { - IndexingUtils::GetUB( UB, b, c, a ); + OrientedLattice::GetUB( UB, b, c, a ); } } @@ -292,7 +293,7 @@ namespace Geometry V3D a; V3D b; V3D c; - IndexingUtils::GetABC( UB, a, b, c ); + OrientedLattice::GetABC( UB, a, b, c ); double alpha = b.angle( c ) * 180.0/M_PI; double beta = c.angle( a ) * 180.0/M_PI; @@ -300,22 +301,22 @@ namespace Geometry // degree angle last if ( fabs(alpha-90) > 20 ) { - IndexingUtils::GetUB( UB, b, c, a ); + OrientedLattice::GetUB( UB, b, c, a ); } else if ( fabs(beta-90) > 20 ) { - IndexingUtils::GetUB( UB, c, a, b ); + OrientedLattice::GetUB( UB, c, a, b ); } // if the non 90 degree angle // is about 60 degrees, make // it about 120 degrees. - IndexingUtils::GetABC( UB, a, b, c ); + OrientedLattice::GetABC( UB, a, b, c ); double gamma = a.angle( b ) * 180.0/M_PI; if ( fabs( gamma - 60 ) < 10 ) { a = a * ( -1 ); // reflect a and c to change c = c * ( -1 ); // alpha and gamma to their - IndexingUtils::GetUB( UB, a, b, c ); // supplementary angle + OrientedLattice::GetUB( UB, a, b, c ); // supplementary angle } } diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/IndexingUtils.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/IndexingUtils.cpp index 16f6fcb52184..d200788ccabb 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/IndexingUtils.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/IndexingUtils.cpp @@ -1,4 +1,5 @@ #include "MantidGeometry/Crystal/IndexingUtils.h" +#include "MantidGeometry/Crystal/NiggliCell.h" #include "MantidKernel/Quat.h" #include #include "MantidGeometry/Crystal/OrientedLattice.h" @@ -29,82 +30,6 @@ namespace const double RAD_TO_DEG = 180. / M_PI; } -/** - Comparator function for sorting list of UB matrices based on the sum - of the lengths of the corresponding real space cell edge lengths - |a|+|b|+|C| - */ -static bool CompareABCsum( const DblMatrix & UB_1, const DblMatrix & UB_2 ) -{ - V3D a1; - V3D b1; - V3D c1; - V3D a2; - V3D b2; - V3D c2; - IndexingUtils::GetABC( UB_1, a1, b1, c1 ); - IndexingUtils::GetABC( UB_2, a2, b2, c2 ); - - double sum_1 = a1.norm() + b1.norm() + c1.norm(); - double sum_2 = a2.norm() + b2.norm() + c2.norm(); - - return (sum_1 < sum_2); -} - - -/** - Get the cell angles for the unit cell corresponding to matrix UB - and calculate the sum of the differences of the cell angles from 90 - degrees. - @param UB the UB matrix - @return The sum of the difference of the cell angles from 90 degrees. - */ -static double GetDiffFrom90Sum( const DblMatrix & UB ) -{ - V3D a; - V3D b; - V3D c; - - if ( !IndexingUtils::GetABC( UB, a, b, c ) ) - return -1; - - double alpha = b.angle( c ) * RAD_TO_DEG; - double beta = c.angle( a ) * RAD_TO_DEG; - double gamma = a.angle( b ) * RAD_TO_DEG; - - double sum = fabs( alpha - 90.0 ) + - fabs( beta - 90.0 ) + - fabs( gamma - 90.0 ); - - return sum; -} - - -/** - Comparator to sort a list of UBs in decreasing order based on - the difference of cell angles from 90 degrees. -*/ -static bool CompareDiffFrom90( const DblMatrix & UB_1, const DblMatrix & UB_2 ) -{ - double sum_1 = GetDiffFrom90Sum( UB_1 ); - double sum_2 = GetDiffFrom90Sum( UB_2 ); - - return ( sum_2 < sum_1 ); -} - - -/** - Comparator function for sorting list of 3D vectors based on their magnitude. - @param v1 first vector - @param v2 seconde vector - @return true if v1.norm() < v2.norm(). - */ -bool IndexingUtils::CompareMagnitude( const V3D & v1, const V3D & v2 ) -{ - double mag_sq_1 = v1[0]*v1[0] + v1[1]*v1[1] + v1[2]*v1[2]; - double mag_sq_2 = v2[0]*v2[0] + v2[1]*v2[1] + v2[2]*v2[2]; - return (mag_sq_1 < mag_sq_2); -} /** @@ -213,7 +138,7 @@ double IndexingUtils::Find_UB( DblMatrix & UB, // specified by the base_peak parameter if ( base_index < 0 || base_index >= (int)q_vectors.size() ) { - std::sort( shifted_qs.begin(), shifted_qs.end(), CompareMagnitude ); + std::sort( shifted_qs.begin(), shifted_qs.end(), V3D::CompareMagnitude ); } else { @@ -237,7 +162,7 @@ double IndexingUtils::Find_UB( DblMatrix & UB, sorted_qs.push_back( q_vectors[i] ); } - std::sort( sorted_qs.begin(), sorted_qs.end(), CompareMagnitude ); + std::sort( sorted_qs.begin(), sorted_qs.end(), V3D::CompareMagnitude ); if ( num_initial > sorted_qs.size() ) num_initial = sorted_qs.size(); @@ -428,7 +353,7 @@ double IndexingUtils::Find_UB( DblMatrix & UB, // specified by the base_peak parameter if ( base_index < 0 || base_index >= (int)q_vectors.size() ) { - std::sort( shifted_qs.begin(), shifted_qs.end(), CompareMagnitude ); + std::sort( shifted_qs.begin(), shifted_qs.end(), V3D::CompareMagnitude ); } else { @@ -452,7 +377,7 @@ double IndexingUtils::Find_UB( DblMatrix & UB, sorted_qs.push_back( q_vectors[i] ); } - std::sort( sorted_qs.begin(), sorted_qs.end(), CompareMagnitude ); + std::sort( sorted_qs.begin(), sorted_qs.end(), V3D::CompareMagnitude ); if ( num_initial > sorted_qs.size() ) num_initial = sorted_qs.size(); @@ -475,7 +400,7 @@ double IndexingUtils::Find_UB( DblMatrix & UB, "Find_UB(): Could not find at least three possible lattice directions"); } - std::sort( directions.begin(), directions.end(), CompareMagnitude ); + std::sort( directions.begin(), directions.end(), V3D::CompareMagnitude ); if ( ! FormUB_From_abc_Vectors(UB, directions, 0, min_d, max_d ) ) { @@ -531,7 +456,7 @@ double IndexingUtils::Find_UB( DblMatrix & UB, } } - if ( MakeNiggliUB( UB, temp_UB ) ) + if ( NiggliCell::MakeNiggliUB( UB, temp_UB ) ) UB = temp_UB; return fit_error; @@ -639,7 +564,7 @@ double IndexingUtils::Find_UB( DblMatrix & UB, throw std::invalid_argument("Find_UB(): Could not find enough a,b,c vectors"); } - std::sort( directions.begin(), directions.end(), CompareMagnitude ); + std::sort( directions.begin(), directions.end(), V3D::CompareMagnitude ); double min_vol = min_d * min_d * min_d / 4.0; @@ -677,7 +602,7 @@ double IndexingUtils::Find_UB( DblMatrix & UB, } } - if ( MakeNiggliUB( UB, temp_UB ) ) + if ( NiggliCell::MakeNiggliUB( UB, temp_UB ) ) UB = temp_UB; return fit_error; @@ -781,6 +706,7 @@ double IndexingUtils::Optimize_UB( DblMatrix & UB, return result; } + /** STATIC method Optimize_UB: Calculates the matrix that most nearly maps the specified hkl_vectors to the specified q_vectors. The calculated @@ -1216,7 +1142,7 @@ double IndexingUtils::ScanFor_UB( DblMatrix & UB, } } - if ( !GetUB( UB, a_dir, b_dir, c_dir ) ) + if ( !OrientedLattice::GetUB( UB, a_dir, b_dir, c_dir ) ) { throw std::runtime_error( "UB could not be formed, invert matrix failed"); } @@ -1585,7 +1511,7 @@ size_t IndexingUtils::FFTScanFor_Directions( std::vector & directions, temp_dirs.push_back( current_dir ); } - std::sort( temp_dirs.begin(), temp_dirs.end(), CompareMagnitude ); + std::sort( temp_dirs.begin(), temp_dirs.end(), V3D::CompareMagnitude ); // discard duplicates: double len_tol = 0.1; // 10% tolerance for lengths @@ -1837,7 +1763,7 @@ bool IndexingUtils::FormUB_From_abc_Vectors( DblMatrix & UB, return false; } // now build the UB matrix from a,b,c - if ( !GetUB( UB, a_dir, b_dir, c_dir ) ) + if ( !OrientedLattice::GetUB( UB, a_dir, b_dir, c_dir ) ) { throw std::runtime_error( "UB could not be formed, invert matrix failed"); } @@ -1934,7 +1860,7 @@ bool IndexingUtils::FormUB_From_abc_Vectors( DblMatrix & UB, c_dir = c_dir * (-1.0); } // now build the UB matrix from a,b,c - if ( !GetUB( UB, a_dir, b_dir, c_dir ) ) + if ( !OrientedLattice::GetUB( UB, a_dir, b_dir, c_dir ) ) { throw std::runtime_error( "UB could not be formed, invert matrix failed"); } @@ -2278,9 +2204,7 @@ bool IndexingUtils::CheckUB( const DblMatrix & UB ) } } - double det = UB[0][0] * ( UB[1][1] * UB[2][2] - UB[1][2] * UB[2][1] ) - - UB[0][1] * ( UB[1][0] * UB[2][2] - UB[1][2] * UB[2][0] ) - + UB[0][2] * ( UB[1][0] * UB[2][1] - UB[1][1] * UB[2][0] ); + double det = UB.determinant(); double abs_det = fabs(det); if ( abs_det > 10 || abs_det < 1e-12 ) // UB not found correctly @@ -2962,86 +2886,6 @@ int IndexingUtils::SelectDirection( V3D & best_direction, } -/** - Get the UB matrix corresponding to the real space edge vectors a,b,c. - The inverse of the matrix with vectors a,b,c as rows will be stored in UB. - - @param UB A 3x3 matrix that will be set to the UB matrix. - @param a_dir The real space edge vector for side a of the unit cell - @param b_dir The real space edge vector for side b of the unit cell - @param c_dir The real space edge vector for side c of the unit cell - - @return true if UB was set to the new matrix and false if UB could not be - set since the matrix with a,b,c as rows could not be inverted. - */ -bool IndexingUtils::GetUB( DblMatrix & UB, - const V3D & a_dir, - const V3D & b_dir, - const V3D & c_dir ) -{ - if ( UB.numRows() != 3 || UB.numCols() != 3 ) - { - throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3"); - } - - UB.setRow( 0, a_dir ); - UB.setRow( 1, b_dir ); - UB.setRow( 2, c_dir ); - try - { - UB.Invert(); - } - catch (...) - { - return false; - } - return true; -} - - -/** - Get the real space edge vectors a,b,c corresponding to the UB matrix. - The rows of the inverse of the matrix with will be stored in a_dir, - b_dir, c_dir. - - @param UB A 3x3 matrix containing a UB matrix. - @param a_dir Will be set to the real space edge vector for side a - of the unit cell - @param b_dir Will be set to the real space edge vector for side b - of the unit cell - @param c_dir Will be set to the real space edge vector for side c - of the unit cell - - @return true if the inverse of the matrix UB could be found and the - a_dir, b_dir and c_dir vectors have been set to the rows of - UB inverse. - */ -bool IndexingUtils::GetABC( const DblMatrix & UB, - V3D & a_dir, - V3D & b_dir, - V3D & c_dir ) -{ - if ( UB.numRows() != 3 || UB.numCols() != 3 ) - { - throw std::invalid_argument("GetABC(): UB matrix NULL or not 3X3"); - } - - DblMatrix UB_inverse( UB ); - try - { - UB_inverse.Invert(); - } - catch (...) - { - return false; - } - a_dir( UB_inverse[0][0], UB_inverse[0][1], UB_inverse[0][2] ); - b_dir( UB_inverse[1][0], UB_inverse[1][1], UB_inverse[1][2] ); - c_dir( UB_inverse[2][0], UB_inverse[2][1], UB_inverse[2][2] ); - - return true; -} - /** * Get the lattice parameters, a, b, c, alpha, beta, gamma and cell volume @@ -3057,34 +2901,22 @@ bool IndexingUtils::GetABC( const DblMatrix & UB, bool IndexingUtils::GetLatticeParameters( const DblMatrix & UB, std::vector & lattice_par ) { - V3D a_dir; - V3D b_dir; - V3D c_dir; + OrientedLattice o_lattice; + o_lattice.setUB( UB ); - if ( ! GetABC( UB, a_dir, b_dir, c_dir ) ) - { - return false; - } + lattice_par.clear(); + lattice_par.push_back( o_lattice.a() ); + lattice_par.push_back( o_lattice.b() ); + lattice_par.push_back( o_lattice.c() ); - lattice_par.clear(); - lattice_par.push_back( a_dir.norm() ); - lattice_par.push_back( b_dir.norm() ); - lattice_par.push_back( c_dir.norm() ); - - double alpha = b_dir.angle( c_dir ) * RAD_TO_DEG; - double beta = c_dir.angle( a_dir ) * RAD_TO_DEG; - double gamma = a_dir.angle( b_dir ) * RAD_TO_DEG; - lattice_par.push_back( alpha ); - lattice_par.push_back( beta ); - lattice_par.push_back( gamma ); + lattice_par.push_back( o_lattice.alpha() ); + lattice_par.push_back( o_lattice.beta() ); + lattice_par.push_back( o_lattice.gamma() ); - V3D acrossb = a_dir.cross_prod( b_dir ); - double volume = acrossb.scalar_prod( c_dir ); - - lattice_par.push_back( fabs(volume) ); // keep volume > 0 even if - // cell is left handed - return true; + lattice_par.push_back( o_lattice.volume() ); // keep volume > 0 even if + // cell is left handed + return true; } @@ -3111,182 +2943,3 @@ std::string IndexingUtils::GetLatticeParameterString( const DblMatrix & UB ) } -/** - Check if a,b,c cell has angles satifying Niggli condition within epsilon. - Specifically, check if all angles are strictly less than 90 degrees, - or all angles are greater than or equal to 90 degrees. The inequality - requirements are relaxed by an amount specified by the paramter epsilon - to accommodate some experimental and/or rounding error in the calculated - angles. - - @param a_dir Vector in the direction of the real cell edge vector 'a' - @param b_dir Vector in the direction of the real cell edge vector 'b' - @param c_dir Vector in the direction of the real cell edge vector 'c' - @param epsilon Tolerance (in degrees) around 90 degrees. For example - an angle theta will be considered strictly less than 90 - degrees, if it is less than 90+epsilon. - @return true if all angles are less than 90 degrees, or if all angles - are greater than or equal to 90 degrees. - */ -bool IndexingUtils::HasNiggliAngles( const V3D & a_dir, - const V3D & b_dir, - const V3D & c_dir, - double epsilon ) -{ - double alpha = b_dir.angle( c_dir ) * RAD_TO_DEG; - double beta = c_dir.angle( a_dir ) * RAD_TO_DEG; - double gamma = a_dir.angle( b_dir ) * RAD_TO_DEG; - - if ( alpha < 90+epsilon && beta < 90+epsilon && gamma < 90+epsilon ) - { - return true; - } - - if ( alpha >= 90-epsilon && beta >= 90-epsilon && gamma >= 90-epsilon ) - { - return true; - } - - return false; -} - - -/** - * Try to find a UB that is equivalent to the original UB, but corresponds - * to a Niggli reduced cell with the smallest sum of edge lengths and - * with angles that are farthest from 90 degrees. - * - * @param UB The original UB - * @param newUB Returns the newUB - * - * @return True if a possibly constructive change was made and newUB has been - * set to a new matrix. It returns false if no constructive change was found - * and newUB is just set to the original UB. - */ - -bool IndexingUtils::MakeNiggliUB( const DblMatrix & UB, - DblMatrix & newUB ) -{ - V3D a; - V3D b; - V3D c; - - if ( !GetABC( UB, a, b, c ) ) - { - return false; - } - - V3D v1; - V3D v2; - V3D v3; - // first make a list of linear combinations - // of vectors a,b,c with coefficients up to 5 - std::vector directions; - int N_coeff = 5; - for ( int i = -N_coeff; i <= N_coeff; i++ ) - { - for ( int j = -N_coeff; j <= N_coeff; j++ ) - { - for ( int k = -N_coeff; k <= N_coeff; k++ ) - { - if ( i != 0 || j != 0 || k != 0 ) - { - v1 = a * i; - v2 = b * j; - v3 = c * k; - V3D sum(v1); - sum += v2; - sum += v3; - directions.push_back( sum ); - } - } - } - } - // next sort the list of linear combinations - // in order of increasing length - std::sort( directions.begin(), directions.end(), CompareMagnitude ); - - // next form a list of possible UB matrices - // using sides from the list of linear - // combinations, using shorter directions first. - // Keep trying more until 25 UBs are found. - // Only keep UBs corresponding to cells with - // at least a minimum cell volume - std::vector UB_list; - - size_t num_needed = 25; - size_t max_to_try = 5; - while ( UB_list.size() < num_needed && max_to_try < directions.size() ) - { - max_to_try *= 2; - size_t num_to_try = std::min( max_to_try, directions.size() ); - - V3D acrossb; - double vol = 0; - double min_vol = .1f; // what should this be? 0.1 works OK, but...? - for ( size_t i = 0; i < num_to_try-2; i++ ) - { - a = directions[i]; - for ( size_t j = i+1; j < num_to_try-1; j++ ) - { - b = directions[j]; - acrossb = a.cross_prod(b); - for ( size_t k = j+1; k < num_to_try; k++ ) - { - c = directions[k]; - vol = acrossb.scalar_prod( c ); - if ( vol > min_vol && HasNiggliAngles( a, b, c, 0.01 ) ) - { - Matrix new_tran(3,3,false); - GetUB( new_tran, a, b, c ); - UB_list.push_back( new_tran ); - } - } - } - } - } - // if no valid UBs could be formed, return - // false and the original UB - if ( UB_list.empty() ) - { - newUB = UB; - return false; - } - // now sort the UB's in order of increasing - // total side length |a|+|b|+|c| - std::sort( UB_list.begin(), UB_list.end(), CompareABCsum ); - - // keep only those UB's with total side length - // within .1% of the first one. This can't - // be much larger or "bad" UBs are made for - // some tests with 5% noise - double length_tol = 0.001; - double total_length; - - std::vector short_list; - short_list.push_back( UB_list[0] ); - GetABC( short_list[0], a, b, c ); - total_length = a.norm() + b.norm() + c.norm(); - - bool got_short_list = false; - size_t i = 1; - while ( i < UB_list.size() && !got_short_list ) - { - GetABC( UB_list[i], v1, v2, v3 ); - double next_length = v1.norm() + v2.norm() + v3.norm(); - if ( fabs(next_length - total_length)/total_length < length_tol ) - short_list.push_back( UB_list[i] ); - else - got_short_list = true; - i++; - } - // now sort on the basis of difference of cell - // angles from 90 degrees and return the one - // with angles most different from 90 - std::sort( short_list.begin(), short_list.end(), CompareDiffFrom90 ); - - newUB = short_list[0]; - - return true; -} - diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/NiggliCell.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/NiggliCell.cpp new file mode 100644 index 000000000000..c8177515abe1 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/src/Crystal/NiggliCell.cpp @@ -0,0 +1,360 @@ +#include "MantidGeometry/Crystal/NiggliCell.h" +#include "MantidGeometry/Crystal/OrientedLattice.h" +#include "MantidKernel/Quat.h" +#include +#include "MantidGeometry/Crystal/OrientedLattice.h" +#include +#include +#include + +namespace Mantid +{ +namespace Geometry +{ + using Mantid::Kernel::DblMatrix; + using Mantid::Kernel::V3D; + using Mantid::Kernel::Matrix; + + namespace { + const double TWO_PI = 2.*M_PI; + const double DEG_TO_RAD = M_PI / 180.; + const double RAD_TO_DEG = 180. / M_PI; + } + /** + Comparator function for sorting list of UB matrices based on the sum + of the lengths of the corresponding real space cell edge lengths + |a|+|b|+|C| + */ + static bool CompareABCsum( const DblMatrix & UB_1, const DblMatrix & UB_2 ) + { + V3D a1; + V3D b1; + V3D c1; + V3D a2; + V3D b2; + V3D c2; + OrientedLattice::GetABC( UB_1, a1, b1, c1 ); + OrientedLattice::GetABC( UB_2, a2, b2, c2 ); + + double sum_1 = a1.norm() + b1.norm() + c1.norm(); + double sum_2 = a2.norm() + b2.norm() + c2.norm(); + + return (sum_1 < sum_2); + } + + + /** + Get the cell angles for the unit cell corresponding to matrix UB + and calculate the sum of the differences of the cell angles from 90 + degrees. + @param UB the UB matrix + @return The sum of the difference of the cell angles from 90 degrees. + */ + static double GetDiffFrom90Sum( const DblMatrix & UB ) + { + V3D a; + V3D b; + V3D c; + + if ( !OrientedLattice::GetABC( UB, a, b, c ) ) + return -1; + + double alpha = b.angle( c ) * RAD_TO_DEG; + double beta = c.angle( a ) * RAD_TO_DEG; + double gamma = a.angle( b ) * RAD_TO_DEG; + + double sum = fabs( alpha - 90.0 ) + + fabs( beta - 90.0 ) + + fabs( gamma - 90.0 ); + + return sum; + } + + + /** + Comparator to sort a list of UBs in decreasing order based on + the difference of cell angles from 90 degrees. + */ + static bool CompareDiffFrom90( const DblMatrix & UB_1, const DblMatrix & UB_2 ) + { + double sum_1 = GetDiffFrom90Sum( UB_1 ); + double sum_2 = GetDiffFrom90Sum( UB_2 ); + + return ( sum_2 < sum_1 ); + } + /** Default constructor + @param Umatrix :: orientation matrix U. By default this will be identity matrix + */ + NiggliCell::NiggliCell(const DblMatrix & Umatrix) : UnitCell() + { + if (Umatrix.isRotation()==true) + { + U=Umatrix; + UB=U*getB(); + } + else throw std::invalid_argument("U is not a proper rotation"); + } + + /** Copy constructor + @param other :: The NiggliCell from which to copy information + */ + NiggliCell::NiggliCell(const NiggliCell& other) : UnitCell(other),U(other.U),UB(other.UB) + { + } + + /** Constructor + @param _a :: lattice parameter \f$ a \f$ with \f$\alpha = \beta = \gamma = 90^\circ \f$ + @param _b :: lattice parameter \f$ b \f$ with \f$\alpha = \beta = \gamma = 90^\circ \f$ + @param _c :: lattice parameter \f$ c \f$ with \f$\alpha = \beta = \gamma = 90^\circ \f$ + @param Umatrix :: orientation matrix U + */ + NiggliCell::NiggliCell(const double _a,const double _b,const double _c, + const DblMatrix &Umatrix) : UnitCell(_a,_b,_c) + { + if (Umatrix.isRotation()==true) + { + U=Umatrix; + UB=U*getB(); + } + else throw std::invalid_argument("U is not a proper rotation"); + } + + /** Constructor + @param _a :: lattice parameter \f$ a \f$ + @param _b :: lattice parameter \f$ b \f$ + @param _c :: lattice parameter \f$ c \f$ + @param _alpha :: lattice parameter \f$ \alpha \f$ + @param _beta :: lattice parameter \f$ \beta \f$ + @param _gamma :: lattice parameter \f$ \gamma \f$ + @param angleunit :: units for angle, of type #AngleUnits. Default is degrees. + @param Umatrix :: orientation matrix U + */ + NiggliCell::NiggliCell(const double _a,const double _b,const double _c,const double _alpha, + const double _beta,const double _gamma, const DblMatrix &Umatrix, + const int angleunit) + :UnitCell(_a,_b,_c,_alpha,_beta,_gamma,angleunit) + { + if (Umatrix.isRotation()==true) + { + U=Umatrix; + UB=U*getB(); + } + else throw std::invalid_argument("U is not a proper rotation"); + } + + /** UnitCell constructor + @param uc :: UnitCell + @param Umatrix :: orientation matrix U. By default this will be identity matrix + */ + NiggliCell::NiggliCell(const UnitCell & uc, const DblMatrix & Umatrix) + : UnitCell(uc),U(Umatrix) + { + if (Umatrix.isRotation()==true) + { + U=Umatrix; + UB=U*getB(); + } + else throw std::invalid_argument("U is not a proper rotation"); + } + + NiggliCell::NiggliCell(const UnitCell * uc , const DblMatrix & Umatrix) + : UnitCell(uc),U(Umatrix) + { + if (Umatrix.isRotation()==true) + { + U=Umatrix; + UB=U*getB(); + } + else throw std::invalid_argument("U is not a proper rotation"); + } + + /// Destructor + NiggliCell::~NiggliCell() + { + } + + + + + /** + Check if a,b,c cell has angles satifying Niggli condition within epsilon. + Specifically, check if all angles are strictly less than 90 degrees, + or all angles are greater than or equal to 90 degrees. The inequality + requirements are relaxed by an amount specified by the paramter epsilon + to accommodate some experimental and/or rounding error in the calculated + angles. + + @param a_dir Vector in the direction of the real cell edge vector 'a' + @param b_dir Vector in the direction of the real cell edge vector 'b' + @param c_dir Vector in the direction of the real cell edge vector 'c' + @param epsilon Tolerance (in degrees) around 90 degrees. For example + an angle theta will be considered strictly less than 90 + degrees, if it is less than 90+epsilon. + @return true if all angles are less than 90 degrees, or if all angles + are greater than or equal to 90 degrees. + */ + bool NiggliCell::HasNiggliAngles( const V3D & a_dir, + const V3D & b_dir, + const V3D & c_dir, + double epsilon ) + { + double alpha = b_dir.angle( c_dir ) * RAD_TO_DEG; + double beta = c_dir.angle( a_dir ) * RAD_TO_DEG; + double gamma = a_dir.angle( b_dir ) * RAD_TO_DEG; + + if ( alpha < 90+epsilon && beta < 90+epsilon && gamma < 90+epsilon ) + { + return true; + } + + if ( alpha >= 90-epsilon && beta >= 90-epsilon && gamma >= 90-epsilon ) + { + return true; + } + + return false; + } + + + /** + * Try to find a UB that is equivalent to the original UB, but corresponds + * to a Niggli reduced cell with the smallest sum of edge lengths and + * with angles that are farthest from 90 degrees. + * + * @param UB The original UB + * @param newUB Returns the newUB + * + * @return True if a possibly constructive change was made and newUB has been + * set to a new matrix. It returns false if no constructive change was found + * and newUB is just set to the original UB. + */ + + bool NiggliCell::MakeNiggliUB( const DblMatrix & UB, + DblMatrix & newUB ) + { + V3D a; + V3D b; + V3D c; + + if ( !OrientedLattice::GetABC( UB, a, b, c ) ) + { + return false; + } + + V3D v1; + V3D v2; + V3D v3; + // first make a list of linear combinations + // of vectors a,b,c with coefficients up to 5 + std::vector directions; + int N_coeff = 5; + for ( int i = -N_coeff; i <= N_coeff; i++ ) + { + for ( int j = -N_coeff; j <= N_coeff; j++ ) + { + for ( int k = -N_coeff; k <= N_coeff; k++ ) + { + if ( i != 0 || j != 0 || k != 0 ) + { + v1 = a * i; + v2 = b * j; + v3 = c * k; + V3D sum(v1); + sum += v2; + sum += v3; + directions.push_back( sum ); + } + } + } + } + // next sort the list of linear combinations + // in order of increasing length + std::sort( directions.begin(), directions.end(), V3D::CompareMagnitude ); + + // next form a list of possible UB matrices + // using sides from the list of linear + // combinations, using shorter directions first. + // Keep trying more until 25 UBs are found. + // Only keep UBs corresponding to cells with + // at least a minimum cell volume + std::vector UB_list; + + size_t num_needed = 25; + size_t max_to_try = 5; + while ( UB_list.size() < num_needed && max_to_try < directions.size() ) + { + max_to_try *= 2; + size_t num_to_try = std::min( max_to_try, directions.size() ); + + V3D acrossb; + double vol = 0; + double min_vol = .1f; // what should this be? 0.1 works OK, but...? + for ( size_t i = 0; i < num_to_try-2; i++ ) + { + a = directions[i]; + for ( size_t j = i+1; j < num_to_try-1; j++ ) + { + b = directions[j]; + acrossb = a.cross_prod(b); + for ( size_t k = j+1; k < num_to_try; k++ ) + { + c = directions[k]; + vol = acrossb.scalar_prod( c ); + if ( vol > min_vol && HasNiggliAngles( a, b, c, 0.01 ) ) + { + Matrix new_tran(3,3,false); + OrientedLattice::GetUB( new_tran, a, b, c ); + UB_list.push_back( new_tran ); + } + } + } + } + } + // if no valid UBs could be formed, return + // false and the original UB + if ( UB_list.empty() ) + { + newUB = UB; + return false; + } + // now sort the UB's in order of increasing + // total side length |a|+|b|+|c| + std::sort( UB_list.begin(), UB_list.end(), CompareABCsum ); + + // keep only those UB's with total side length + // within .1% of the first one. This can't + // be much larger or "bad" UBs are made for + // some tests with 5% noise + double length_tol = 0.001; + double total_length; + + std::vector short_list; + short_list.push_back( UB_list[0] ); + OrientedLattice::GetABC( short_list[0], a, b, c ); + total_length = a.norm() + b.norm() + c.norm(); + + bool got_short_list = false; + size_t i = 1; + while ( i < UB_list.size() && !got_short_list ) + { + OrientedLattice::GetABC( UB_list[i], v1, v2, v3 ); + double next_length = v1.norm() + v2.norm() + v3.norm(); + if ( fabs(next_length - total_length)/total_length < length_tol ) + short_list.push_back( UB_list[i] ); + else + got_short_list = true; + i++; + } + // now sort on the basis of difference of cell + // angles from 90 degrees and return the one + // with angles most different from 90 + std::sort( short_list.begin(), short_list.end(), CompareDiffFrom90 ); + + newUB = short_list[0]; + + return true; + } + + +}//Namespace Geometry +}//Namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/OrientedLattice.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/OrientedLattice.cpp index e1b5647249f0..16a5e40f39eb 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/OrientedLattice.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/OrientedLattice.cpp @@ -163,13 +163,13 @@ namespace Geometry { DblMatrix UBinv = this->getUB(); UBinv.Invert(); - V3D out = UBinv*Q; //transform back to HKL + V3D out = UBinv*Q/TWO_PI; //transform back to HKL return out; } /** Calculate the hkl corresponding to a given Q-vector - * @return Q :: Q-vector in $AA^-1 in the sample frame - * @param a V3D with H,K,L + * @param hkl a V3D with H,K,L + * @return Q-vector in $AA^-1 in the sample frame */ V3D OrientedLattice::qFromHKL(const V3D & hkl) const { @@ -282,5 +282,85 @@ namespace Geometry file->closeGroup(); } + + /** + Get the UB matrix corresponding to the real space edge vectors a,b,c. + The inverse of the matrix with vectors a,b,c as rows will be stored in UB. + + @param UB A 3x3 matrix that will be set to the UB matrix. + @param a_dir The real space edge vector for side a of the unit cell + @param b_dir The real space edge vector for side b of the unit cell + @param c_dir The real space edge vector for side c of the unit cell + + @return true if UB was set to the new matrix and false if UB could not be + set since the matrix with a,b,c as rows could not be inverted. + */ + bool OrientedLattice::GetUB( DblMatrix & UB, + const V3D & a_dir, + const V3D & b_dir, + const V3D & c_dir ) + { + if ( UB.numRows() != 3 || UB.numCols() != 3 ) + { + throw std::invalid_argument("Find_UB(): UB matrix NULL or not 3X3"); + } + + UB.setRow( 0, a_dir ); + UB.setRow( 1, b_dir ); + UB.setRow( 2, c_dir ); + try + { + UB.Invert(); + } + catch (...) + { + return false; + } + return true; + } + + + /** + Get the real space edge vectors a,b,c corresponding to the UB matrix. + The rows of the inverse of the matrix with will be stored in a_dir, + b_dir, c_dir. + + @param UB A 3x3 matrix containing a UB matrix. + @param a_dir Will be set to the real space edge vector for side a + of the unit cell + @param b_dir Will be set to the real space edge vector for side b + of the unit cell + @param c_dir Will be set to the real space edge vector for side c + of the unit cell + + @return true if the inverse of the matrix UB could be found and the + a_dir, b_dir and c_dir vectors have been set to the rows of + UB inverse. + */ + bool OrientedLattice::GetABC( const DblMatrix & UB, + V3D & a_dir, + V3D & b_dir, + V3D & c_dir ) + { + if ( UB.numRows() != 3 || UB.numCols() != 3 ) + { + throw std::invalid_argument("GetABC(): UB matrix NULL or not 3X3"); + } + + DblMatrix UB_inverse( UB ); + try + { + UB_inverse.Invert(); + } + catch (...) + { + return false; + } + a_dir( UB_inverse[0][0], UB_inverse[0][1], UB_inverse[0][2] ); + b_dir( UB_inverse[1][0], UB_inverse[1][1], UB_inverse[1][2] ); + c_dir( UB_inverse[2][0], UB_inverse[2][1], UB_inverse[2][2] ); + + return true; + } }//Namespace Geometry }//Namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/ScalarUtils.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/ScalarUtils.cpp index 0f414733c5ed..f52dcf92ccc1 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/ScalarUtils.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/ScalarUtils.cpp @@ -5,6 +5,7 @@ #include "MantidGeometry/Crystal/ScalarUtils.h" #include "MantidGeometry/Crystal/ReducedCell.h" #include "MantidGeometry/Crystal/IndexingUtils.h" +#include "MantidGeometry/Crystal/OrientedLattice.h" using namespace Mantid::Geometry; using Mantid::Kernel::V3D; @@ -211,7 +212,7 @@ std::vector ScalarUtils::GetCellsUBOnly( * reflections of a,b,c and forming the ConventionalCellInfo object * corresponding to the smallest form error. * - * @param UB Orientation transformation corresponding to a Niggli + * @param UB Crystal::Orientation transformation corresponding to a Niggli * reduced cell. * @param form_num The form number to use. * @@ -379,7 +380,7 @@ std::vector ScalarUtils::GetRelatedUBs( V3D a_temp, b_temp, c_temp, // vectors for generating handedness m_a_temp,m_b_temp,m_c_temp; // preserving permutations of sides - IndexingUtils::GetABC( UB, a_vec, b_vec, c_vec ); + OrientedLattice::GetABC( UB, a_vec, b_vec, c_vec ); m_a_vec = a_vec * (-1.0); m_b_vec = b_vec * (-1.0); @@ -435,7 +436,7 @@ std::vector ScalarUtils::GetRelatedUBs( b.norm() <= factor * c.norm() ) // could be Niggli within { // experimental error Matrix temp_UB(3,3,false); - IndexingUtils::GetUB( temp_UB, a, b, c ); + OrientedLattice::GetUB( temp_UB, a, b, c ); result.push_back( temp_UB ); } } diff --git a/Code/Mantid/Framework/Geometry/src/Instrument/FitParameter.cpp b/Code/Mantid/Framework/Geometry/src/Instrument/FitParameter.cpp index 047a28c9e572..d89318de0565 100644 --- a/Code/Mantid/Framework/Geometry/src/Instrument/FitParameter.cpp +++ b/Code/Mantid/Framework/Geometry/src/Instrument/FitParameter.cpp @@ -151,7 +151,24 @@ namespace Geometry } /** - Reads in parameter value + Reads in information about a fitting parameter. The expected format is a comma separated + list that can have varying length but the entries in the list will be read according to: + + 1st (0) : parameter value (which is converted to float) + 2nd (1) : fitting function this parameter belong to + 3rd (2) : parameter name + 4th (3) : constrain min + 5th (4) : constrain max + 6th (5) : constrain penalty factor + 7th (6) : set tie + 8th (7) : set formula + 9th (8) : set formula unit + 10th (9) : set result unit + 11th onwards (10-) : read lookup table values + + Information about fitting \ can be found on www.mantidproject.org/IDF. + Note also printSelf() does the reverse of the this method, i.e. print of the information + of a parameter as listed above. @param in :: Input Stream @param f :: FitParameter to write to @return Current state of stream diff --git a/Code/Mantid/Framework/Geometry/src/Instrument/ObjCompAssembly.cpp b/Code/Mantid/Framework/Geometry/src/Instrument/ObjCompAssembly.cpp index 95996f5f1c32..14593e1f09f3 100644 --- a/Code/Mantid/Framework/Geometry/src/Instrument/ObjCompAssembly.cpp +++ b/Code/Mantid/Framework/Geometry/src/Instrument/ObjCompAssembly.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace Mantid { diff --git a/Code/Mantid/Framework/Geometry/src/Math/Acomp.cpp b/Code/Mantid/Framework/Geometry/src/Math/Acomp.cpp index 6511e2493b9a..5ba425e04db0 100644 --- a/Code/Mantid/Framework/Geometry/src/Math/Acomp.cpp +++ b/Code/Mantid/Framework/Geometry/src/Math/Acomp.cpp @@ -5,6 +5,7 @@ #include "MantidKernel/Matrix.h" #include "MantidGeometry/Math/RotCounter.h" #include +#include #include #include diff --git a/Code/Mantid/Framework/Geometry/src/Objects/Object.cpp b/Code/Mantid/Framework/Geometry/src/Objects/Object.cpp index 435c672560d5..bfc4cb191af0 100644 --- a/Code/Mantid/Framework/Geometry/src/Objects/Object.cpp +++ b/Code/Mantid/Framework/Geometry/src/Objects/Object.cpp @@ -18,6 +18,7 @@ #include "MantidKernel/RegexStrings.h" #include "MantidKernel/Tolerance.h" #include +#include #include namespace Mantid @@ -1021,7 +1022,7 @@ namespace Mantid const double scalTripProd = ao.scalar_prod(bo.cross_prod(co)); const double denom = modao * modbo * modco + modco * aobo + modbo * aoco + modao * boco; if (denom != 0.0) - return 2.0 * atan(scalTripProd / denom); + return 2.0 * atan2(scalTripProd, denom); else return 0.0; // not certain this is correct } diff --git a/Code/Mantid/Framework/Geometry/src/Rendering/BitmapGeometryHandler.cpp b/Code/Mantid/Framework/Geometry/src/Rendering/BitmapGeometryHandler.cpp index a3d9b27a195b..12f691b42c82 100644 --- a/Code/Mantid/Framework/Geometry/src/Rendering/BitmapGeometryHandler.cpp +++ b/Code/Mantid/Framework/Geometry/src/Rendering/BitmapGeometryHandler.cpp @@ -2,6 +2,7 @@ #include "MantidGeometry/Rendering/OpenGL_Headers.h" #include "MantidGeometry/Instrument/RectangularDetector.h" #include +#include namespace Mantid { diff --git a/Code/Mantid/Framework/Geometry/src/Surfaces/Cylinder.cpp b/Code/Mantid/Framework/Geometry/src/Surfaces/Cylinder.cpp index df7443d54a83..8a88f862b951 100644 --- a/Code/Mantid/Framework/Geometry/src/Surfaces/Cylinder.cpp +++ b/Code/Mantid/Framework/Geometry/src/Surfaces/Cylinder.cpp @@ -4,6 +4,7 @@ #include "MantidKernel/Tolerance.h" #include "MantidKernel/Matrix.h" #include +#include namespace Mantid { diff --git a/Code/Mantid/Framework/Geometry/src/Surfaces/Line.cpp b/Code/Mantid/Framework/Geometry/src/Surfaces/Line.cpp index 64d4f0f5eb26..c5c41a66c5de 100644 --- a/Code/Mantid/Framework/Geometry/src/Surfaces/Line.cpp +++ b/Code/Mantid/Framework/Geometry/src/Surfaces/Line.cpp @@ -8,6 +8,7 @@ #include "MantidGeometry/Surfaces/Plane.h" #include "MantidGeometry/Surfaces/Sphere.h" #include "MantidKernel/Tolerance.h" +#include namespace Mantid { diff --git a/Code/Mantid/Framework/Geometry/src/Surfaces/Plane.cpp b/Code/Mantid/Framework/Geometry/src/Surfaces/Plane.cpp index 7f005de1b3e6..00d2f5c9ed79 100644 --- a/Code/Mantid/Framework/Geometry/src/Surfaces/Plane.cpp +++ b/Code/Mantid/Framework/Geometry/src/Surfaces/Plane.cpp @@ -3,6 +3,7 @@ #include "MantidKernel/Strings.h" #include "MantidKernel/Tolerance.h" #include +#include namespace Mantid { diff --git a/Code/Mantid/Framework/Geometry/test/IndexingUtilsTest.h b/Code/Mantid/Framework/Geometry/test/IndexingUtilsTest.h index d89e20bd4188..b92a4a6cf4ac 100644 --- a/Code/Mantid/Framework/Geometry/test/IndexingUtilsTest.h +++ b/Code/Mantid/Framework/Geometry/test/IndexingUtilsTest.h @@ -8,7 +8,7 @@ #include #include #include - +#include "MantidGeometry/Crystal/OrientedLattice.h" #include using namespace Mantid::Geometry; @@ -19,7 +19,6 @@ class IndexingUtilsTest : public CxxTest::TestSuite { public: -#define PI 3.141592653589793238 static std::vector getNatroliteQs() { @@ -75,19 +74,6 @@ class IndexingUtilsTest : public CxxTest::TestSuite } - static Matrix getSiliconUB() - { - Matrix UB(3,3,false); - V3D row_0( -0.147196, -0.141218, 0.304286 ); - V3D row_1( 0.106642, 0.120341, 0.090518 ); - V3D row_2( -0.261273, 0.258426, -0.006190 ); - UB.setRow( 0, row_0 ); - UB.setRow( 1, row_1 ); - UB.setRow( 2, row_2 ); - return UB; - } - - static void ShowLatticeParameters( Matrix UB ) { Matrix UB_inv(3,3,false); @@ -96,9 +82,9 @@ class IndexingUtilsTest : public CxxTest::TestSuite V3D a_dir( UB_inv[0][0], UB_inv[0][1], UB_inv[0][2] ); V3D b_dir( UB_inv[1][0], UB_inv[1][1], UB_inv[1][2] ); V3D c_dir( UB_inv[2][0], UB_inv[2][1], UB_inv[2][2] ); - double alpha = b_dir.angle( c_dir ) * 180 / PI; - double beta = c_dir.angle( a_dir ) * 180 / PI; - double gamma = a_dir.angle( b_dir ) * 180 / PI; + double alpha = b_dir.angle( c_dir ) * 180 / M_PI; + double beta = c_dir.angle( a_dir ) * 180 / M_PI; + double gamma = a_dir.angle( b_dir ) * 180 / M_PI; std::cout << "-------------------------------------------" << std::endl; std::cout << "a = " << a_dir << " " << a_dir.norm() << std::endl; std::cout << "b = " << b_dir << " " << b_dir.norm() << std::endl; @@ -556,15 +542,15 @@ class IndexingUtilsTest : public CxxTest::TestSuite V3D a_dir( 1, 2, 3 ); V3D b_dir( -3, 2, 1 ); - double gamma = a_dir.angle( b_dir ) * 180.0 / PI; + double gamma = a_dir.angle( b_dir ) * 180.0 / M_PI; double alpha = 123; double beta = 74; double c_length = 10; V3D result = IndexingUtils::Make_c_dir( a_dir, b_dir, c_length, alpha, beta, gamma ); - double alpha_calc = result.angle( b_dir ) * 180 / PI; - double beta_calc = result.angle( a_dir ) * 180 / PI; + double alpha_calc = result.angle( b_dir ) * 180 / M_PI; + double beta_calc = result.angle( a_dir ) * 180 / M_PI; TS_ASSERT_DELTA( result.norm(), c_length, 1e-5 ); TS_ASSERT_DELTA( alpha_calc, alpha, 1e-5 ); @@ -959,7 +945,7 @@ class IndexingUtilsTest : public CxxTest::TestSuite Matrix UB(3,3,false); - IndexingUtils::GetUB( UB, a_dir, b_dir, c_dir ); + OrientedLattice::GetUB( UB, a_dir, b_dir, c_dir ); for ( size_t row = 0; row < 3; row++ ) { @@ -980,7 +966,7 @@ class IndexingUtilsTest : public CxxTest::TestSuite V3D b; V3D c; - IndexingUtils::GetABC( UB, a, b, c ); + OrientedLattice::GetABC( UB, a, b, c ); a = a - a_dir; b = b - b_dir; @@ -1008,86 +994,6 @@ class IndexingUtilsTest : public CxxTest::TestSuite TS_ASSERT_DELTA( lat_par[i], correct_value[i], 1e-3 ); } - - void test_HasNiggleAngles() - { - V3D a(1,0,0); - V3D b(0,1,0); - V3D c(0,0,1); - - TS_ASSERT_EQUALS( IndexingUtils::HasNiggliAngles(a, b, c, 0.001 ), true); - - V3D b1( 0.1, 1, 0 ); - V3D c1(-0.1, 0, 1 ); - - TS_ASSERT_EQUALS( IndexingUtils::HasNiggliAngles(a, b1, c1, 0.001), false); - - V3D a2( 1, 0.1, 0.1 ); - V3D b2( 0.1, 1, 0.1 ); - V3D c2( 0.1, 0.1, 1 ); - - TS_ASSERT_EQUALS( IndexingUtils::HasNiggliAngles(a2, b2, c2, 0.001), true); - - V3D a3( 1, -0.1, -0.1 ); - V3D b3( -0.1, 1, -0.1 ); - V3D c3( -0.1, -0.1, 1 ); - - TS_ASSERT_EQUALS( IndexingUtils::HasNiggliAngles(a3, b3, c3, 0.001), true); - } - - - void test_MakeNiggliUB() - { - double answer[3][3] = { { -0.147196, -0.141218, 0.304286 }, - { 0.106642, 0.120341, 0.090518 }, - { -0.261273, 0.258426, -0.006190 } }; - - Matrix newUB(3,3,false); - Matrix UB = getSiliconUB(); - UB = UB * 1.0; - - TS_ASSERT( IndexingUtils::MakeNiggliUB( UB, newUB ) ); - - for ( size_t row = 0; row < 3; row++ ) - for ( size_t col = 0; col < 3; col++ ) - TS_ASSERT_DELTA( newUB[row][col], answer[row][col], 1e-5 ); - } - - - void test_MakeNiggliUB2() - { - // Make a fake UB matrix with: - // gamma > 90 deg - // alpha < 90 deg - Matrix UB(3,3, true); - V3D a(10,0,0); - V3D b(-5,5,0); - V3D c(0,5,5); - IndexingUtils::GetUB(UB, a,b,c); - - Matrix newUB(3,3,false); - - TS_ASSERT( IndexingUtils::MakeNiggliUB( UB, newUB ) ); - - // Extract the a,b,c vectors - V3D a_dir; - V3D b_dir; - V3D c_dir; - IndexingUtils::GetABC(newUB, a_dir,b_dir,c_dir); - double alpha = b_dir.angle( c_dir ) * 180.0/PI; - double beta = c_dir.angle( a_dir ) * 180.0/PI; - double gamma = a_dir.angle( b_dir ) * 180.0/PI; - // All vectors have two components of length 5.0 - double norm = sqrt(50.0); - TS_ASSERT_DELTA( a_dir.norm(), norm, 1e-3 ); - TS_ASSERT_DELTA( b_dir.norm(), norm, 1e-3 ); - TS_ASSERT_DELTA( c_dir.norm(), norm, 1e-3 ); - // Angles are 60 degrees - TS_ASSERT_DELTA( alpha, 60, 1e-1 ); - TS_ASSERT_DELTA( beta, 60, 1e-1 ); - TS_ASSERT_DELTA( gamma, 60, 1e-1 ); - - } }; #endif /* MANTID_GEOMETRY_INDEXING_UTILS_TEST_H_ */ diff --git a/Code/Mantid/Framework/Geometry/test/NiggliCellTest.h b/Code/Mantid/Framework/Geometry/test/NiggliCellTest.h new file mode 100644 index 000000000000..060290591e30 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/test/NiggliCellTest.h @@ -0,0 +1,132 @@ +#ifndef MANTID_GEOMETRY_NiggliCellTEST_H_ +#define MANTID_GEOMETRY_NiggliCellTEST_H_ + +#include +#include +#include +#include "MantidKernel/Matrix.h" +#include "MantidGeometry/Crystal/NiggliCell.h" +#include "MantidGeometry/Crystal/OrientedLattice.h" +#include "MantidTestHelpers/NexusTestHelper.h" + +using namespace Mantid::Geometry; +using Mantid::Kernel::V3D; +using Mantid::Kernel::DblMatrix; +using Mantid::Kernel::Matrix; + +class NiggliCellTest : public CxxTest::TestSuite +{ +public: + + + static Matrix getSiliconUB() + { + Matrix UB(3,3,false); + V3D row_0( -0.147196, -0.141218, 0.304286 ); + V3D row_1( 0.106642, 0.120341, 0.090518 ); + V3D row_2( -0.261273, 0.258426, -0.006190 ); + UB.setRow( 0, row_0 ); + UB.setRow( 1, row_1 ); + UB.setRow( 2, row_2 ); + return UB; + } + /// test constructors, access to some of the variables + void test_Simple() + { + + NiggliCell u1,u2(3,4,5),u3(2,3,4,85.,95.,100),u4; + u4=u2; + TS_ASSERT_EQUALS(u1.a1(),1); + TS_ASSERT_EQUALS(u1.alpha(),90); + TS_ASSERT_DELTA(u2.b1(),1./3.,1e-10); + TS_ASSERT_DELTA(u2.alphastar(),90,1e-10); + TS_ASSERT_DELTA(u4.volume(),1./u2.recVolume(),1e-10); + u2.seta(3); + TS_ASSERT_DELTA(u2.a(),3,1e-10); + } + +void test_HasNiggleAngles() +{ + V3D a(1,0,0); + V3D b(0,1,0); + V3D c(0,0,1); + + TS_ASSERT_EQUALS( NiggliCell::HasNiggliAngles(a, b, c, 0.001 ), true); + + V3D b1( 0.1, 1, 0 ); + V3D c1(-0.1, 0, 1 ); + + TS_ASSERT_EQUALS( NiggliCell::HasNiggliAngles(a, b1, c1, 0.001), false); + + V3D a2( 1, 0.1, 0.1 ); + V3D b2( 0.1, 1, 0.1 ); + V3D c2( 0.1, 0.1, 1 ); + + TS_ASSERT_EQUALS( NiggliCell::HasNiggliAngles(a2, b2, c2, 0.001), true); + + V3D a3( 1, -0.1, -0.1 ); + V3D b3( -0.1, 1, -0.1 ); + V3D c3( -0.1, -0.1, 1 ); + + TS_ASSERT_EQUALS( NiggliCell::HasNiggliAngles(a3, b3, c3, 0.001), true); +} + + +void test_MakeNiggliUB() +{ + double answer[3][3] = { { -0.147196, -0.141218, 0.304286 }, + { 0.106642, 0.120341, 0.090518 }, + { -0.261273, 0.258426, -0.006190 } }; + + Matrix newUB(3,3,false); + Matrix UB = getSiliconUB(); + UB = UB * 1.0; + + TS_ASSERT( NiggliCell::MakeNiggliUB( UB, newUB ) ); + + for ( size_t row = 0; row < 3; row++ ) + for ( size_t col = 0; col < 3; col++ ) + TS_ASSERT_DELTA( newUB[row][col], answer[row][col], 1e-5 ); +} + + +void test_MakeNiggliUB2() +{ + // Make a fake UB matrix with: + // gamma > 90 deg + // alpha < 90 deg + Matrix UB(3,3, true); + V3D a(10,0,0); + V3D b(-5,5,0); + V3D c(0,5,5); + OrientedLattice::GetUB(UB, a,b,c); + + Matrix newUB(3,3,false); + + TS_ASSERT( NiggliCell::MakeNiggliUB( UB, newUB ) ); + + // Extract the a,b,c vectors + V3D a_dir; + V3D b_dir; + V3D c_dir; + OrientedLattice::GetABC(newUB, a_dir,b_dir,c_dir); + double alpha = b_dir.angle( c_dir ) * 180.0/M_PI; + double beta = c_dir.angle( a_dir ) * 180.0/M_PI; + double gamma = a_dir.angle( b_dir ) * 180.0/M_PI; + // All vectors have two components of length 5.0 + double norm = sqrt(50.0); + TS_ASSERT_DELTA( a_dir.norm(), norm, 1e-3 ); + TS_ASSERT_DELTA( b_dir.norm(), norm, 1e-3 ); + TS_ASSERT_DELTA( c_dir.norm(), norm, 1e-3 ); + // Angles are 60 degrees + TS_ASSERT_DELTA( alpha, 60, 1e-1 ); + TS_ASSERT_DELTA( beta, 60, 1e-1 ); + TS_ASSERT_DELTA( gamma, 60, 1e-1 ); + +} + +}; + + +#endif /* MANTID_GEOMETRY_NiggliCellTEST_H_ */ + diff --git a/Code/Mantid/Framework/Geometry/test/OrientedLatticeTest.h b/Code/Mantid/Framework/Geometry/test/OrientedLatticeTest.h index 2ea23bcbbb69..a2006de288f4 100644 --- a/Code/Mantid/Framework/Geometry/test/OrientedLatticeTest.h +++ b/Code/Mantid/Framework/Geometry/test/OrientedLatticeTest.h @@ -41,7 +41,7 @@ class OrientedLatticeTest : public CxxTest::TestSuite // Convert to and from HKL V3D hkl = u.hklFromQ(V3D(1.0, 2.0, 3.0)); double dstar = u.dstar(hkl[0], hkl[1], hkl[2]); - TS_ASSERT_DELTA( dstar, sqrt(1+4.0+9.0), 1e-4); // The d-spacing after a round trip matches the Q we put in + TS_ASSERT_DELTA( dstar, .5*sqrt(1+4.0+9.0)/M_PI, 1e-4); // The d-spacing after a round trip matches the Q we put in } diff --git a/Code/Mantid/Framework/ICat/inc/MantidICat/ICat3/ICat3Helper.h b/Code/Mantid/Framework/ICat/inc/MantidICat/ICat3/ICat3Helper.h index 9deed4109cb0..6cb98192381d 100644 --- a/Code/Mantid/Framework/ICat/inc/MantidICat/ICat3/ICat3Helper.h +++ b/Code/Mantid/Framework/ICat/inc/MantidICat/ICat3/ICat3Helper.h @@ -132,6 +132,12 @@ namespace Mantid /// Builds search query based on user input and stores query in related ICAT class. ICat3::ns1__advancedSearchDetails* buildSearchQuery(const CatalogSearchParam& inputs); + // Defines the SSL authentication scheme. + void setSSLContext(ICat3::ICATPortBindingProxy& icat); + + // Sets the soap-endpoint & SSL context for the given ICAT proxy. + void setICATProxySettings(ICat3::ICATPortBindingProxy& icat); + /** This is a template method to save data to table workspace * @param input :: pointer to input value * @param t :: table row reference diff --git a/Code/Mantid/Framework/ICat/inc/MantidICat/ICat4/ICat4Catalog.h b/Code/Mantid/Framework/ICat/inc/MantidICat/ICat4/ICat4Catalog.h index 28f771da40e2..b6d15a6c6c18 100644 --- a/Code/Mantid/Framework/ICat/inc/MantidICat/ICat4/ICat4Catalog.h +++ b/Code/Mantid/Framework/ICat/inc/MantidICat/ICat4/ICat4Catalog.h @@ -86,6 +86,9 @@ namespace Mantid std::string bytesToString(int64_t &fileSize); // Helper method that formats a given timestamp. std::string formatDateTime(const time_t ×tamp, const std::string &format); + // Sets the soap-endpoint & SSL context for the given ICAT proxy. + void setICATProxySettings(ICat4::ICATPortBindingProxy& icat); + // Reference to the logger class. Kernel::Logger& g_log; diff --git a/Code/Mantid/Framework/ICat/inc/MantidICat/Session.h b/Code/Mantid/Framework/ICat/inc/MantidICat/Session.h index bd089562e516..6cae0713864a 100644 --- a/Code/Mantid/Framework/ICat/inc/MantidICat/Session.h +++ b/Code/Mantid/Framework/ICat/inc/MantidICat/Session.h @@ -45,6 +45,10 @@ namespace Mantid const std::string & getUserName() const { return m_userName; } ///set username void setUserName(const std::string& userName) { m_userName=userName; } + /// Get the soap end-point. + const std::string & getSoapEndPoint() const { return m_soapEndPoint; } + /// Set the soap end-point. + void setSoapEndPoint(const std::string& soapEndPoint) { m_soapEndPoint = soapEndPoint; } private: /// used to create singleton @@ -61,6 +65,8 @@ namespace Mantid std::string m_sessionId; /// user name std::string m_userName; + /// Cache soap end-point + std::string m_soapEndPoint; }; #ifdef _WIN32 diff --git a/Code/Mantid/Framework/ICat/src/CatalogLogin.cpp b/Code/Mantid/Framework/ICat/src/CatalogLogin.cpp index 4e3a14c7da72..5e1fab8a4e87 100644 --- a/Code/Mantid/Framework/ICat/src/CatalogLogin.cpp +++ b/Code/Mantid/Framework/ICat/src/CatalogLogin.cpp @@ -8,6 +8,7 @@ This algorithm connects the logged in user to the information catalog. #include "MantidICat/CatalogAlgorithmHelper.h" #include "MantidKernel/MandatoryValidator.h" #include "MantidKernel/MaskedProperty.h" +#include "MantidKernel/ListValidator.h" namespace Mantid { @@ -29,17 +30,22 @@ namespace Mantid declareProperty("Username","", requireValue,"The username to log into the catalog."); declareProperty(new Kernel::MaskedProperty("Password","", requireValue), "The password of the related username to use."); + declareProperty("FacilityName",Mantid::Kernel::ConfigService::Instance().getFacility().name(), + boost::make_shared(Kernel::ConfigService::Instance().getFacilityNames()), + "Select a facility to log in to."); } /// execute the algorithm void CatalogLogin::exec() { - std::string username = getProperty("Username"); - std::string password = getProperty("Password"); + // Obtain the soapEndPoint based on the name of the facility the user has selected. + std::string soapEndPoint = Kernel::ConfigService::Instance().getFacility(getProperty("FacilityName")).catalogInfo().soapEndPoint(); + if (soapEndPoint.empty()) throw std::runtime_error("There is no soap end-point for the facility you have selected."); + g_log.notice() << "Attempting to verify user credentials against " << Mantid::Kernel::ConfigService::Instance().getFacility().catalogInfo().catalogName() << std::endl; progress(0.5, "Verifying user credentials..."); - CatalogAlgorithmHelper().createCatalog()->login(username, password, ""); + CatalogAlgorithmHelper().createCatalog()->login(getProperty("Username"), getProperty("Password"), soapEndPoint); } } diff --git a/Code/Mantid/Framework/ICat/src/ICat3/ICat3Helper.cpp b/Code/Mantid/Framework/ICat/src/ICat3/ICat3Helper.cpp index 5ea0b6ad0167..dab2fab53679 100644 --- a/Code/Mantid/Framework/ICat/src/ICat3/ICat3Helper.cpp +++ b/Code/Mantid/Framework/ICat/src/ICat3/ICat3Helper.cpp @@ -25,19 +25,8 @@ namespace Mantid */ int CICatHelper::doSearch(ICATPortBindingProxy& icat,boost::shared_ptr& request,ns1__searchByAdvancedResponse& response) { - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); + clock_t start=clock(); int ret_advsearch=icat.searchByAdvanced(request.get(),&response); if(ret_advsearch!=0) @@ -288,19 +277,7 @@ namespace Mantid { //ICAt proxy object ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); ns1__getInvestigationIncludes request; //get the sessionid which is cached in session class during login @@ -437,19 +414,7 @@ namespace Mantid { //ICAt proxy object ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); // request object ns1__getInvestigationIncludes request; @@ -546,20 +511,7 @@ namespace Mantid { //ICAt proxy object ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } - + setICATProxySettings(icat); ns1__listInstruments request; //get the sessionid which is cached in session class during login @@ -611,20 +563,7 @@ namespace Mantid { //ICAt proxy object ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } - + setICATProxySettings(icat); ns1__listInvestigationTypes request; //get the sessionid which is cached in session class during login @@ -687,20 +626,7 @@ namespace Mantid int CICatHelper::doLogout() { ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); ns1__logout request; ns1__logoutResponse response; @@ -723,20 +649,7 @@ namespace Mantid void CICatHelper::doMyDataSearch(API::ITableWorkspace_sptr& ws_sptr) { ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); ns1__getMyInvestigationsIncludes request; ns1__getMyInvestigationsIncludesResponse response; @@ -817,21 +730,8 @@ namespace Mantid request.startIndex = offset; request.advancedSearchDetails = buildSearchQuery(inputs); - //ICAt proxy object ICATPortBindingProxy icat; - - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); int result = icat.searchByAdvancedPagination(&request, &response); @@ -951,19 +851,7 @@ namespace Mantid int64_t CICatHelper::getNumberOfSearchResults(const CatalogSearchParam& inputs) { ICATPortBindingProxy icat; - - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); ns1__searchByAdvanced request; ns1__searchByAdvancedResponse response; @@ -995,22 +883,8 @@ namespace Mantid */ bool CICatHelper::isvalidSession() { - ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); ns1__isSessionValid request; ns1__isSessionValidResponse response; @@ -1029,22 +903,15 @@ namespace Mantid */ void CICatHelper::doLogin(const std::string& name,const std::string& password,const std::string & url) { - UNUSED_ARG(url) - + // Store the soap end-point in the session for use later. + ICat::Session::Instance().setSoapEndPoint(url); + + // Obtain the ICAT proxy that has been securely set, including soap-endpoint. ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); + + // Output the soap end-point in use for debugging purposes. + g_log.debug() << "The ICAT soap end-point is: " << icat.soap_endpoint << "\n"; // CatalogLogin to icat ns1__login login; @@ -1075,21 +942,8 @@ namespace Mantid void CICatHelper::getdownloadURL(const long long& fileId,std::string& url) { - ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } + setICATProxySettings(icat); ns1__downloadDatafile request; @@ -1120,22 +974,9 @@ namespace Mantid void CICatHelper::getlocationString(const long long& fileid,std::string& filelocation) { - ICATPortBindingProxy icat; - // Define ssl authentication scheme - if (soap_ssl_client_context(&icat, - SOAP_SSL_NO_AUTHENTICATION, /* use SOAP_SSL_DEFAULT in production code */ - NULL, /* keyfile: required only when client must authenticate to - server (see SSL docs on how to obtain this file) */ - NULL, /* password to read the keyfile */ - NULL, /* optional cacert file to store trusted certificates */ - NULL, /* optional capath to directory with trusted certificates */ - NULL /* if randfile!=NULL: use a file with random data to seed randomness */ - )) - { - CErrorHandling::throwErrorMessages(icat); - } - + setICATProxySettings(icat); + ns1__getDatafile request; boost::shared_ptrsessionId_sptr(new std::string); @@ -1157,7 +998,35 @@ namespace Mantid } } + /** + * Sets the soap-endpoint & SSL context for the given ICAT proxy. + */ + void CICatHelper::setICATProxySettings(ICat3::ICATPortBindingProxy& icat) + { + // Set the soap-endpoint of the catalog we want to use. + icat.soap_endpoint = ICat::Session::Instance().getSoapEndPoint().c_str(); + // Sets SSL authentication scheme + setSSLContext(icat); + } - + /** + * Defines the SSL authentication scheme. + * @param icat :: ICATPortBindingProxy object. + */ + void CICatHelper::setSSLContext(ICat3::ICATPortBindingProxy& icat) + { + if (soap_ssl_client_context(&icat, + SOAP_SSL_CLIENT, /* use SOAP_SSL_DEFAULT in production code */ + NULL, /* keyfile: required only when client must authenticate to + server (see SSL docs on how to obtain this file) */ + NULL, /* password to read the keyfile */ + NULL, /* optional cacert file to store trusted certificates */ + NULL, /* optional capath to directory with trusted certificates */ + NULL /* if randfile!=NULL: use a file with random data to seed randomness */ + )) + { + CErrorHandling::throwErrorMessages(icat); + } + } } } diff --git a/Code/Mantid/Framework/ICat/src/ICat4/ICat4Catalog.cpp b/Code/Mantid/Framework/ICat/src/ICat4/ICat4Catalog.cpp index cc77ba64e267..b2448ffc10da 100644 --- a/Code/Mantid/Framework/ICat/src/ICat4/ICat4Catalog.cpp +++ b/Code/Mantid/Framework/ICat/src/ICat4/ICat4Catalog.cpp @@ -28,11 +28,14 @@ namespace Mantid */ void ICat4Catalog::login(const std::string& username, const std::string& password, const std::string& url) { - UNUSED_ARG(url) + // Store the soap end-point in the session for use later. + ICat::Session::Instance().setSoapEndPoint(url); + // Securely set, including soap-endpoint. ICat4::ICATPortBindingProxy icat; + setICATProxySettings(icat); - // Define ssl authentication scheme - setSSLContext(icat); + // Output the soap end-point in use for debugging purposes. + g_log.debug() << "The ICAT soap end-point is: " << icat.soap_endpoint << "\n"; // Used to authenticate the user. ns1__login login; @@ -87,7 +90,7 @@ namespace Mantid void ICat4Catalog::logout() { ICat4::ICATPortBindingProxy icat; - setSSLContext(icat); + setICATProxySettings(icat); ns1__logout request; ns1__logoutResponse response; @@ -258,7 +261,7 @@ namespace Mantid g_log.debug() << "ICat4Catalog::search -> Query is: { " << query << " }" << std::endl; ICat4::ICATPortBindingProxy icat; - setSSLContext(icat); + setICATProxySettings(icat); ns1__search request; ns1__searchResponse response; @@ -286,7 +289,7 @@ namespace Mantid int64_t ICat4Catalog::getNumberOfSearchResults(const CatalogSearchParam& inputs) { ICat4::ICATPortBindingProxy icat; - setSSLContext(icat); + setICATProxySettings(icat); ns1__search request; ns1__searchResponse response; @@ -329,7 +332,7 @@ namespace Mantid void ICat4Catalog::myData(Mantid::API::ITableWorkspace_sptr& outputws) { ICat4::ICATPortBindingProxy icat; - setSSLContext(icat); + setICATProxySettings(icat); ns1__search request; ns1__searchResponse response; @@ -435,7 +438,7 @@ namespace Mantid void ICat4Catalog::getDataSets(const long long& investigationId, Mantid::API::ITableWorkspace_sptr& outputws) { ICat4::ICATPortBindingProxy icat; - setSSLContext(icat); + setICATProxySettings(icat); ns1__search request; ns1__searchResponse response; @@ -504,7 +507,7 @@ namespace Mantid void ICat4Catalog::getDataFiles(const long long& investigationId, Mantid::API::ITableWorkspace_sptr& outputws) { ICat4::ICATPortBindingProxy icat; - setSSLContext(icat); + setICATProxySettings(icat); ns1__search request; ns1__searchResponse response; @@ -582,7 +585,7 @@ namespace Mantid void ICat4Catalog::listInstruments(std::vector& instruments) { ICat4::ICATPortBindingProxy icat; - setSSLContext(icat); + setICATProxySettings(icat); ns1__search request; ns1__searchResponse response; @@ -623,7 +626,7 @@ namespace Mantid void ICat4Catalog::listInvestigationTypes(std::vector& invstTypes) { ICat4::ICATPortBindingProxy icat; - setSSLContext(icat); + setICATProxySettings(icat); ns1__search request; ns1__searchResponse response; @@ -665,7 +668,7 @@ namespace Mantid void ICat4Catalog::getFileLocation(const long long & fileID, std::string & fileLocation) { ICat4::ICATPortBindingProxy icat; - setSSLContext(icat); + setICATProxySettings(icat); ns1__get request; ns1__getResponse response; @@ -817,5 +820,15 @@ namespace Mantid return (dateTime.toFormattedString(format)); } + /** + * Sets the soap-endpoint & SSL context for the given ICAT proxy. + */ + void ICat4Catalog::setICATProxySettings(ICat4::ICATPortBindingProxy& icat) + { + // Set the soap-endpoint of the catalog we want to use. + icat.soap_endpoint = ICat::Session::Instance().getSoapEndPoint().c_str(); + // Sets SSL authentication scheme + setSSLContext(icat); + } } } diff --git a/Code/Mantid/Framework/ICat/test/ICatTestHelper.cpp b/Code/Mantid/Framework/ICat/test/ICatTestHelper.cpp index bd0060d3952b..598b9b408dd5 100644 --- a/Code/Mantid/Framework/ICat/test/ICatTestHelper.cpp +++ b/Code/Mantid/Framework/ICat/test/ICatTestHelper.cpp @@ -10,6 +10,8 @@ namespace ICatTestHelper loginobj.initialize(); loginobj.setPropertyValue("Username", "mantid_test"); loginobj.setPropertyValue("Password", "mantidtestuser"); + loginobj.setPropertyValue("FacilityName", "ISIS"); + loginobj.execute(); if (!loginobj.isExecuted()) { diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/BinaryFile.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/BinaryFile.h index c65ddb0d858b..d7c6ae05ab5a 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/BinaryFile.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/BinaryFile.h @@ -1,7 +1,6 @@ #ifndef BINARYFILE_H_ #define BINARYFILE_H_ -#include #include #include #include diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/BoundedValidator.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/BoundedValidator.h index e1f91a629b77..764ff9137b8e 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/BoundedValidator.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/BoundedValidator.h @@ -5,8 +5,6 @@ // Includes //---------------------------------------------------------------------- #include "MantidKernel/TypedValidator.h" -#include -#include #include @@ -60,6 +58,7 @@ class DLLExport BoundedValidator : public TypedValidator /** Constructor * @param lowerBound :: The lower bounding value * @param upperBound :: The upper bounding value + * @param exclusive :: make bounds exclusive (default inclusive) */ BoundedValidator(const TYPE lowerBound, const TYPE upperBound, bool exclusive=false) : TypedValidator(), diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Cache.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Cache.h index 8cdd78100749..9a232a419e2a 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Cache.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Cache.h @@ -8,8 +8,6 @@ #include "MantidKernel/DllConfig.h" #include "MantidKernel/MultiThreaded.h" -#include - namespace Mantid { namespace Kernel diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/DateAndTime.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/DateAndTime.h index 01274dbaa8fb..294f8a5d2376 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/DateAndTime.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/DateAndTime.h @@ -2,11 +2,8 @@ #define DATE_AND_TIME_H #include "MantidKernel/DllConfig.h" -#include "MantidKernel/Logger.h" -#include "MantidKernel/System.h" #include -#include -#include +#include namespace Mantid { @@ -16,6 +13,8 @@ namespace Kernel /// Durations and time intervals typedef boost::posix_time::time_duration time_duration; +// forward declaration +class Logger; //============================================================================================= /** Class for holding the date and time in Mantid. @@ -57,10 +56,7 @@ class MANTID_KERNEL_DLL DateAndTime std::string toISO8601String() const; /// Stream output operator - friend std::ostream& operator<< (std::ostream& stream, const DateAndTime & t) - { - stream << t.toSimpleString(); return stream; - } + friend MANTID_KERNEL_DLL std::ostream& operator<< (std::ostream& stream, const DateAndTime & t); void setToMaximum(); void setToMinimum(); @@ -199,11 +195,7 @@ class MANTID_KERNEL_DLL TimeInterval std::string end_str()const; /** Stream output operator */ - friend std::ostream& operator<<(std::ostream& s,const Mantid::Kernel::TimeInterval& t) - { - s << t.begin().toSimpleString() << " - " << t.end().toSimpleString(); - return s; - } + friend MANTID_KERNEL_DLL std::ostream& operator<<(std::ostream& s,const Mantid::Kernel::TimeInterval& t); private: /// begin diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/DynamicFactory.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/DynamicFactory.h index cf4ee91d3eb8..b90dc5d7464d 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/DynamicFactory.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/DynamicFactory.h @@ -19,7 +19,6 @@ // std #include -#include #include namespace Mantid diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/EnvironmentHistory.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/EnvironmentHistory.h index bd1051dd7321..95dd77b0d5de 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/EnvironmentHistory.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/EnvironmentHistory.h @@ -6,7 +6,6 @@ //---------------------------------------------------------------------- #include "MantidKernel/DllConfig.h" #include -#include namespace Mantid { diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/FileDescriptor.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/FileDescriptor.h index 1e94ba74e5d8..2408a7239449 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/FileDescriptor.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/FileDescriptor.h @@ -45,6 +45,8 @@ namespace Mantid static bool isAscii(const std::string & filename, const size_t nbytes=256); /// Returns true if the stream is considered ascii static bool isAscii(std::istream & data, const size_t nbytes=256); + /// Returns true if the file is considered ascii + static bool isAscii(FILE* file, const size_t nbytes=256); public: /// Constructor accepting a filename diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/FileValidator.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/FileValidator.h index bbe5b87c3e01..fde97345c355 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/FileValidator.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/FileValidator.h @@ -42,8 +42,7 @@ bool has_ending(const std::string &value, const std::string & ending); class MANTID_KERNEL_DLL FileValidator : public TypedValidator { public: - FileValidator(); - explicit FileValidator(const std::vector& extensions, bool testFileExists = true); + explicit FileValidator(const std::vector& extensions=std::vector(), bool testFileExists = true, bool testCanWrite=false); virtual ~FileValidator(); virtual std::set allowedValues() const; IValidator_sptr clone() const; @@ -52,7 +51,9 @@ class MANTID_KERNEL_DLL FileValidator : public TypedValidator /// The list of permitted extensions const std::set m_extensions; /// Flag indicating whether to test for existence of filename - bool m_fullTest; + bool m_testExist; + /// Flag indicating whether to test for the file being writable + bool m_testCanWrite; private: virtual std::string checkValidity(const std::string &value) const; diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Interpolation.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Interpolation.h index 97f554764c61..7178d164c0bb 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Interpolation.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Interpolation.h @@ -10,7 +10,6 @@ #include "MantidKernel/Logger.h" #include "MantidKernel/DateAndTime.h" #include "MantidKernel/Unit.h" -#include #include #include #include diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyHistory.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyHistory.h index e52cbf908512..8037b60282cb 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyHistory.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyHistory.h @@ -6,7 +6,6 @@ //---------------------------------------------------------------------- #include "MantidKernel/DllConfig.h" #include -#include namespace Mantid { diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Quat.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Quat.h index 5beb760226bc..003a8c856505 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Quat.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Quat.h @@ -4,8 +4,6 @@ #include "MantidKernel/DllConfig.h" #include "MantidKernel/Matrix.h" #include "MantidKernel/Logger.h" -#include -#include namespace Mantid { diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/RegexStrings.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/RegexStrings.h index 2fea05434964..eb6f928616f6 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/RegexStrings.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/RegexStrings.h @@ -1,7 +1,7 @@ #ifndef REGEXSUPPORT_H #define REGEXSUPPORT_H -#include "MantidKernel/Strings.h" +#include "MantidKernel/DllConfig.h" #include namespace Mantid diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/SingletonHolder.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/SingletonHolder.h index 907ba6dca08a..19e7c0f728ac 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/SingletonHolder.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/SingletonHolder.h @@ -26,7 +26,6 @@ #include #include #include -#include #include namespace Mantid diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Strings.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Strings.h index bba48a111ce9..c9aa68e5f33e 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Strings.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Strings.h @@ -5,7 +5,7 @@ // Includes //---------------------------------------------------------------------- #include "MantidKernel/DllConfig.h" -#include +#include #include #include #include @@ -94,8 +94,12 @@ namespace Mantid MANTID_KERNEL_DLL void stripComment(std::string& A); /// Determines if a string is only spaces MANTID_KERNEL_DLL int isEmpty(const std::string& A); + /// Determines if a string starts with a # + MANTID_KERNEL_DLL bool skipLine(const std::string & line); /// Get a line and strip comments MANTID_KERNEL_DLL std::string getLine(std::istream& fh,const int spc = 256); + /// Peek at a line without extracting it from the stream + MANTID_KERNEL_DLL std::string peekLine(std::istream & fh); /// get a part of a long line MANTID_KERNEL_DLL int getPartLine(std::istream& fh,std::string& Out,std::string& Excess,const int spc = 256); diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Task.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Task.h index 7510a502883f..005019d23a97 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/Task.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/Task.h @@ -1,6 +1,7 @@ #ifndef MANTID_KERNEL_TASK_H_ #define MANTID_KERNEL_TASK_H_ +#include #include "MantidKernel/DllConfig.h" #include "MantidKernel/Exception.h" #include "MantidKernel/MultiThreaded.h" @@ -27,7 +28,7 @@ namespace Kernel //--------------------------------------------------------------------------------------------- /** Default constructor */ Task() : - m_cost(1.0), m_mutex(NULL) + m_cost(1.0) { } //--------------------------------------------------------------------------------------------- @@ -36,7 +37,7 @@ namespace Kernel * @param cost :: computational cost */ Task(double cost) : - m_cost(cost), m_mutex(NULL) + m_cost(cost) { } /// Destructor @@ -73,7 +74,7 @@ namespace Kernel /** Get the mutex object for this Task * @return Mutex pointer, or NULL */ - Mutex * getMutex() + boost::shared_ptr getMutex() { return m_mutex; } @@ -82,7 +83,7 @@ namespace Kernel /** Set the mutex object for this Task * @param mutex :: Mutex pointer, or NULL */ - void setMutex(Mutex * mutex) + void setMutex( boost::shared_ptr &mutex) { m_mutex = mutex; } @@ -94,7 +95,7 @@ namespace Kernel double m_cost; /// Mutex associated with this task (can be NULL) - Mutex * m_mutex; + boost::shared_ptr m_mutex; }; diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/ThreadSchedulerMutexes.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/ThreadSchedulerMutexes.h index fe0b452f2723..d9257d81bfae 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/ThreadSchedulerMutexes.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/ThreadSchedulerMutexes.h @@ -45,7 +45,7 @@ namespace Kernel m_queueLock.lock(); m_cost += newTask->cost(); - Mutex * mut = newTask->getMutex(); + boost::shared_ptr mut = newTask->getMutex(); m_supermap[mut].insert( std::pair(newTask->cost(), newTask) ); m_queueLock.unlock(); } @@ -68,7 +68,7 @@ namespace Kernel for (; it != it_end; ++it) { // The key is the mutex associated with the inner map - Mutex * mapMutex = it->first; + boost::shared_ptr mapMutex = it->first; if ((!mapMutex) || (m_mutexes.empty()) || (m_mutexes.find(mapMutex) == m_mutexes.end())) { // The mutex of this map is free! @@ -112,7 +112,7 @@ namespace Kernel // --- Add the mutex (if any) to the list of "busy" ones --- if (temp) { - Mutex * mut = temp->getMutex(); + boost::shared_ptr mut = temp->getMutex(); if (mut) m_mutexes.insert(mut); } @@ -134,7 +134,7 @@ namespace Kernel virtual void finished(Task * task, size_t threadnum) { UNUSED_ARG(threadnum); - Mutex * mut = task->getMutex(); + boost::shared_ptr mut = task->getMutex(); if (mut) { m_queueLock.lock(); @@ -199,14 +199,14 @@ namespace Kernel /// Map to tasks, sorted by cost typedef std::multimap InnerMap; /// Map to maps, sorted by Mutex* - typedef std::map SuperMap; + typedef std::map, InnerMap> SuperMap; /** A super map; first key = a Mutex * * Inside it: second key = the cost. */ SuperMap m_supermap; /// Vector of currently used mutexes. - std::set m_mutexes; + std::set > m_mutexes; }; diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/UserStringParser.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/UserStringParser.h index 79007297dfb5..6fbb28e1d3d2 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/UserStringParser.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/UserStringParser.h @@ -9,7 +9,6 @@ #include #include -#include #include namespace Mantid { @@ -89,4 +88,4 @@ namespace Kernel } } -#endif \ No newline at end of file +#endif diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/V3D.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/V3D.h index 7b80c2419f26..75c2f53a9c76 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/V3D.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/V3D.h @@ -54,6 +54,10 @@ namespace Mantid V3D operator+(const V3D& v) const; V3D& operator+=(const V3D& v); + /// Convenience method for sorting list of V3D objects based on magnitude + static bool CompareMagnitude( const Kernel::V3D & v1, + const Kernel::V3D & v2 ); + // explicit conversion into vector operator std::vector()const{std::vector tmp(3); tmp[0]=x;tmp[1]=y; tmp[2]=z;return tmp;} diff --git a/Code/Mantid/Framework/Kernel/src/DateAndTime.cpp b/Code/Mantid/Framework/Kernel/src/DateAndTime.cpp index 5b21f8b1454e..0284d1c7da29 100644 --- a/Code/Mantid/Framework/Kernel/src/DateAndTime.cpp +++ b/Code/Mantid/Framework/Kernel/src/DateAndTime.cpp @@ -1,9 +1,12 @@ #include "MantidKernel/DateAndTime.h" +#include "MantidKernel/Logger.h" #include #include #include #include #include +#include +#include namespace Mantid { @@ -968,19 +971,10 @@ bool DateAndTime::stringIsISO8601(const std::string & str) return Poco::DateTimeParser::tryParse(Poco::DateTimeFormat::ISO8601_FORMAT, str, dt, tz_diff); } - - - - - - - - - - - - - +std::ostream& operator<< (std::ostream& stream, const DateAndTime & t) +{ + stream << t.toSimpleString(); return stream; +} TimeInterval::TimeInterval(const DateAndTime& from, const DateAndTime& to) :m_begin(from) @@ -1021,6 +1015,11 @@ std::string TimeInterval::end_str()const return boost::posix_time::to_simple_string(this->m_end.to_ptime()); } +std::ostream& operator<<(std::ostream& s,const Mantid::Kernel::TimeInterval& t) +{ + s << t.begin().toSimpleString() << " - " << t.end().toSimpleString(); + return s; +} } // namespace Kernel diff --git a/Code/Mantid/Framework/Kernel/src/DirectoryValidator.cpp b/Code/Mantid/Framework/Kernel/src/DirectoryValidator.cpp index 5e6847dd2b82..33c86eb18035 100644 --- a/Code/Mantid/Framework/Kernel/src/DirectoryValidator.cpp +++ b/Code/Mantid/Framework/Kernel/src/DirectoryValidator.cpp @@ -18,7 +18,7 @@ Logger& DirectoryValidator::g_log = Logger::get("DirectoryValidator"); DirectoryValidator::DirectoryValidator(bool testDirectoryExists) : FileValidator() { - this->m_fullTest = testDirectoryExists; + this->m_testExist = testDirectoryExists; } /// Destructor @@ -52,7 +52,7 @@ std::string DirectoryValidator::checkValidity(const std::string& value) const } //If the path is required to exist check it is there - if ( m_fullTest ) + if ( m_testExist ) { if ( value.empty() || !Poco::File(value).exists() ) return "Directory \"" + value + "\" not found"; diff --git a/Code/Mantid/Framework/Kernel/src/EnvironmentHistory.cpp b/Code/Mantid/Framework/Kernel/src/EnvironmentHistory.cpp index 655db192c91d..dcc2e5cb8109 100644 --- a/Code/Mantid/Framework/Kernel/src/EnvironmentHistory.cpp +++ b/Code/Mantid/Framework/Kernel/src/EnvironmentHistory.cpp @@ -4,6 +4,7 @@ #include "MantidKernel/EnvironmentHistory.h" #include "MantidKernel/ConfigService.h" #include "MantidKernel/MantidVersion.h" +#include namespace Mantid { diff --git a/Code/Mantid/Framework/Kernel/src/FileDescriptor.cpp b/Code/Mantid/Framework/Kernel/src/FileDescriptor.cpp index 06be8ab9e2a8..9282ddf29fb3 100644 --- a/Code/Mantid/Framework/Kernel/src/FileDescriptor.cpp +++ b/Code/Mantid/Framework/Kernel/src/FileDescriptor.cpp @@ -16,7 +16,7 @@ namespace Mantid * first nbytes of the file and returns false if a non-ascii character is found. * If the file is shorter than nbytes then it checks until the end of the stream. * @param filename A string pointing to an existing file - * @param bytes The number of bytes of the file to check (Default=256) + * @param nbytes The number of bytes of the file to check (Default=256) * @returns True if the file is considered ASCII, false otherwise * @throws std::invalid_argument if the file cannot be opened * @throws std::runtime_error if an error is occurred while reading the stream @@ -39,7 +39,7 @@ namespace Mantid * the result up to that point * The stream is reset to the position is was at when entering the function * @param data An input stream opened in binary mode - * @param bytes The number of bytes of the file to check (Default=256) + * @param nbytes The number of bytes of the file to check (Default=256) * @returns True if the stream is considered ASCII, false otherwise */ bool FileDescriptor::isAscii(std::istream & data, const size_t nbytes) @@ -69,6 +69,37 @@ namespace Mantid return result; } + /** + * Check if a file is a text file + * @param file :: The file pointer + * @param nbytes The number of bytes of the file to check (Default=256) + * @returns true if the file an ascii text file, false otherwise + */ + bool FileDescriptor::isAscii(FILE* file, const size_t nbytes) + { + // read the data and reset the seek index back to the beginning + char *data = new char[nbytes]; + char *pend = &data[fread(data, 1, nbytes, file)]; + fseek(file,0,SEEK_SET); + + // Call it a binary file if we find a non-ascii character in the + // first nbytes bytes of the file. + bool result = true; + for( char *p = data; p < pend; ++p ) + { + unsigned long ch = (unsigned long)*p; + if( !(ch <= 0x7F) ) + { + result = false; + break; + } + + } + delete[] data; + + return result; + } + //---------------------------------------------------------------------------------------------- // Public methods //---------------------------------------------------------------------------------------------- diff --git a/Code/Mantid/Framework/Kernel/src/FileValidator.cpp b/Code/Mantid/Framework/Kernel/src/FileValidator.cpp index 8e90d930e212..e4648eb82273 100644 --- a/Code/Mantid/Framework/Kernel/src/FileValidator.cpp +++ b/Code/Mantid/Framework/Kernel/src/FileValidator.cpp @@ -25,18 +25,17 @@ namespace Kernel // Initialize the logger Logger& FileValidator::g_log = Logger::get("FileValidator"); -/// Default constructor. -FileValidator::FileValidator() : TypedValidator(), m_extensions(), m_fullTest(true) -{} - /** Constructor * @param extensions :: The permitted file extensions (e.g. .RAW) * @param testFileExists :: Flag indicating whether to test for existence of file (default: yes) + * @param testCanWrite :: Flag to check if file writing permissible. */ -FileValidator::FileValidator(const std::vector& extensions, bool testFileExists) : +FileValidator::FileValidator(const std::vector& extensions, bool testFileExists, + bool testCanWrite) : TypedValidator(), m_extensions(extensions.begin(),extensions.end()), - m_fullTest(testFileExists) + m_testExist(testFileExists), + m_testCanWrite(testCanWrite) { for_each(m_extensions.begin(), m_extensions.end(), lowercase()); } @@ -88,10 +87,74 @@ std::string FileValidator::checkValidity(const std::string &value) const } } + // create a variable for the absolute path to be used in error messages + std::string abspath(value); + if (!value.empty()) + { + Poco::Path path(value); + if (path.isAbsolute()) + abspath = path.toString(); + } + //If the file is required to exist check it is there - if ( m_fullTest && ( value.empty() || !Poco::File(value).exists() ) ) + if ( m_testExist && ( value.empty() || !Poco::File(value).exists() ) ) { - return "File \"" + Poco::Path(value).getFileName() + "\" not found"; + return "File \"" + abspath + "\" not found"; + } + + //If the file is required to be writable... + if (m_testCanWrite) + { + if (value.empty()) + return "Cannot write to empty filename"; + + Poco::File file(value); + // the check for writable is different for whether or not a version exists + // this is taken from ConfigService near line 443 + if (file.exists()) + { + try + { + if (!file.canWrite()) + return "File \"" + abspath + "\" cannot be written"; + } + catch (std::exception &e) + { + g_log.information() << "Encountered exception while checking for writable: " << e.what(); + } + } + else // if the file doesn't exist try to temporarily create one + { + try + { + Poco::Path direc(value); + if (direc.isAbsolute()) + { + // look for an existing parent + while (!Poco::File(direc).exists()) + { + direc = direc.parent(); + } + + // see if the directory exists + Poco::File direcFile(direc); + if (direcFile.exists() && direcFile.isDirectory()) + { + if (direcFile.canWrite()) + return ""; + else + return "Cannot write to directory \"" + direc.toString() + "\""; + } + } + + g_log.debug() << "Do not have enough information to validate \"" + << abspath << "\"\n"; + } + catch (std::exception &e) + { + g_log.information() << "Encountered exception while checking for writable: " << e.what(); + } + } } //Otherwise we are okay, file extensions are just a suggestion so no validation on them is necessary diff --git a/Code/Mantid/Framework/Kernel/src/Logger.cpp b/Code/Mantid/Framework/Kernel/src/Logger.cpp index baaa7f8ccf07..3feecbef3d59 100644 --- a/Code/Mantid/Framework/Kernel/src/Logger.cpp +++ b/Code/Mantid/Framework/Kernel/src/Logger.cpp @@ -450,8 +450,8 @@ namespace Kernel /** * Log a given message at a given priority - * @param message :: The message to log * @param priority :: The priority level + * @return :: the stream */ std::ostream& Logger::getLogStream(Logger::Priority priority) { diff --git a/Code/Mantid/Framework/Kernel/src/NeutronAtom.cpp b/Code/Mantid/Framework/Kernel/src/NeutronAtom.cpp index 4b06ccb245a3..546037af0a7a 100644 --- a/Code/Mantid/Framework/Kernel/src/NeutronAtom.cpp +++ b/Code/Mantid/Framework/Kernel/src/NeutronAtom.cpp @@ -663,7 +663,7 @@ NeutronAtom operator*(const NeutronAtom& left, const double right) } /** - * This calls @link operator*(const NeutronAtom&, const double) + * This calls @link operator*(const NeutronAtom&, const double) @endlink * with the parameters reversed. */ NeutronAtom operator*(const double left, const NeutronAtom& right) diff --git a/Code/Mantid/Framework/Kernel/src/NexusDescriptor.cpp b/Code/Mantid/Framework/Kernel/src/NexusDescriptor.cpp index c49c01a698ac..a9afc0ba829f 100644 --- a/Code/Mantid/Framework/Kernel/src/NexusDescriptor.cpp +++ b/Code/Mantid/Framework/Kernel/src/NexusDescriptor.cpp @@ -220,7 +220,6 @@ namespace Mantid * @param file An open NeXus File object * @param rootPath The current path that is open in the file * @param className The class of the current open path - * @param tmap [Out] An output map filled with mappings of type->path * @param pmap [Out] An output map filled with mappings of path->type * @param level An integer defining the current level in the file */ diff --git a/Code/Mantid/Framework/Kernel/src/Property.cpp b/Code/Mantid/Framework/Kernel/src/Property.cpp index 81806bda8bdc..718be81a6f5c 100644 --- a/Code/Mantid/Framework/Kernel/src/Property.cpp +++ b/Code/Mantid/Framework/Kernel/src/Property.cpp @@ -223,11 +223,13 @@ void Property::splitByTime(TimeSplitterType& splitter, std::vector< Property * > namespace API { class Workspace; + class WorkspaceGroup; class MatrixWorkspace; class ITableWorkspace; class IMDEventWorkspace; class IMDWorkspace; class IEventWorkspace; + class IPeaksWorkspace; class IMDHistoWorkspace; class IFunction; class IAlgorithm; @@ -297,8 +299,12 @@ std::string getUnmangledTypeName(const std::type_info& type) string("EventWorkspace"))); typestrings.insert(make_pair(typeid(boost::shared_ptr).name(), string("PeaksWorkspace"))); + typestrings.insert(make_pair(typeid(boost::shared_ptr).name(), + string("IPeaksWorkspace"))); typestrings.insert(make_pair(typeid(boost::shared_ptr).name(), string("GroupingWorkspace"))); + typestrings.insert(make_pair(typeid(boost::shared_ptr).name(), + string("WorkspaceGroup"))); typestrings.insert(make_pair(typeid(boost::shared_ptr).name(), string("OffsetsWorkspace"))); typestrings.insert(make_pair(typeid(boost::shared_ptr).name(), diff --git a/Code/Mantid/Framework/Kernel/src/RegexStrings.cpp b/Code/Mantid/Framework/Kernel/src/RegexStrings.cpp index 5f585ed622ef..bcb1dad94785 100644 --- a/Code/Mantid/Framework/Kernel/src/RegexStrings.cpp +++ b/Code/Mantid/Framework/Kernel/src/RegexStrings.cpp @@ -9,6 +9,7 @@ #include #include "MantidKernel/RegexStrings.h" +#include "MantidKernel/Strings.h" namespace Mantid { diff --git a/Code/Mantid/Framework/Kernel/src/Strings.cpp b/Code/Mantid/Framework/Kernel/src/Strings.cpp index 2455fdbdba07..0e373b5b76f2 100644 --- a/Code/Mantid/Framework/Kernel/src/Strings.cpp +++ b/Code/Mantid/Framework/Kernel/src/Strings.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -11,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -330,6 +330,19 @@ namespace Mantid return Line; } + /** + * Peek at a line without extracting it from the stream + */ + std::string peekLine(std::istream & fh) + { + std::string str; + std::streampos pos = fh.tellg(); + getline(fh, str); + fh.seekg(pos); + + return strip(str); + } + //------------------------------------------------------------------------------------------------ /** * Determines if a string is only spaces @@ -383,13 +396,21 @@ namespace Mantid */ std::string strip(const std::string& A) { - std::string::size_type posA=A.find_first_not_of(" "); - std::string::size_type posB=A.find_last_not_of(" "); - if (posA==std::string::npos) - return ""; - return A.substr(posA,1+posB-posA); + std::string result(A); + boost::trim(result); + return result; } + /** + * Return true if the line is to be skipped (starts with #). + * @param line :: The line to be checked + * @return True if the line should be skipped + */ + bool skipLine(const std::string & line) + { + // Empty or comment + return ( line.empty() || boost::starts_with(line, "#") ); + } //------------------------------------------------------------------------------------------------ /** diff --git a/Code/Mantid/Framework/Kernel/src/ThreadPoolRunnable.cpp b/Code/Mantid/Framework/Kernel/src/ThreadPoolRunnable.cpp index f7864d35c234..472c33b2f0ba 100644 --- a/Code/Mantid/Framework/Kernel/src/ThreadPoolRunnable.cpp +++ b/Code/Mantid/Framework/Kernel/src/ThreadPoolRunnable.cpp @@ -64,8 +64,8 @@ namespace Kernel if (task) { //Task-specific mutex if specified? - Mutex * mutex = task->getMutex(); - if (mutex) + boost::shared_ptr mutex = task->getMutex(); + if (bool(mutex)) mutex->lock(); try diff --git a/Code/Mantid/Framework/Kernel/src/UnitConversion.cpp b/Code/Mantid/Framework/Kernel/src/UnitConversion.cpp index 3d27082af192..4b659424232c 100644 --- a/Code/Mantid/Framework/Kernel/src/UnitConversion.cpp +++ b/Code/Mantid/Framework/Kernel/src/UnitConversion.cpp @@ -62,13 +62,7 @@ namespace Mantid /** * Convert a single value between the given units (overload for Unit objects) - * @param srcUnit :: The starting unit - * @param destUnit :: The destination unit - * @param srcValue :: The value to convert - * @param l1 :: The source-sample distance (in metres) - * @param l2 :: The sample-detector distance (in metres) * @param twoTheta :: The scattering angle (in radians) - * @param emode :: The energy mode enumeration * @param efixed :: Value of fixed energy: EI (emode=1) or EF (emode=2) (in meV) * @return The value converted to the destination unit */ diff --git a/Code/Mantid/Framework/Kernel/src/V3D.cpp b/Code/Mantid/Framework/Kernel/src/V3D.cpp index 3d042bcbc522..d86f9d42c2c6 100644 --- a/Code/Mantid/Framework/Kernel/src/V3D.cpp +++ b/Code/Mantid/Framework/Kernel/src/V3D.cpp @@ -864,6 +864,19 @@ double V3D::toMillerIndexes(double eps) } +/** + Comparator function for sorting list of 3D vectors based on their magnitude. + @param v1 first vector + @param v2 seconde vector + @return true if v1.norm() < v2.norm(). + */ +bool V3D::CompareMagnitude( const V3D & v1, const V3D & v2 ) +{ + double mag_sq_1 = v1[0]*v1[0] + v1[1]*v1[1] + v1[2]*v1[2]; + double mag_sq_2 = v2[0]*v2[0] + v2[1]*v2[1] + v2[2]*v2[2]; + return (mag_sq_1 < mag_sq_2); +} + } // Namespace Kernel } // Namespace Mantid diff --git a/Code/Mantid/Framework/Kernel/test/FileDescriptorTest.h b/Code/Mantid/Framework/Kernel/test/FileDescriptorTest.h index d5874df2c042..9e4583858951 100644 --- a/Code/Mantid/Framework/Kernel/test/FileDescriptorTest.h +++ b/Code/Mantid/Framework/Kernel/test/FileDescriptorTest.h @@ -2,6 +2,7 @@ #define MANTID_KERNEL_FILEDESCRIPTORTEST_H_ #include +#include #include "MantidKernel/FileDescriptor.h" #include "MantidKernel/ConfigService.h" @@ -80,6 +81,27 @@ class FileDescriptorTest : public CxxTest::TestSuite TS_ASSERT_EQUALS(1, is.tellg()); } + void test_isAscii_Returns_True_For_C_Handle() + { + FILE* handle = fopen(m_testAsciiPath.c_str(), "r"); + if (handle) + { + TS_ASSERT(FileDescriptor::isAscii(handle)); + TS_ASSERT_EQUALS(0, ftell(handle)); + fclose(handle); + } + } + + void test_isAscii_Returns_False_For_C_Handle() + { + FILE* handle = fopen(m_testNonNexusPath.c_str(), "r"); + if (handle) + { + TS_ASSERT(!FileDescriptor::isAscii(handle)); + TS_ASSERT_EQUALS(0, ftell(handle)); + fclose(handle); + } + } void test_Constructor_With_Existing_File_Initializes_Description_Fields() { diff --git a/Code/Mantid/Framework/Kernel/test/FileValidatorTest.h b/Code/Mantid/Framework/Kernel/test/FileValidatorTest.h index 9dc4be3bf6b1..b3d20aa48e8d 100644 --- a/Code/Mantid/Framework/Kernel/test/FileValidatorTest.h +++ b/Code/Mantid/Framework/Kernel/test/FileValidatorTest.h @@ -12,13 +12,18 @@ class FileValidatorTest: public CxxTest::TestSuite { public: -void testVectorConstructor() +void testConstructors() { + // empty constructor + FileValidator v1; + TS_ASSERT_EQUALS(v1.allowedValues().size(), 0 ); + + // one with a vector of extensions std::vector vec; vec.push_back("raw"); vec.push_back("RAW"); - FileValidator v(vec); - TS_ASSERT_EQUALS ( v.allowedValues().size(), 2 ); + FileValidator v2(vec); + TS_ASSERT_EQUALS ( v2.allowedValues().size(), 2 ); } void testPassesOnExistentFile() @@ -109,6 +114,21 @@ void testFailsOnEmptyFileString() TS_ASSERT_EQUALS( file_val.isValid(""), "File \"\" not found" ); } +void testCanWrite() +{ + std::string filename("myJunkFile_hgfvj.cpp"); + + // file existance is optional + FileValidator v1(std::vector(), false, true); + TS_ASSERT_EQUALS( v1.isValid(""), "Cannot write to empty filename" ); + TS_ASSERT_EQUALS( v1.isValid(filename), ""); + + // file existance is required + FileValidator v2(std::vector(), true, true) ; + TS_ASSERT_EQUALS( v2.isValid(""), "File \"\" not found" ); + TS_ASSERT_EQUALS( v2.isValid(filename), "File \"" + filename + "\" not found"); +} + }; #endif /*FILEVALIDATORTEST_H_*/ diff --git a/Code/Mantid/Framework/Kernel/test/StringsTest.h b/Code/Mantid/Framework/Kernel/test/StringsTest.h index c38acada8733..c95e96aa713f 100644 --- a/Code/Mantid/Framework/Kernel/test/StringsTest.h +++ b/Code/Mantid/Framework/Kernel/test/StringsTest.h @@ -120,6 +120,29 @@ class StringsTest : public CxxTest::TestSuite TSM_ASSERT_THROWS(" this path should go out of range",split_path("/aaaa\\bbbbb/../../../",result),std::invalid_argument); } + void testSkipLine() + { + TS_ASSERT(skipLine("#blah blah")); + TS_ASSERT(!skipLine("blah blah")); + } + void testpeekLine() + { + std::istringstream text1("blah blah\nfoo bar\n"); + TS_ASSERT(peekLine(text1) == "blah blah"); + TS_ASSERT(peekLine(text1) == "blah blah"); + + std::istringstream text2("\tblah blah \nfoo bar\n"); + TS_ASSERT(peekLine(text2) == "blah blah"); + TS_ASSERT(peekLine(text2) == "blah blah"); + } + void testStrip() + { + TS_ASSERT(strip("blah") == "blah"); + TS_ASSERT(strip(" blah") == "blah"); + TS_ASSERT(strip("\nblah \t ") == "blah"); + TS_ASSERT(strip("\tblah\t\n") == "blah"); + } + void testExtractWord() /** Applies a test to the extractWord diff --git a/Code/Mantid/Framework/Kernel/test/TaskTest.h b/Code/Mantid/Framework/Kernel/test/TaskTest.h index 8501325ef2bb..2ff51b38bcac 100644 --- a/Code/Mantid/Framework/Kernel/test/TaskTest.h +++ b/Code/Mantid/Framework/Kernel/test/TaskTest.h @@ -43,10 +43,9 @@ class TaskTest : public CxxTest::TestSuite void test_mutex() { MyTask t; - Mutex * mut = new Mutex(); + boost::shared_ptr mut(new Mutex()); t.setMutex(mut); TS_ASSERT_EQUALS( mut, t.getMutex() ); - delete mut; } diff --git a/Code/Mantid/Framework/Kernel/test/ThreadPoolTest.h b/Code/Mantid/Framework/Kernel/test/ThreadPoolTest.h index 5b1b174c630c..cac3e1e365f9 100644 --- a/Code/Mantid/Framework/Kernel/test/ThreadPoolTest.h +++ b/Code/Mantid/Framework/Kernel/test/ThreadPoolTest.h @@ -12,6 +12,7 @@ #include "MantidKernel/ThreadSchedulerMutexes.h" #include +#include #include #include #include @@ -382,13 +383,13 @@ class ThreadPoolTest : public CxxTest::TestSuite TimeWaster mywaster; size_t num = 30000; mywaster.total = 0; - Mutex * lastMutex = NULL; + boost::shared_ptr lastMutex; for (size_t i=0; i<=num; i++) { Task * task = new FunctionTask( boost::bind(&TimeWaster::add_to_number, &mywaster, i), static_cast(i) ); // Create a new mutex every 1000 tasks. This is more relevant to the ThreadSchedulerMutexes; others ignore it. if (i % 1000 == 0) - lastMutex = new Mutex(); + lastMutex = boost::make_shared(); task->setMutex(lastMutex); p.schedule( task ); } diff --git a/Code/Mantid/Framework/Kernel/test/ThreadSchedulerMutexesTest.h b/Code/Mantid/Framework/Kernel/test/ThreadSchedulerMutexesTest.h index 19e78577ff7e..ade9d21c9f25 100644 --- a/Code/Mantid/Framework/Kernel/test/ThreadSchedulerMutexesTest.h +++ b/Code/Mantid/Framework/Kernel/test/ThreadSchedulerMutexesTest.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -22,7 +23,7 @@ class ThreadSchedulerMutexesTest : public CxxTest::TestSuite class TaskWithMutex : public Task { public: - TaskWithMutex(Mutex * mutex, double cost) + TaskWithMutex(boost::shared_ptr mutex, double cost) { m_mutex = mutex; m_cost = cost; @@ -44,8 +45,8 @@ class ThreadSchedulerMutexesTest : public CxxTest::TestSuite void test_push() { ThreadSchedulerMutexes sc; - Mutex * mut1 = new Mutex(); - Mutex * mut2 = new Mutex(); + auto mut1 = boost::make_shared(); + auto mut2 = boost::make_shared(); TaskWithMutex * task1 = new TaskWithMutex(mut1, 10.0); TaskWithMutex * task2 = new TaskWithMutex(mut2, 9.0); @@ -53,26 +54,21 @@ class ThreadSchedulerMutexesTest : public CxxTest::TestSuite TS_ASSERT_EQUALS( sc.size(), 1); sc.push( task2 ); TS_ASSERT_EQUALS( sc.size(), 2); - -// delete task1; -// delete task2; - delete mut1; - delete mut2; } void test_queue() { ThreadSchedulerMutexes sc; - Mutex * mut1 = new Mutex(); - Mutex * mut2 = new Mutex(); - Mutex * mut3 = new Mutex(); + auto mut1 = boost::make_shared(); + auto mut2 = boost::make_shared(); + auto mut3 = boost::make_shared(); TaskWithMutex * task1 = new TaskWithMutex(mut1, 10.0); TaskWithMutex * task2 = new TaskWithMutex(mut1, 9.0); TaskWithMutex * task3 = new TaskWithMutex(mut1, 8.0); TaskWithMutex * task4 = new TaskWithMutex(mut2, 7.0); TaskWithMutex * task5 = new TaskWithMutex(mut2, 6.0); TaskWithMutex * task6 = new TaskWithMutex(mut3, 5.0); - TaskWithMutex * task7 = new TaskWithMutex(NULL, 4.0); + TaskWithMutex * task7 = new TaskWithMutex(boost::shared_ptr(), 4.0); sc.push(task1); sc.push(task2); sc.push(task3); @@ -122,17 +118,6 @@ class ThreadSchedulerMutexesTest : public CxxTest::TestSuite TS_ASSERT_EQUALS( task, task5 ); TS_ASSERT_EQUALS( sc.size(), 0 ); // (for this task, the thread pool would have to wait till the mutex is released) - -// delete task1; -// delete task2; -// delete task3; -// delete task4; -// delete task5; -// delete task6; -// delete task7; - delete mut1; - delete mut2; - delete mut3; } void test_clear() @@ -140,7 +125,7 @@ class ThreadSchedulerMutexesTest : public CxxTest::TestSuite ThreadSchedulerMutexes sc; for (size_t i=0; i<10; i++) { - TaskWithMutex * task = new TaskWithMutex(new Mutex(), 10.0); + TaskWithMutex * task = new TaskWithMutex(boost::make_shared(), 10.0); sc.push(task); } TS_ASSERT_EQUALS(sc.size(), 10); @@ -155,7 +140,7 @@ class ThreadSchedulerMutexesTest : public CxxTest::TestSuite { ThreadSchedulerMutexes sc; Timer tim0; - Mutex * mut1 = new Mutex(); + auto mut1 = boost::make_shared(); size_t num = 500; for (size_t i=0; i < num; i++) { @@ -171,8 +156,6 @@ class ThreadSchedulerMutexesTest : public CxxTest::TestSuite } //std::cout << tim1.elapsed() << " secs to pop." << std::endl; TS_ASSERT_EQUALS( sc.size(), 0); - - delete mut1; } void test_performance_lotsOfMutexes() @@ -182,7 +165,7 @@ class ThreadSchedulerMutexesTest : public CxxTest::TestSuite size_t num = 500; for (size_t i=0; i < num; i++) { - sc.push(new TaskWithMutex(new Mutex(), 10.0)); + sc.push(new TaskWithMutex(boost::make_shared(), 10.0)); } //std::cout << tim0.elapsed() << " secs to push." << std::endl; TS_ASSERT_EQUALS( sc.size(), num); diff --git a/Code/Mantid/Framework/LiveData/inc/MantidLiveData/ISISLiveEventDataListener.h b/Code/Mantid/Framework/LiveData/inc/MantidLiveData/ISISLiveEventDataListener.h index ec5631ccd653..69434c0ccc40 100644 --- a/Code/Mantid/Framework/LiveData/inc/MantidLiveData/ISISLiveEventDataListener.h +++ b/Code/Mantid/Framework/LiveData/inc/MantidLiveData/ISISLiveEventDataListener.h @@ -13,10 +13,14 @@ #include #include +// Time we'll wait on a receive call (in seconds) +const long RECV_TIMEOUT = 30; +// Sleep time in case we need to wait for the data to become available (in milliseconds) +const long RECV_WAIT = 100; + //---------------------------------------------------------------------- // Forward declarations //---------------------------------------------------------------------- - struct idc_info; typedef struct idc_info* idc_handle_t; @@ -127,7 +131,7 @@ namespace Mantid // Initialize the event buffer void initEventBuffer(const TCPStreamEventDataSetup& setup); // Save received event data in the buffer workspace - void saveEvents(const std::vector &data, const Kernel::DateAndTime &pulseTime, const size_t period); + void saveEvents(const std::vector &data, const Kernel::DateAndTime &pulseTime, size_t period); // Set the spectra-detector map void loadSpectraMap(); // Load the instrument @@ -137,6 +141,31 @@ namespace Mantid // Get an integer array ising the IDC interface void getIntArray(const std::string& par, std::vector& arr, const size_t dim); + // receive a header and check if it's valid + template + void Receive(T buffer, const std::string& head, const std::string &msg) + { + long timeout = 0; + while( m_socket.available() < static_cast(sizeof(buffer)) ) + { + Poco::Thread::sleep(RECV_WAIT); + timeout += RECV_WAIT; + if ( timeout > RECV_TIMEOUT * 1000 ) throw std::runtime_error("Operation of receiving " + head + " timed out."); + } + m_socket.receiveBytes(&buffer, sizeof(buffer)); + if ( !buffer.isValid() ) + { + throw std::runtime_error(msg); + } + } + + // receive data that cannot be processed + template + void CollectJunk(T head) + { + m_socket.receiveBytes(junk_buffer, head.length - static_cast(sizeof(head))); + } + /// The socket communicating with the DAE Poco::Net::StreamSocket m_socket; /// Keep connection status @@ -166,6 +195,9 @@ namespace Mantid /// number of spectra int m_numberOfSpectra; + /// buffer to collect data that cannot be processed + char* junk_buffer[1000]; + /// reference to the logger class static Kernel::Logger& g_log; diff --git a/Code/Mantid/Framework/LiveData/src/ISISLiveEventDataListener.cpp b/Code/Mantid/Framework/LiveData/src/ISISLiveEventDataListener.cpp index 3289f213f673..1d60def9e0f0 100644 --- a/Code/Mantid/Framework/LiveData/src/ISISLiveEventDataListener.cpp +++ b/Code/Mantid/Framework/LiveData/src/ISISLiveEventDataListener.cpp @@ -13,10 +13,8 @@ #include "LoadDAE/idc.h" -// Time we'll wait on a receive call (in seconds) -const long RECV_TIMEOUT = 30; -// Sleep time in case we need to wait for the data to become available (in milliseconds) -const long RECV_WAIT = 100; +const char* PROTON_CHARGE_PROPERTY = "proton_charge"; +const char* RUN_NUMBER_PROPERTY = "run_number"; namespace Mantid { @@ -25,34 +23,6 @@ namespace LiveData DECLARE_LISTENER(ISISLiveEventDataListener) -// receive a header and check if it's valid -#define RECEIVE(buffer,msg) \ -{\ - long timeout = 0;\ - while( m_socket.available() < static_cast(sizeof(buffer)) )\ - {\ - Poco::Thread::sleep(RECV_WAIT);\ - timeout += RECV_WAIT;\ - if ( timeout > RECV_TIMEOUT * 1000 ) throw std::runtime_error("Receive operation timed out.");\ - }\ - m_socket.receiveBytes(&buffer, sizeof(buffer));\ - if ( !buffer.isValid() )\ - {\ - throw std::runtime_error(msg);\ - }\ -} - -namespace { -// buffer to collect data that cannot be processed -static char* junk_buffer[10000]; -} - -// receive data that cannot be processed -#define COLLECT_JUNK(head) m_socket.receiveBytes(junk_buffer, head.length - static_cast(sizeof(head))); - -#define PROTON_CHARGE_PROPERTY "proton_charge" -#define RUN_NUMBER_PROPERTY "run_number" - // Get a reference to the logger Kernel::Logger& ISISLiveEventDataListener::g_log = Kernel::Logger::get("ISISLiveEventDataListener"); @@ -100,7 +70,6 @@ bool ISISLiveEventDataListener::connect(const Poco::Net::SocketAddress &address) if (address.host().toString().compare( "0.0.0.0") == 0) { Poco::Net::SocketAddress tempAddress("127.0.0.1:10000"); - //Poco::Net::SocketAddress tempAddress("NDXTESTFAA:10000"); try { m_socket.connect( tempAddress); // BLOCKING connect } catch (...) { @@ -141,11 +110,8 @@ bool ISISLiveEventDataListener::connect(const Poco::Net::SocketAddress &address) m_numberOfPeriods = getInt("NPER"); m_numberOfSpectra = getInt("NSP1"); - std::cerr << "number of periods " << m_numberOfPeriods << std::endl; - std::cerr << "number of spectra " << m_numberOfSpectra << std::endl; - TCPStreamEventDataSetup setup; - RECEIVE(setup,"Wrong version"); + Receive(setup, "Setup", "Wrong version"); m_startTime.set_from_time_t(setup.head_setup.start_time); // initialize the buffer workspace @@ -236,17 +202,17 @@ void ISISLiveEventDataListener::run() while (m_stopThread == false) { // get the header with the type of the packet - RECEIVE(events.head,"Corrupt stream - you should reconnect."); + Receive(events.head, "Events header","Corrupt stream - you should reconnect."); if ( !(events.head.type == TCPStreamEventHeader::Neutron) ) { // don't know what to do with it - stop throw std::runtime_error("Unknown packet type."); } - COLLECT_JUNK( events.head ); + CollectJunk( events.head ); // get the header with the sream size - RECEIVE(events.head_n,"Corrupt stream - you should reconnect."); - COLLECT_JUNK( events.head_n ); + Receive(events.head_n, "Neutrons header","Corrupt stream - you should reconnect."); + CollectJunk( events.head_n ); // absolute pulse (frame) time Mantid::Kernel::DateAndTime pulseTime = m_startTime + static_cast( events.head_n.frame_time_zero ); @@ -366,10 +332,15 @@ void ISISLiveEventDataListener::initEventBuffer(const TCPStreamEventDataSetup &s * Save received event data in the buffer workspace. * @param data :: A vector with events. */ -void ISISLiveEventDataListener::saveEvents(const std::vector &data, const Kernel::DateAndTime &pulseTime, const size_t period) +void ISISLiveEventDataListener::saveEvents(const std::vector &data, const Kernel::DateAndTime &pulseTime, size_t period) { Poco::ScopedLock scopedLock(m_mutex); + if ( period >= static_cast(m_numberOfPeriods) ) + { + period = 0; + } + for(auto it = data.begin(); it != data.end(); ++it) { Mantid::DataObjects::TofEvent event( it->time_of_flight, pulseTime ); @@ -408,17 +379,32 @@ void ISISLiveEventDataListener::loadSpectraMap() */ void ISISLiveEventDataListener::loadInstrument(const std::string &instrName) { - API::Algorithm_sptr alg = API::AlgorithmFactory::Instance().create("LoadInstrument",-1); - alg->initialize(); - alg->setPropertyValue("InstrumentName",instrName); - alg->setProperty("Workspace", m_eventBuffer[0]); - alg->setProperty("RewriteSpectraMap", false); - alg->setChild(true); - alg->execute(); - // check if the instrument was loaded - if ( !alg->isExecuted() ) + // try to load the instrument. if it doesn't load give a warning and carry on + if ( instrName.empty() ) + { + g_log.warning() << "Unable to read instrument name from DAE." << std::endl; + return; + } + const char *warningMessage = "Failed to load instrument "; + try + { + API::Algorithm_sptr alg = API::AlgorithmFactory::Instance().create("LoadInstrument",-1); + alg->initialize(); + alg->setPropertyValue("InstrumentName",instrName); + alg->setProperty("Workspace", m_eventBuffer[0]); + alg->setProperty("RewriteSpectraMap", false); + alg->setChild(true); + alg->execute(); + // check if the instrument was loaded + if ( !alg->isExecuted() ) + { + g_log.warning() << warningMessage << instrName << std::endl; + } + } + catch(std::exception& e) { - throw std::runtime_error("Failed to load instrument " + instrName); + g_log.warning() << warningMessage << instrName << std::endl; + g_log.warning() << e.what() << instrName << std::endl; } } diff --git a/Code/Mantid/Framework/LiveData/src/SNSLiveEventDataListener.cpp b/Code/Mantid/Framework/LiveData/src/SNSLiveEventDataListener.cpp index 647d6cc4b0b1..0c84363b84b7 100644 --- a/Code/Mantid/Framework/LiveData/src/SNSLiveEventDataListener.cpp +++ b/Code/Mantid/Framework/LiveData/src/SNSLiveEventDataListener.cpp @@ -685,7 +685,7 @@ namespace LiveData // Add the run_start property char timeString[64]; // largest the string should end up is 20 (plus a null terminator) - strftime( timeString, 64, "%FT%H:%M:%SZ", gmtime( &runStartTime)); + strftime( timeString, 64, "%Y-%m-%dT%H:%M:%SZ", gmtime( &runStartTime)); // addProperty() wants the time as an ISO 8601 string m_eventBuffer->mutableRun().addProperty("run_start", std::string( timeString) ); } diff --git a/Code/Mantid/Framework/MDAlgorithms/CMakeLists.txt b/Code/Mantid/Framework/MDAlgorithms/CMakeLists.txt index 4edf284a8cd5..c7f93158eb9a 100644 --- a/Code/Mantid/Framework/MDAlgorithms/CMakeLists.txt +++ b/Code/Mantid/Framework/MDAlgorithms/CMakeLists.txt @@ -49,6 +49,7 @@ set ( SRC_FILES src/Quantification/ForegroundModelFactory.cpp src/Quantification/MDResolutionConvolution.cpp src/Quantification/MDResolutionConvolutionFactory.cpp + src/Quantification/Models/QCoordinate.cpp src/Quantification/Models/Strontium122.cpp src/Quantification/Resolution/ModeratorChopperResolution.cpp src/Quantification/Resolution/TobyFitBMatrix.cpp @@ -123,6 +124,7 @@ set ( INC_FILES inc/MantidMDAlgorithms/Quantification/ForegroundModelFactory.h inc/MantidMDAlgorithms/Quantification/MDResolutionConvolution.h inc/MantidMDAlgorithms/Quantification/MDResolutionConvolutionFactory.h + inc/MantidMDAlgorithms/Quantification/Models/QCoordinate.h inc/MantidMDAlgorithms/Quantification/Models/Strontium122.h inc/MantidMDAlgorithms/Quantification/Resolution/ModeratorChopperResolution.h inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitBMatrix.h diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/BinMD.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/BinMD.h index a504924d57c8..7d9d7642332a 100644 --- a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/BinMD.h +++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/BinMD.h @@ -6,7 +6,6 @@ #include "MantidAPI/IMDEventWorkspace.h" #include "MantidGeometry/MDGeometry/MDHistoDimension.h" #include "MantidGeometry/MDGeometry/MDImplicitFunction.h" -#include "MantidKernel/Strings.h" #include "MantidKernel/System.h" #include "MantidKernel/VMD.h" #include "MantidMDEvents/MDBox.h" diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/IntegratePeaksMD2.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/IntegratePeaksMD2.h index f78b1555a8f2..74a78a88ac97 100644 --- a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/IntegratePeaksMD2.h +++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/IntegratePeaksMD2.h @@ -51,6 +51,10 @@ namespace MDAlgorithms /// Instrument reference Geometry::Instrument_const_sptr inst; + /// Check if peaks overlap + void checkOverlap(int i, + Mantid::DataObjects::PeaksWorkspace_sptr peakWS, int CoordinatesToUse, double radius); + }; diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/ForegroundModel.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/ForegroundModel.h index d5d734129820..84f8b75e182e 100644 --- a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/ForegroundModel.h +++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/ForegroundModel.h @@ -25,6 +25,12 @@ #include "MantidAPI/ParamFunction.h" #include "MantidMDAlgorithms/Quantification/ForegroundModelFactory.h" +// Includes that each model will most probably require +// This lightens the load for the user model writer +#include "MantidGeometry/Crystal/OrientedLattice.h" +#include "MantidKernel/Math/Distributions/BoseEinsteinDistribution.h" + + namespace Mantid { //---------------------------------------------------------------------------- diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/Models/QCoordinate.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/Models/QCoordinate.h new file mode 100644 index 000000000000..29821927528f --- /dev/null +++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/Models/QCoordinate.h @@ -0,0 +1,58 @@ +#ifndef MANTID_MDALGORITHMS_QCOORDINATE_H_ +#define MANTID_MDALGORITHMS_QCOORDINATE_H_ +/** + Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: . + Code Documentation is available at: +*/ +#include "MantidMDAlgorithms/Quantification/ForegroundModel.h" + +namespace Mantid +{ + namespace MDAlgorithms + { + + /** + * Defines a diagnostic model that simply returns + * the value of H,K,L,Energy as the weight depending on + * the input parameter + */ + class DLLExport QCoordinate : public ForegroundModel + { + private: + /// String name of the model + std::string name() const { return "QCoordinate"; } + + /// Setup the model + void init(); + /// Called when an attribute is set + void setAttribute(const std::string & name, const API::IFunction::Attribute& attr); + + /// Returns the type of model + ModelType modelType() const { return Broad; } + /// Calculates the intensity for the model for the current parameters. + double scatteringIntensity(const API::ExperimentInfo & exptDescr, const std::vector & point) const; + + /// Which coordinate has been chosen + size_t m_coord; + }; + + } +} +#endif /* MANTID_MDALGORITHMS_QCOORDINATE_H_ */ diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitResolutionModel.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitResolutionModel.h index 1664803eb0dc..6b92cd6ccdfc 100644 --- a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitResolutionModel.h +++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/Resolution/TobyFitResolutionModel.h @@ -53,7 +53,7 @@ namespace Mantid qz(qZ), deltaE(dE) {} /// - const double qx, qy, qz, deltaE; + double qx, qy, qz, deltaE; }; /** diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/SimulateResolutionConvolvedModel.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/SimulateResolutionConvolvedModel.h index 6d4701f1a14c..65a2ee3bbf81 100644 --- a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/SimulateResolutionConvolvedModel.h +++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/Quantification/SimulateResolutionConvolvedModel.h @@ -56,8 +56,6 @@ namespace Mantid void createDomains(); /// Generate the output MD workspace that is a result of the simulation void createOutputWorkspace(); - /// Add the simulated events - void addSimulatedEvents(); /// The input workspace API::IMDEventWorkspace_sptr m_inputWS; diff --git a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SlicingAlgorithm.h b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SlicingAlgorithm.h index 486061e7767b..7b061137a3c0 100644 --- a/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SlicingAlgorithm.h +++ b/Code/Mantid/Framework/MDAlgorithms/inc/MantidMDAlgorithms/SlicingAlgorithm.h @@ -8,7 +8,6 @@ #include "MantidAPI/IMDWorkspace.h" #include "MantidGeometry/MDGeometry/MDHistoDimension.h" #include "MantidGeometry/MDGeometry/MDImplicitFunction.h" -#include "MantidKernel/Strings.h" #include "MantidKernel/System.h" #include "MantidKernel/VMD.h" #include "MantidMDEvents/MDBox.h" diff --git a/Code/Mantid/Framework/MDAlgorithms/src/ConvertToMD.cpp b/Code/Mantid/Framework/MDAlgorithms/src/ConvertToMD.cpp index c94048eb47f7..eda66f007a08 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/ConvertToMD.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/ConvertToMD.cpp @@ -29,7 +29,7 @@ The examples below demonstrate the usages of the algorithm in most common situat Load(Filename='MAR11001.nxspe',OutputWorkspace='MAR11001') SofQW3(InputWorkspace='MAR11001',OutputWorkspace='MAR11001Qe2',QAxisBinning='0,0.1,7',EMode='Direct') -AddSampleLog(Workspace='MAR11001Qe2',LogName='T',LogText='100',LogType='Number Series') +AddSampleLog(Workspace='MAR11001Qe2',LogName='T',LogText='100.0',LogType='Number Series') ConvertToMD(InputWorkspace='MAR11001Qe2',OutputWorkspace='MD3',QDimensions='CopyToMD',OtherDimensions='T',\ MinValues='-10,0,0',MaxValues='10,6,500',SplitInto='50,50,5') @@ -50,7 +50,7 @@ Load(Filename=WS_Name,OutputWorkspace=WS_Name) # this workspace has been obtained from an inelastic experiment with input energy Ei = 3. # Usually this energy is stored in workspace # but if it is not, we have to provide it for inelastic conversion to work. -AddSampleLog(Workspace=WS_Name,LogName='Ei',LogText='3',LogType='Number') +AddSampleLog(Workspace=WS_Name,LogName='Ei',LogText='3.0',LogType='Number') # # set up target ws name and remove target workspace with the same name which can occasionally exist. RezWS = 'WS_4D' @@ -100,7 +100,7 @@ WS_Name='MAR11001.nxspe' Load(Filename=WS_Name,OutputWorkspace=WS_Name) # this workspace has been obtained from an inelastic experiment with input energy # nxspe file has input energy stored in it so no need to add energy artificially -#AddSampleLog(Workspace=WS_Name,LogName='Ei',LogText='3',LogType='Number') +#AddSampleLog(Workspace=WS_Name,LogName='Ei',LogText='3.0',LogType='Number') # set up target ws name and remove target workspace with the same name which can occasionally exist. RezWS = 'WS_3D' @@ -111,7 +111,7 @@ except ValueError: i=0 # let's assume this is the temperature range obtained in experiments and # each data file is obtained for particular temperature. -T = [1,1.5,2,2.5,3,3.5,4.,4.5,5,5.5,6,6.5,7,7.5,8,8.5,9,9.5,10] +T = [1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,7.0,7.5,8.0,8.5,9.0,9.5,10.0] for i in range(0,len(T),1): # EMULATE LOAD OF DIFFERENT results obtained for different temperatures. ------> SourceWS = 'SourcePart'+str(i) diff --git a/Code/Mantid/Framework/MDAlgorithms/src/IntegratePeaksMD.cpp b/Code/Mantid/Framework/MDAlgorithms/src/IntegratePeaksMD.cpp index d9b26bdce337..1a23a1cedb0f 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/IntegratePeaksMD.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/IntegratePeaksMD.cpp @@ -649,7 +649,7 @@ namespace MDAlgorithms /** Calculate if this Q is on a detector * - * @param QLabFrame. The Peak center. + * @param QLabFrame: The Peak center. * @param r: Peak radius. */ bool IntegratePeaksMD::detectorQ(Mantid::Kernel::V3D QLabFrame, double r) diff --git a/Code/Mantid/Framework/MDAlgorithms/src/IntegratePeaksMD2.cpp b/Code/Mantid/Framework/MDAlgorithms/src/IntegratePeaksMD2.cpp index 6d2968fba409..0eef355653d0 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/IntegratePeaksMD2.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/IntegratePeaksMD2.cpp @@ -593,6 +593,8 @@ namespace MDAlgorithms } } } + checkOverlap (i, peakWS, CoordinatesToUse, + 2.0 * std::max(PeakRadiusVector[i],BackgroundOuterRadiusVector[i])); // Save it back in the peak object. if (signal != 0. || replaceIntensity) { @@ -638,7 +640,7 @@ namespace MDAlgorithms /** Calculate if this Q is on a detector * - * @param QLabFrame. The Peak center. + * @param QLabFrame: The Peak center. * @param r: Peak radius. */ bool IntegratePeaksMD2::detectorQ(Mantid::Kernel::V3D QLabFrame, double r) @@ -676,7 +678,36 @@ namespace MDAlgorithms } return in; } - + void IntegratePeaksMD2::checkOverlap(int i, + Mantid::DataObjects::PeaksWorkspace_sptr peakWS, int CoordinatesToUse, double radius) + { + // Get a direct ref to that peak. + IPeak & p1 = peakWS->getPeak(i); + V3D pos1; + if (CoordinatesToUse == 1) //"Q (lab frame)" + pos1 = p1.getQLabFrame(); + else if (CoordinatesToUse == 2) //"Q (sample frame)" + pos1 = p1.getQSampleFrame(); + else if (CoordinatesToUse == 3) //"HKL" + pos1 = p1.getHKL(); + for (int j=i+1; j < peakWS->getNumberPeaks(); ++j) + { + // Get a direct ref to rest of peaks peak. + IPeak & p2 = peakWS->getPeak(j); + V3D pos2; + if (CoordinatesToUse == 1) //"Q (lab frame)" + pos2 = p2.getQLabFrame(); + else if (CoordinatesToUse == 2) //"Q (sample frame)" + pos2 = p2.getQSampleFrame(); + else if (CoordinatesToUse == 3) //"HKL" + pos2 = p2.getHKL(); + if (pos1.distance(pos2) < radius) + { + g_log.warning() << " Warning: Peak integration spheres for peaks " + << i << " and " << j <<" overlap. Distance between peaks is "<< pos1.distance(pos2)<getDetector(detID)->shape(); - if(!detShape) + // Make sure it encompasses all possible detectors + det->getBoundingBox(m_detBox); + if(m_detBox.isNull()) { throw std::invalid_argument("CachedExperimentInfo::initCaches - Detector has no bounding box, cannot sample from it. ID:" + boost::lexical_cast(det->getID())); } - m_detBox = detShape->getBoundingBox(); const double rad2deg = 180./M_PI; const double thetaInDegs = twoTheta()*rad2deg; diff --git a/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Models/QCoordinate.cpp b/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Models/QCoordinate.cpp new file mode 100644 index 000000000000..e4f9f91401a4 --- /dev/null +++ b/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Models/QCoordinate.cpp @@ -0,0 +1,117 @@ +// Includes +#include "MantidMDAlgorithms/Quantification/Models/QCoordinate.h" +#include + +namespace Mantid +{ + namespace MDAlgorithms + { + DECLARE_FOREGROUNDMODEL(QCoordinate); + + namespace // anonymous + { + /// N attrs + const unsigned int NATTS = 1; + /// Attribute names + const char * ATTR_NAMES[NATTS] = { "Coord" }; + /// 2 \pi + const double TWO_PI = 2.*M_PI; + } + + /** + * Initialize the model + */ + void QCoordinate::init() + { + declareAttribute(ATTR_NAMES[0], API::IFunction::Attribute("H")); + } + + /** + * Called when an attribute is set from the Fit string + * @param name :: The name of the attribute + * @param attr :: The value of the attribute + */ + void QCoordinate::setAttribute(const std::string & name, const API::IFunction::Attribute& attr) + { + if(name == ATTR_NAMES[0]) + { + std::string value = attr.asString(); + if(value == "QcX") m_coord = 0; + else if(value == "QcY") m_coord = 1; + else if(value == "QcZ") m_coord = 2; + else if(value == "H") m_coord = 3; + else if(value == "K") m_coord = 4; + else if(value == "L") m_coord = 5; + else if(value == "En") m_coord = 6; + else if(value == "Unity") m_coord = 7; + else + throw std::invalid_argument("Unknown coordinate name passed to QCoordinate model"); + } + else ForegroundModel::setAttribute(name, attr); // pass it on the base + } + + /** + * Calculates the scattering intensity + * @param exptSetup :: Details of the current experiment + * @param point :: The axis values for the current point in Q-W space: Qx, Qy, Qz, DeltaE. They + * are in the cartesian crystal frame + * @return The weight contributing from this point + */ + double QCoordinate::scatteringIntensity(const API::ExperimentInfo & exptSetup, const std::vector & point) const + { + // unity + if(m_coord == 7) return 1.0; + + // energy + const double eps(point[3]); + if(m_coord == 6) return eps; + + // crystal frame + const double qx(point[0]), qy(point[1]), qz(point[2]); + if(m_coord == 0) return qx; + else if(m_coord == 1) return qy; + else if(m_coord == 2) return qz; + + // HKL coords + // Transform the HKL only requires B matrix & goniometer (R) as ConvertToMD should have already + // handled addition of U matrix + // qhkl = (1/2pi)(RUB)^-1(qxyz) + const Geometry::OrientedLattice & lattice = exptSetup.sample().getOrientedLattice(); + const Kernel::DblMatrix & gr = exptSetup.run().getGoniometerMatrix(); + const Kernel::DblMatrix & bmat = lattice.getUB(); + + // Avoid doing inversion with Matrix class as it forces memory allocations + // M^-1 = (1/|M|)*M^T + double rb00(0.0), rb01(0.0), rb02(0.0), + rb10(0.0), rb11(0.0), rb12(0.0), + rb20(0.0), rb21(0.0), rb22(0.0); + for(unsigned int i = 0; i < 3; ++i) + { + rb00 += gr[0][i]*bmat[i][0]; + rb01 += gr[0][i]*bmat[i][1]; + rb02 += gr[0][i]*bmat[i][2]; + + rb10 += gr[1][i]*bmat[i][0]; + rb11 += gr[1][i]*bmat[i][1]; + rb12 += gr[1][i]*bmat[i][2]; + + rb20 += gr[2][i]*bmat[i][0]; + rb21 += gr[2][i]*bmat[i][1]; + rb22 += gr[2][i]*bmat[i][2]; + } + // 2pi*determinant. The tobyFit definition of rl vector has extra 2pi factor in it + const double twoPiDet= TWO_PI*(rb00*(rb11*rb22 - rb12*rb21) - + rb01*(rb10*rb22 - rb12*rb20) + + rb02*(rb10*rb21 - rb11*rb20)); + + //qh + if(m_coord == 3) return ((rb11*rb22 - rb12*rb21)*qx + (rb02*rb21 - rb01*rb22)*qy + (rb01*rb12 - rb02*rb11)*qz)/twoPiDet; + //qk + else if(m_coord == 4) return ((rb12*rb20 - rb10*rb22)*qx + (rb00*rb22 - rb02*rb20)*qy + (rb02*rb10 - rb00*rb12)*qz)/twoPiDet; + //ql + else if(m_coord == 5) return ((rb10*rb21 - rb11*rb20)*qx + (rb01*rb20 - rb00*rb21)*qy + (rb00*rb11 - rb01*rb10)*qz)/twoPiDet; + else + throw std::invalid_argument("Logical error. Invalid coord type " + boost::lexical_cast(m_coord)); + } + } +} diff --git a/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Models/Strontium122.cpp b/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Models/Strontium122.cpp index 229a8f16bd9a..8b5fa137cc4a 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Models/Strontium122.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Models/Strontium122.cpp @@ -1,8 +1,6 @@ // Includes #include "MantidMDAlgorithms/Quantification/Models/Strontium122.h" -#include "MantidGeometry/Crystal/OrientedLattice.h" -#include "MantidKernel/Math/Distributions/BoseEinsteinDistribution.h" #include @@ -74,8 +72,8 @@ namespace Mantid /** * Calculates the scattering intensity * @param exptSetup :: Details of the current experiment - * @param point :: The axis values for the current point in Q-W space: Qx, Qy, Qz, DeltaE. These contain the U matrix - * rotation already. + * @param point :: The axis values for the current point in Q-W space: Qx, Qy, Qz, DeltaE. These are in + * crystal cartesian coordinates * @return The weight contributing from this point */ double Strontium122::scatteringIntensity(const API::ExperimentInfo & exptSetup, const std::vector & point) const diff --git a/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Resolution/TobyFitResolutionModel.cpp b/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Resolution/TobyFitResolutionModel.cpp index 7689b1bea403..12542daa29aa 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Resolution/TobyFitResolutionModel.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/Quantification/Resolution/TobyFitResolutionModel.cpp @@ -84,7 +84,7 @@ namespace Mantid /** * Returns the value of the cross-section convoluted with the resolution an event. This assumes that - * the box forms a 4D point with axes: Qx, Qy, Qz, \f$\Delta E\f$ + * the box forms a 4D point with axes: Qx, Qy, Qz, \f$\Delta E\f$ in the cartesian crystal frame * @param box :: An iterator pointing at the current box under examination * @param eventIndex :: An index of the current event in the box * @param innerRunIndex :: An index of the current run within the workspace. This is NOT the run number. The experiment @@ -96,42 +96,95 @@ namespace Mantid { auto iter = m_exptCache.find(std::make_pair(innerRunIndex, box.getInnerDetectorID(eventIndex))); // Guaranteed to exist const CachedExperimentInfo & detCachedExperimentInfo = *(iter->second); - QOmegaPoint qOmega(box, eventIndex); + QOmegaPoint qCrystal(box, eventIndex); + + // Transform to spectrometer coordinates for resolution calculation + // Done by hand to avoid expensive memory allocations when using Matrix classes + const Geometry::OrientedLattice & lattice = detCachedExperimentInfo.experimentInfo().sample().getOrientedLattice(); + const Kernel::DblMatrix & gr = detCachedExperimentInfo.experimentInfo().run().getGoniometerMatrix(); + const Kernel::DblMatrix & umat = lattice.getU(); + + QOmegaPoint qLab(0.0,0.0,0.0,qCrystal.deltaE); + for(unsigned int i = 0; i < 3; ++i) + { + qLab.qx += (gr[0][i]*umat[i][0]*qCrystal.qx + + gr[0][i]*umat[i][1]*qCrystal.qy + + gr[0][i]*umat[i][2]*qCrystal.qz); + + qLab.qy += (gr[1][i]*umat[i][0]*qCrystal.qx + + gr[1][i]*umat[i][1]*qCrystal.qy + + gr[1][i]*umat[i][2]*qCrystal.qz); + + qLab.qz += (gr[2][i]*umat[i][0]*qCrystal.qx + + gr[2][i]*umat[i][1]*qCrystal.qy + + gr[2][i]*umat[i][2]*qCrystal.qz); + } if(m_foregroundOnly) { std::vector & nominalQ = m_deltaQE[PARALLEL_THREAD_NUMBER]; - nominalQ[0] = qOmega.qx; - nominalQ[1] = qOmega.qy; - nominalQ[2] = qOmega.qz; - nominalQ[3] = qOmega.deltaE; + nominalQ[0] = qCrystal.qx; + nominalQ[1] = qCrystal.qy; + nominalQ[2] = qCrystal.qz; + nominalQ[3] = qCrystal.deltaE; return foregroundModel().scatteringIntensity(detCachedExperimentInfo.experimentInfo(), nominalQ); } // -- Add in perturbations to nominal Q from instrument resolution -- // Calculate the matrix of coefficients that contribute to the resolution function (the B matrix in TobyFit). - calculateResolutionCoefficients(detCachedExperimentInfo, qOmega); + calculateResolutionCoefficients(detCachedExperimentInfo, qLab); + + // Pre calculate the transform inverse (RU) matrix elements + double rb00(0.0), rb01(0.0), rb02(0.0), + rb10(0.0), rb11(0.0), rb12(0.0), + rb20(0.0), rb21(0.0), rb22(0.0); + for(unsigned int i = 0; i < 3; ++i) + { + rb00 += gr[0][i]*umat[i][0]; + rb01 += gr[0][i]*umat[i][1]; + rb02 += gr[0][i]*umat[i][2]; + + rb10 += gr[1][i]*umat[i][0]; + rb11 += gr[1][i]*umat[i][1]; + rb12 += gr[1][i]*umat[i][2]; + + rb20 += gr[2][i]*umat[i][0]; + rb21 += gr[2][i]*umat[i][1]; + rb22 += gr[2][i]*umat[i][2]; + } + const double determinant = (rb00*(rb11*rb22 - rb12*rb21) - + rb01*(rb10*rb22 - rb12*rb20) + + rb02*(rb10*rb21 - rb11*rb20)); // Start MC loop and check the relative error every min steps monteCarloLoopStarting(); double sumSigma(0.0), sumSigmaSqr(0.0), avgSigma(0.0); for(int step = 1; step <= m_mcLoopMax; ++step) { - generateIntegrationVariables(detCachedExperimentInfo, qOmega); - calculatePerturbedQE(detCachedExperimentInfo, qOmega); + generateIntegrationVariables(detCachedExperimentInfo, qLab); + calculatePerturbedQE(detCachedExperimentInfo, qLab); std::vector & q0 = m_deltaQE[PARALLEL_THREAD_NUMBER]; // Currently ordered beam,perp,up (z,x,y) // Reorder to X,Y,Z std::swap(q0[0],q0[1]); std::swap(q0[1],q0[2]); + // Transform to crystal frame for model + // Need to tidy this up when we confirm it is correct + const double qcx = ((rb11*rb22 - rb12*rb21)*q0[0] + (rb02*rb21 - rb01*rb22)*q0[1] + (rb01*rb12 - rb02*rb11)*q0[2])/determinant; + const double qcy = ((rb12*rb20 - rb10*rb22)*q0[0] + (rb00*rb22 - rb02*rb20)*q0[1] + (rb02*rb10 - rb00*rb12)*q0[2])/determinant; + const double qcz = ((rb10*rb21 - rb11*rb20)*q0[0] + (rb01*rb20 - rb00*rb21)*q0[1] + (rb00*rb11 - rb01*rb10)*q0[2])/determinant; + q0[0] = qcx; + q0[1] = qcy; + q0[2] = qcz; + // Compute weight from the foreground at this point const double weight = foregroundModel().scatteringIntensity(detCachedExperimentInfo.experimentInfo(), q0); // Add on this contribution to the average sumSigma += weight; sumSigmaSqr += weight*weight; - + // cppcheck-suppress zerodivcond avgSigma = sumSigma/step; if(checkForConvergence(step) && hasConverged(step, sumSigma, sumSigmaSqr, avgSigma)) diff --git a/Code/Mantid/Framework/MDAlgorithms/src/Quantification/SimulateResolutionConvolvedModel.cpp b/Code/Mantid/Framework/MDAlgorithms/src/Quantification/SimulateResolutionConvolvedModel.cpp index d3545c3f12d0..80f87cb4a7cf 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/Quantification/SimulateResolutionConvolvedModel.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/Quantification/SimulateResolutionConvolvedModel.cpp @@ -43,6 +43,7 @@ namespace Mantid const char * RESOLUTION_NAME = "ResolutionFunction"; const char * FOREGROUND_NAME = "ForegroundModel"; const char * PARS_NAME = "Parameters"; + const char * APPEND_NAME = "AppendToExisting"; } @@ -92,6 +93,10 @@ namespace Mantid "The parameters/attributes for the function & model. See Fit documentation for format", Direction::Input); + declareProperty(APPEND_NAME, false, + "If true then the simulated events will be added to an existing workspace. If the workspace does " + "not exist then it is created", Direction::Input); + } /** @@ -113,13 +118,14 @@ namespace Mantid // If output workspace exists just add the events to that IMDEventWorkspace_sptr existingWS = getProperty(SIMULATED_NAME); - if(!existingWS) + bool append = getProperty(APPEND_NAME); + if(append && existingWS) { - createOutputWorkspace(); + m_outputWS = boost::dynamic_pointer_cast(existingWS); } else { - m_outputWS = boost::dynamic_pointer_cast(existingWS); + createOutputWorkspace(); } auto functionMD = boost::dynamic_pointer_cast(resolution); functionMD->storeSimulatedEvents(m_outputWS); @@ -165,6 +171,9 @@ namespace Mantid m_outputWS = boost::shared_ptr(new QOmegaWorkspace); // Bins extents and meta data + // Set sensible defaults for splitting behaviour + BoxController_sptr bc = m_outputWS->getBoxController(); + bc->setSplitThreshold(3000); for(size_t i = 0;i < 4; ++i) { boost::shared_ptr inputDim = m_inputWS->getDimension(i); @@ -173,61 +182,18 @@ namespace Mantid builder.setId(inputDim->getDimensionId()); builder.setUnits(inputDim->getUnits()); builder.setNumBins(inputDim->getNBins()); + bc->setSplitInto(i, inputDim->getNBins()); builder.setMin(inputDim->getMinimum()); builder.setMax(inputDim->getMaximum()); m_outputWS->addDimension(builder.create()); } - // Run information m_outputWS->copyExperimentInfos(*m_inputWS); - // Set sensible defaults for splitting behaviour - BoxController_sptr bc = m_outputWS->getBoxController(); - bc->setSplitInto(3); - bc->setSplitThreshold(3000); m_outputWS->initialize(); m_outputWS->splitBox(); // Make grid box } - /** - * Adds simulated events to the output workspace - */ - void SimulateResolutionConvolvedModel::addSimulatedEvents() - { - auto inputIter = m_inputWS->createIterator(); - size_t resultValueIndex(0); - const float errorSq = 0.0; - do - { - const size_t numEvents = inputIter->getNumEvents(); - const float signal = static_cast(m_calculatedValues->getCalculated(resultValueIndex)); - for(size_t i = 0; i < numEvents; ++i) - { - coord_t centers[4] = { inputIter->getInnerPosition(i,0), inputIter->getInnerPosition(i,1), - inputIter->getInnerPosition(i,2), inputIter->getInnerPosition(i,3) }; - m_outputWS->addEvent(MDEvent<4>(signal, errorSq, - inputIter->getInnerRunIndex(i), - inputIter->getInnerDetectorID(i), - centers)); - } - ++resultValueIndex; - } - while(inputIter->next()); - delete inputIter; - - API::MemoryManager::Instance().releaseFreeMemory(); - // This splits up all the boxes according to split thresholds and sizes. - auto threadScheduler = new Kernel::ThreadSchedulerFIFO(); - Kernel::ThreadPool threadPool(threadScheduler); - m_outputWS->splitAllIfNeeded(threadScheduler); - threadPool.joinAll(); - m_outputWS->refreshCache(); - - // Flush memory - API::MemoryManager::Instance().releaseFreeMemory(); - } - - } // namespace MDAlgorithms } // namespace Mantid diff --git a/Code/Mantid/Framework/MDAlgorithms/test/IntegratePeaksMD2Test.h b/Code/Mantid/Framework/MDAlgorithms/test/IntegratePeaksMD2Test.h index 89387cda21d2..c84458b08a21 100644 --- a/Code/Mantid/Framework/MDAlgorithms/test/IntegratePeaksMD2Test.h +++ b/Code/Mantid/Framework/MDAlgorithms/test/IntegratePeaksMD2Test.h @@ -40,6 +40,7 @@ class IntegratePeaksMD2Test : public CxxTest::TestSuite void test_Init() { + FrameworkManager::Instance(); IntegratePeaksMD2 alg; TS_ASSERT_THROWS_NOTHING( alg.initialize() ) TS_ASSERT( alg.isInitialized() ) diff --git a/Code/Mantid/Framework/MDAlgorithms/test/IntegratePeaksMDTest.h b/Code/Mantid/Framework/MDAlgorithms/test/IntegratePeaksMDTest.h index 80fc7aeb4697..892789cd86dc 100644 --- a/Code/Mantid/Framework/MDAlgorithms/test/IntegratePeaksMDTest.h +++ b/Code/Mantid/Framework/MDAlgorithms/test/IntegratePeaksMDTest.h @@ -40,6 +40,7 @@ class IntegratePeaksMDTest : public CxxTest::TestSuite void test_Init() { + FrameworkManager::Instance(); IntegratePeaksMD alg; TS_ASSERT_THROWS_NOTHING( alg.initialize() ) TS_ASSERT( alg.isInitialized() ) diff --git a/Code/Mantid/Framework/MDAlgorithms/test/SimulateResolutionConvolvedModelTest.h b/Code/Mantid/Framework/MDAlgorithms/test/SimulateResolutionConvolvedModelTest.h index 30b2c0b806e4..814deae94f85 100644 --- a/Code/Mantid/Framework/MDAlgorithms/test/SimulateResolutionConvolvedModelTest.h +++ b/Code/Mantid/Framework/MDAlgorithms/test/SimulateResolutionConvolvedModelTest.h @@ -21,7 +21,7 @@ class SimulateResolutionConvolvedModelTest : public CxxTest::TestSuite TS_ASSERT_THROWS_NOTHING(alg.initialize()); TS_ASSERT(alg.isInitialized()); - TS_ASSERT_EQUALS(alg.propertyCount(), 5); + TS_ASSERT_EQUALS(alg.propertyCount(), 6); } }; diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/ConvToMDBase.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/ConvToMDBase.h index 42975691e566..15619c52734d 100644 --- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/ConvToMDBase.h +++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/ConvToMDBase.h @@ -59,7 +59,7 @@ namespace MDEvents ConvToMDBase(); ///method which initates all main class variables - virtual size_t initialize(const MDWSDescription &WSD, boost::shared_ptr inWSWrapper, bool IgnoreZeros); + virtual size_t initialize(const MDWSDescription &WSD, boost::shared_ptr inWSWrapper, bool ignoreZeros); /// method which starts the conversion procedure virtual void runConversion(API::Progress *)=0; /// virtual destructor diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h index 84200faedead..65f391c551d4 100644 --- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h +++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h @@ -40,7 +40,7 @@ namespace MDEvents { public: - MDGridBox(boost::shared_ptr &bc, const uint32_t depth,const std::vector > & extentsVector); + MDGridBox(boost::shared_ptr &bc, const uint32_t depth,const std::vector > & extentsVector); MDGridBox(Mantid::API::BoxController *const bc, const uint32_t depth,const std::vector > & extentsVector); MDGridBox(MDBox * box); diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDHistoWorkspace.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDHistoWorkspace.h index 7c726cb7b054..2406ab0d3e25 100644 --- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDHistoWorkspace.h +++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDHistoWorkspace.h @@ -126,7 +126,7 @@ namespace MDEvents } /** @return the direct pointer to the signal array. For speed */ - signal_t * getSignalArray() + signal_t * getSignalArray() const { return m_signals; } @@ -138,19 +138,19 @@ namespace MDEvents } /** @return the direct pointer to the error squared array. For speed */ - signal_t * getErrorSquaredArray() + signal_t * getErrorSquaredArray() const { return m_errorsSquared; } /** @return the direct pointer to the array of the number of events. For speed */ - signal_t * getNumEventsArray() + signal_t * getNumEventsArray() const { return m_numEvents; } /** @return the direct pointer to the array of mask bits (bool). For speed/testing */ - bool * getMaskArray() + bool * getMaskArray() const { return m_masks; } @@ -197,7 +197,7 @@ namespace MDEvents } /// Returns the number of contributing events from the bin at the specified index. - signal_t getNumEventsAt(size_t index) + signal_t getNumEventsAt(size_t index) const { return m_numEvents[index]; } diff --git a/Code/Mantid/Framework/MDEvents/src/CoordTransformAffine.cpp b/Code/Mantid/Framework/MDEvents/src/CoordTransformAffine.cpp index 67dcd7383e9d..8e16312cec5c 100644 --- a/Code/Mantid/Framework/MDEvents/src/CoordTransformAffine.cpp +++ b/Code/Mantid/Framework/MDEvents/src/CoordTransformAffine.cpp @@ -8,6 +8,7 @@ #include "MantidKernel/VectorHelper.h" #include "MantidMDEvents/CoordTransformAligned.h" #include "MantidAPI/CoordTransform.h" +#include using namespace Mantid::Geometry; using namespace Mantid::Kernel; diff --git a/Code/Mantid/Framework/MDEvents/src/CoordTransformDistance.cpp b/Code/Mantid/Framework/MDEvents/src/CoordTransformDistance.cpp index c69c681581a7..d8d7e1ec3696 100644 --- a/Code/Mantid/Framework/MDEvents/src/CoordTransformDistance.cpp +++ b/Code/Mantid/Framework/MDEvents/src/CoordTransformDistance.cpp @@ -24,6 +24,7 @@ namespace MDEvents * @param center :: array of size[inD], with the coordinates at the center * @param dimensionsUsed :: bool array of size[inD] where True is set for those dimensions that are considered when * calculating distance. + * @param outD :: # of output dimensions * @return */ CoordTransformDistance::CoordTransformDistance(const size_t inD, const coord_t * center, const bool * dimensionsUsed, const size_t outD) diff --git a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp index 8df0b7bfecde..96e5461f03cb 100644 --- a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp +++ b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp @@ -614,6 +614,7 @@ namespace MDEvents * @param length :: length below which to integrate * @param[out] signal :: set to the integrated signal * @param[out] errorSquared :: set to the integrated squared error. + * @param[out] signal_fit :: evaluation parameter on fit */ TMDE( void MDBox)::integrateCylinder(Mantid::API::CoordTransform & radiusTransform, const coord_t radius, const coord_t length, signal_t & signal, signal_t & errorSquared, std::vector & signal_fit) const diff --git a/Code/Mantid/Framework/MDEvents/src/MDGridBox.cpp b/Code/Mantid/Framework/MDEvents/src/MDGridBox.cpp index c082d2b0a0e3..be571ac4dec3 100644 --- a/Code/Mantid/Framework/MDEvents/src/MDGridBox.cpp +++ b/Code/Mantid/Framework/MDEvents/src/MDGridBox.cpp @@ -1316,9 +1316,11 @@ GCC_DIAG_OFF(array-bounds) * * @param radiusTransform :: nd-to-1 coordinate transformation that converts from these * dimensions to the distance (squared) from the center of the sphere. - * @param radiusSquared :: radius^2 below which to integrate + * @param radius :: radius below which to integrate + * @param length :: length below which to integrate * @param signal [out] :: set to the integrated signal * @param errorSquared [out] :: set to the integrated squared error. + * @param[out] signal_fit :: evaluation parameter on fit */ TMDE( void MDGridBox)::integrateCylinder(Mantid::API::CoordTransform & radiusTransform, const coord_t radius, const coord_t length, signal_t & signal, signal_t & errorSquared, std::vector & signal_fit) const diff --git a/Code/Mantid/Framework/MDEvents/src/MDHistoWorkspaceIterator.cpp b/Code/Mantid/Framework/MDEvents/src/MDHistoWorkspaceIterator.cpp index d7bf1559393e..02e037593d18 100644 --- a/Code/Mantid/Framework/MDEvents/src/MDHistoWorkspaceIterator.cpp +++ b/Code/Mantid/Framework/MDEvents/src/MDHistoWorkspaceIterator.cpp @@ -244,8 +244,7 @@ namespace MDEvents case VolumeNormalization: return m_ws->getSignalAt(m_pos) * m_ws->getInverseVolume(); case NumEventsNormalization: - /// TODO: # of events normalization - return m_ws->getSignalAt(m_pos); + return m_ws->getSignalAt(m_pos) / m_ws->getNumEventsAt(m_pos); } // Should not reach here return std::numeric_limits::quiet_NaN(); @@ -263,8 +262,7 @@ namespace MDEvents case VolumeNormalization: return m_ws->getErrorAt(m_pos) * m_ws->getInverseVolume(); case NumEventsNormalization: - /// TODO: # of events normalization - return m_ws->getErrorAt(m_pos); + return m_ws->getErrorAt(m_pos) / m_ws->getNumEventsAt(m_pos); } // Should not reach here return std::numeric_limits::quiet_NaN(); @@ -317,7 +315,7 @@ namespace MDEvents /// @return 1 always: e.g. there is one (fake) event in the middle of the box. size_t MDHistoWorkspaceIterator::getNumEvents() const { - return 1; + return static_cast(m_ws->getNumEventsAt(m_pos)); } //---------------------------------------------------------------------------------------------- diff --git a/Code/Mantid/Framework/Nexus/src/NexusFileIO.cpp b/Code/Mantid/Framework/Nexus/src/NexusFileIO.cpp index 9b67a20e6151..1e04569298af 100644 --- a/Code/Mantid/Framework/Nexus/src/NexusFileIO.cpp +++ b/Code/Mantid/Framework/Nexus/src/NexusFileIO.cpp @@ -150,6 +150,7 @@ using namespace DataObjects; The URLs are not correct as they do not exist presently, but follow the format for other Nexus specs. @param title :: title field. + @param wsName :: workspace name. */ int NexusFileIO::writeNexusProcessedHeader( const std::string& title, const std::string& wsName) const { diff --git a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/FunctionFactory.cpp b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/FunctionFactory.cpp index f7e21755113e..af90f1f1c29d 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/FunctionFactory.cpp +++ b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/FunctionFactory.cpp @@ -30,7 +30,7 @@ namespace */ PyObject * getFunctionNames(FunctionFactoryImpl & self) { - std::vector names = self.getFunctionNames(); + const std::vector& names = self.getFunctionNames(); PyObject *registered = PyList_New(0); for (auto name = names.begin(); name != names.end(); ++name) diff --git a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/ITableWorkspace.cpp b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/ITableWorkspace.cpp index 1849c0dc66ca..a715b769ede2 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/ITableWorkspace.cpp +++ b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/ITableWorkspace.cpp @@ -2,6 +2,9 @@ #include "MantidAPI/Column.h" #include "MantidAPI/TableRow.h" #include "MantidAPI/WorkspaceProperty.h" +#include "MantidPythonInterface/kernel/Converters/NDArrayToVector.h" +#include "MantidPythonInterface/kernel/Converters/PySequenceToVector.h" +#include "MantidPythonInterface/kernel/Converters/CloneToNumpy.h" #include "MantidPythonInterface/kernel/SharedPtrToPythonMacro.h" #include "MantidPythonInterface/kernel/Registry/RegisterSingleValueHandler.h" #include "MantidPythonInterface/kernel/PropertyWithValue.h" @@ -14,6 +17,11 @@ #include #include +// See http://docs.scipy.org/doc/numpy/reference/c-api.array.html#PY_ARRAY_UNIQUE_SYMBOL +#define PY_ARRAY_UNIQUE_SYMBOL API_ARRAY_API +#define NO_IMPORT_ARRAY +#include + using Mantid::API::ITableWorkspace; using Mantid::API::ITableWorkspace_sptr; using Mantid::API::TableRow; @@ -26,6 +34,7 @@ using namespace boost::python; namespace { namespace bpl = boost::python; + namespace Converters = Mantid::PythonInterface::Converters; /// Boost macro for "looping" over builtin types #define BUILTIN_TYPES \ @@ -36,6 +45,10 @@ namespace BOOST_PP_TUPLE_TO_LIST( \ 1, (Mantid::Kernel::V3D) \ ) + #define ARRAY_TYPES \ + BOOST_PP_TUPLE_TO_LIST( \ + 2, (std::vector, std::vector ) \ + ) /** * Get out the Python value from a specific cell of the supplied column. This is meant to @@ -64,12 +77,18 @@ namespace if(!entry) throw std::invalid_argument("Cannot find converter from C++ type.");\ result = entry->to_python((const void *)&column->cell(row));\ } + #define GET_ARRAY(R, _, T) \ + else if(typeID == typeid(T))\ + {\ + result = Converters::Clone::apply::create1D( column->cell(row) ); \ + } // -- Use the boost preprocessor to generate a list of else if clause to cut out copy // and pasted code. PyObject *result(NULL); if(false){} // So that it always falls through to the list checking BOOST_PP_LIST_FOR_EACH(GET_BUILTIN, _ , BUILTIN_TYPES) + BOOST_PP_LIST_FOR_EACH(GET_ARRAY, _ , ARRAY_TYPES) BOOST_PP_LIST_FOR_EACH(GET_USER, _ , USER_TYPES) else { @@ -92,11 +111,25 @@ namespace return; } -#define SET_CELL(R, _, T) \ + #define SET_CELL(R, _, T) \ else if(typeID == typeid(T)) \ {\ column->cell(row) = bpl::extract(value)();\ } + #define SET_VECTOR_CELL(R, _, T) \ + else if(typeID == typeid(T)) \ + {\ + if( ! PyArray_Check( value.ptr() ) ) \ + { \ + column->cell(row) = Converters::PySequenceToVector(value)(); \ + } \ + else \ + { \ + column->cell(row) = Converters::NDArrayToVector(value)();\ + } \ + } + + // -- Use the boost preprocessor to generate a list of else if clause to cut out copy // and pasted code. // I think cppcheck is getting confused by the define @@ -104,6 +137,7 @@ namespace const std::type_info & typeID = column->get_type_info(); if(false){} // So that it always falls through to the list checking BOOST_PP_LIST_FOR_EACH(SET_CELL, _ , BUILTIN_TYPES) + BOOST_PP_LIST_FOR_EACH(SET_VECTOR_CELL, _ , ARRAY_TYPES) BOOST_PP_LIST_FOR_EACH(SET_CELL, _ , USER_TYPES) else { diff --git a/Code/Mantid/Framework/PythonInterface/mantid/geometry/CMakeLists.txt b/Code/Mantid/Framework/PythonInterface/mantid/geometry/CMakeLists.txt index 13530a2ea1e9..1d89cc5c09c3 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/PythonInterface/mantid/geometry/CMakeLists.txt @@ -16,6 +16,7 @@ set ( EXPORT_FILES src/Exports/ObjCompAssembly.cpp src/Exports/Detector.cpp src/Exports/DetectorGroup.cpp + src/Exports/RectangularDetector.cpp src/Exports/Instrument.cpp src/Exports/UnitCell.cpp src/Exports/OrientedLattice.cpp @@ -65,4 +66,4 @@ target_link_libraries ( PythonGeometryModule PythonKernelModule ${PYTHON_DEPS} ) install ( TARGETS PythonGeometryModule ${SYSTEM_PACKAGE_TARGET} DESTINATION ${BIN_DIR}/mantid/geometry ) # Pure Python files -install ( FILES ${PY_FILES} DESTINATION ${BIN_DIR}/mantid/geometry ) \ No newline at end of file +install ( FILES ${PY_FILES} DESTINATION ${BIN_DIR}/mantid/geometry ) diff --git a/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/RectangularDetector.cpp b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/RectangularDetector.cpp new file mode 100644 index 000000000000..dc42666f4312 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/RectangularDetector.cpp @@ -0,0 +1,36 @@ +#include "MantidGeometry/Instrument/RectangularDetector.h" +#include +#include "MantidPythonInterface/kernel/SharedPtrToPythonMacro.h" +#include "MantidGeometry/Instrument/CompAssembly.h" + +using Mantid::Geometry::CompAssembly; +using Mantid::Geometry::RectangularDetector; +using Mantid::Geometry::IDetector; +using Mantid::Geometry::IObjComponent; +using namespace boost::python; + +/** + * Enables boost.python to automatically "cast" an object up to the + * appropriate Detector leaf type + */ +void export_RectangularDetector() +{ + REGISTER_SHARED_PTR_TO_PYTHON(RectangularDetector); + class_, boost::noncopyable>("RectangularDetector", no_init) + .def("xpixels",&RectangularDetector::xpixels, "Returns the number of pixels in the X direction") + .def("ypixels",&RectangularDetector::ypixels, "Returns the number of pixels in the Y direction") + .def("xstep",&RectangularDetector::xstep, "Returns the step size in the X direction") + .def("ystep",&RectangularDetector::ystep, "Returns the step size in the Y direction") + .def("xsize",&RectangularDetector::xsize, "Returns the size in the X direction") + .def("ysize",&RectangularDetector::ysize, "Returns the size in the Y direction") + .def("xstart",&RectangularDetector::xstart, "Returns the start position in the X direction") + .def("ystart",&RectangularDetector::ystart, "Returns the start position in the Y direction") + .def("idstart",&RectangularDetector::idstart, "Returns the idstart") + .def("idfillbyfirst_y",&RectangularDetector::idfillbyfirst_y, "Returns the idfillbyfirst_y") + .def("idstepbyrow",&RectangularDetector::idstepbyrow, "Returns the idstepbyrow") + .def("idstep",&RectangularDetector::idstep, "Returns the idstep") + .def("minDetectorID",&RectangularDetector::minDetectorID, "Returns the minimum detector id") + .def("maxDetectorID",&RectangularDetector::maxDetectorID, "Returns the maximum detector id") + ; +} + diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/CorrectLogTimes.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/CorrectLogTimes.py new file mode 100644 index 000000000000..a75658ad09a9 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/CorrectLogTimes.py @@ -0,0 +1,69 @@ +"""*WIKI* +Sometimes the clocks controlling different sample environments or other experimental log values are not synchronized. +This algorithm attempts to make all (some) time series property logs start at the same time as the first time in the proton charge log. +*WIKI*""" + +import mantid.simpleapi +import mantid.api +import mantid.kernel +import numpy + +class CorrectLogTimes(mantid.api.PythonAlgorithm): + """ Class to shift log times to match proton charge + """ + + def category(self): + """ Mantid required + """ + return "PythonAlgorithms;DataHandling\\Logs" + + def name(self): + """ Mantid required + """ + return "CorrectLogTimes" + + + def PyInit(self): + self.declareProperty(mantid.api.WorkspaceProperty("Workspace", "",direction=mantid.kernel.Direction.InOut), "Input workspace") + self.declareProperty("LogNames","",doc="Experimental og values to be shifted. If empty, will attempt to shift all logs") + + def PyExec(self): + self.ws = self.getProperty("Workspace").value + logNames = self.getProperty("LogNames").value + + logList=[] + + #check for parameters and build the result string + for value in logNames.split(','): + value=value.strip() + if len(value)>0: + if not self.ws.run().hasProperty(value): + err = 'Property '+value+' not found' + raise ValueError(err) + else: + logList.append(value) + + + if len(logList)==0: + logList=self.ws.getRun().keys() + + for x in logList: + if x not in ['duration','proton_charge','start_time','run_title','run_start','run_number','gd_prtn_chrg','end_time']: + try: + self.ShiftTime(x) + except: + pass + + + def ShiftTime(self, logName): + """ + shift the time in a given log to match the time in the proton charge log" + """ + PC = self.ws.getRun()['proton_charge'].firstTime() + P = self.ws.getRun()[logName].firstTime() + Tdiff = PC-P + Tdiff_num = Tdiff.total_milliseconds()*1E-3 + mantid.simpleapi.ChangeLogTime(InputWorkspace=self.ws, OutputWorkspace = self.ws, LogName = logName, TimeOffset = Tdiff_num) + + +mantid.api.AlgorithmFactory.subscribe(CorrectLogTimes) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/CreateTransmissionWorkspaceAuto.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/CreateTransmissionWorkspaceAuto.py new file mode 100644 index 000000000000..9e7ad0888a31 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/CreateTransmissionWorkspaceAuto.py @@ -0,0 +1,130 @@ +"""*WIKI* + +Facade over [[CreateTransmissionWorkspace]]. Pull numeric parameters out of the instrument parameters where possible. You can override any of these automatically +applied defaults by providing your own value for the input. + +See [[CreateTransmissionWorkspace]] for more information on the wrapped algorithm. + +*WIKI*""" + + +import sys +from mantid.simpleapi import CreateTransmissionWorkspace +from mantid.api import * +from mantid.kernel import * + +class CreateTransmissionWorkspaceAuto(PythonAlgorithm): + + def category(self): + return "Reflectometry\\ISIS" + + def name(self): + return "CreateTransmissionWorkspaceAuto" + + def PyInit(self): + + analysis_modes = ["PointDetectorAnalysis", "MultiDetectorAnalysis"] + analysis_mode_validator = StringListValidator(analysis_modes) + + self.declareProperty(name="AnalysisMode", defaultValue=analysis_modes[0], validator=analysis_mode_validator, direction = Direction.Input, doc="Analysis Mode to Choose") + + input_validator = WorkspaceUnitValidator("TOF") + self.declareProperty(MatrixWorkspaceProperty("FirstTransmissionRun", "", Direction.Input, validator=input_validator), "Input workspace") + self.declareProperty(MatrixWorkspaceProperty("SecondTransmissionRun", "", optional=PropertyMode.Optional, validator=input_validator, direction=Direction.Input), "Second transmission run workspace in TOF") + self.declareProperty(MatrixWorkspaceProperty("OutputWorkspace", "", Direction.Output), "Output transmission workspace in wavelength") + + self.declareProperty(FloatArrayProperty(name="Params", values=[sys.maxint]), doc="Rebinning Parameters. See Rebin for format.") + self.declareProperty(name="StartOverlap", defaultValue=sys.float_info.max, doc="Overlap in Q.") + self.declareProperty(name="EndOverlap", defaultValue=sys.float_info.max, doc="End overlap in Q.") + index_bounds = IntBoundedValidator() + index_bounds.setLower(0) + self.declareProperty(name="I0MonitorIndex", defaultValue=sys.maxint, validator=index_bounds, doc="I0 monitor index" ) + self.declareProperty(name="ProcessingInstructions", direction=Direction.Input, defaultValue="", doc="Processing instructions to extract spectra belonging to detector workspace, see [[PerformIndexOperations]] for syntax.") + self.declareProperty(name="WavelengthMin", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Wavelength Min in angstroms") + self.declareProperty(name="WavelengthMax", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Wavelength Max in angstroms") + self.declareProperty(name="WavelengthStep", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Wavelength step in angstroms") + self.declareProperty(name="MonitorBackgroundWavelengthMin", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Monitor wavelength background min in angstroms") + self.declareProperty(name="MonitorBackgroundWavelengthMax", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Monitor wavelength background max in angstroms") + self.declareProperty(name="MonitorIntegrationWavelengthMin", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Monitor integral min in angstroms") + self.declareProperty(name="MonitorIntegrationWavelengthMax", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Monitor integral max in angstroms") + + + def value_or_none(self, prop_name, instrument): + property = self.getProperty(prop_name) + if property.isDefault: + return None # TODO: check this + else: + return property.value + + + def value_to_apply(self, prop_name, instrument, idf_name=""): + property = self.getProperty(prop_name) + if property.isDefault: + return instrument.getNumberParameter(idf_name)[0] + else: + return property.value + + def PyExec(self): + first_ws = self.getProperty("FirstTransmissionRun").value + instrument = first_ws.getInstrument() + + ''' + Get all the inputs. + ''' + outputWorkspaceName = self.getPropertyValue("OutputWorkspace") + + analysis_mode = self.getPropertyValue("AnalysisMode") + + second_ws_property = self.getProperty("SecondTransmissionRun") + second_ws = None + if not second_ws_property.isDefault: + second_ws = second_ws_property.value + + start_overlap = self.value_or_none("StartOverlap", instrument) + + end_overlap = self.value_or_none("EndOverlap", instrument) + + params = self.value_or_none("Params", instrument) + + i0_monitor_index = int(self.value_to_apply("I0MonitorIndex", instrument, "I0MonitorIndex")) + + processing_commands = str() + workspace_index_list = list() + if self.getProperty("ProcessingInstructions").isDefault: + if analysis_mode == "PointDetectorAnalysis": + workspace_index_list.append( int(instrument.getNumberParameter("PointDetectorStart")[0]) ) + workspace_index_list.append( int(instrument.getNumberParameter("PointDetectorStop")[0]) ) + else: + workspace_index_list.append( int(instrument.getNumberParameter("MultiDetectorStart")[0]) ) + workspace_index_list.append( first_ws.getNumberHistograms() - 1) + processing_commands = ','.join(map(str, workspace_index_list)) + else: + processing_commands = self.getProperty("ProcessingInstructions").value + + wavelength_min = self.value_to_apply("WavelengthMin", instrument, "LambdaMin") + + wavelength_max = self.value_to_apply("WavelengthMax", instrument, "LambdaMax") + + wavelength_step = self.value_or_none("WavelengthStep", instrument) + + wavelength_back_min = self.value_to_apply("MonitorBackgroundWavelengthMin", instrument, "MonitorBackgroundMin") + + wavelength_back_max = self.value_to_apply("MonitorBackgroundWavelengthMax", instrument, "MonitorBackgroundMax") + + wavelength_integration_min = self.value_to_apply("MonitorIntegrationWavelengthMin", instrument, "MonitorIntegralMin") + + wavelength_integration_max = self.value_to_apply("MonitorIntegrationWavelengthMax", instrument, "MonitorIntegralMax") + + ''' + Pass the arguments and execute the main algorithm. + ''' + output_ws = CreateTransmissionWorkspace(FirstTransmissionRun=first_ws, SecondTransmissionRun=second_ws, OutputWorkspace=outputWorkspaceName, + StartOverlap=start_overlap, EndOverlap=end_overlap, Params=params, I0MonitorIndex=i0_monitor_index, + ProcessingInstructions=processing_commands, WavelengthMin=wavelength_min, WavelengthStep=wavelength_step, WavelengthMax=wavelength_max, + MonitorBackgroundWavelengthMin=wavelength_back_min, MonitorBackgroundWavelengthMax=wavelength_back_max, + MonitorIntegrationWavelengthMin=wavelength_integration_min, MonitorIntegrationWavelengthMax=wavelength_integration_max) + + + self.setProperty("OutputWorkspace", output_ws) + +AlgorithmFactory.subscribe(CreateTransmissionWorkspaceAuto) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/GetEiT0atSNS.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/GetEiT0atSNS.py new file mode 100644 index 000000000000..86676aa78157 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/GetEiT0atSNS.py @@ -0,0 +1,86 @@ +"""*WIKI* +Get Ei and T0 on ARCS and SEQUOIA instruments. It accounts for the following: +* in the ADARA framework, the monitors are in the first frame. +* SEQUOIA has event based monitors. +* some data aquisition errors will create unphysical monitor IDs. This will be ignored +* when vChTrans is 2, on ARCS and SEQUOIA there is no chopper in the beam (white beam). Will return not a number for both Ei and T0 +*WIKI*""" + +import mantid +import numpy + + +class GetEiT0atSNS(mantid.api.PythonAlgorithm): + def category(self): + """ Return category + """ + return "PythonAlgorithms;Inelastic" + + def name(self): + """ Return name + """ + return "GetEiT0atSNS" + + def PyInit(self): + """ Declare properties + """ + self.declareProperty(mantid.api.WorkspaceProperty("MonitorWorkspace", "",direction=mantid.kernel.Direction.InOut), "Monitor workspace") + self.declareProperty("IncidentEnergyGuess",-1.,doc="Incident energy guess") + self.declareProperty("Ei",0.0,mantid.kernel.Direction.Output) + self.declareProperty("T0",0.0,mantid.kernel.Direction.Output) + return + + def PyExec(self): + """ Main execution body + """ + wm=self.getProperty("MonitorWorkspace").value + i=wm.getInstrument() + + if numpy.mean(wm.getRun()['vChTrans'].value) == 2: + Ei=numpy.nan + Tzero=numpy.nan + else: + EGuess=self.getProperty("IncidentEnergyGuess").value + if EGuess<0: + try: + EGuess=wm.getRun()['EnergyRequest'].getStatistics().mean + except: + raise RuntimeError("No energy guess was given or could be found in sample logs") + try: + #fix more than 2 monitors + sp1=-1 + sp2=-1 + nsp=wm.getNumberHistograms() + if nsp < 2: + raise ValueError("There are less than 2 monitors") + for sp in range(nsp): + if wm.getSpectrum(sp).getDetectorIDs()[0]==-int(i.getNumberParameter('ei-mon1-spec')[0]): + sp1=sp + if wm.getSpectrum(sp).getDetectorIDs()[0]==-int(i.getNumberParameter('ei-mon2-spec')[0]): + sp2=sp + if sp1==-1: + raise RuntimeError("Could not find spectrum for the first monitor") + if sp2==-1: + raise RuntimeError("Could not find spectrum for the second monitor") + #change frame for monitors. ARCS monitors would be in the first frame for Ei>10meV + so=i.getSource().getPos() + m1=wm.getDetector(sp1).getPos() + m2=wm.getDetector(sp2).getPos() + v=437.4*numpy.sqrt(wm.getRun()['EnergyRequest'].getStatistics().mean) + t1=m1.distance(so)*1e6/v + t2=m2.distance(so)*1e6/v + t1f=int(t1*60e-6) #frame number for monitor 1 + t2f=int(t2*60e-6) #frame number for monitor 2 + wtemp=mantid.simpleapi.ChangeBinOffset(wm,t1f*16667,0,0) + wtemp=mantid.simpleapi.ChangeBinOffset(wtemp,t2f*16667,1,1) + wtemp=mantid.simpleapi.Rebin(InputWorkspace=wtemp,Params="1",PreserveEvents=True) + alg=mantid.simpleapi.GetEi(InputWorkspace=wtemp,Monitor1Spec=sp1+1,Monitor2Spec=sp2+1,EnergyEstimate=EGuess) #Run GetEi algorithm + Ei=alg[0] + Tzero=alg[3] #Extract incident energy and T0 + mantid.simpleapi.DeleteWorkspace(wtemp) + except Exception as e: + raise RuntimeError("Could not get Ei, and this is not a white beam run\n"+e.message) + self.setProperty("Ei",Ei) + self.setProperty("T0",Tzero) + +mantid.api.AlgorithmFactory.subscribe(GetEiT0atSNS) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadVesuvio.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadVesuvio.py index f347c7febcca..e23d59e8cb1d 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadVesuvio.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadVesuvio.py @@ -19,6 +19,7 @@ MODES=["SingleDifference", "DoubleDifference", "ThickDifference", "FoilOut", "FoilIn", "FoilInOut"] SPECTRA_PROP = "SpectrumList" INST_PAR_PROP = "InstrumentParFile" +SUM_PROP = "SumSpectra" # Raw workspace names which are necessary at the moment SUMMED_WS, SUMMED_MON = "__loadraw_evs", "__loadraw_evs_monitors" @@ -60,6 +61,10 @@ def PyInit(self): doc="An optional IP file. If provided the values are used to correct " "the default instrument values and attach the t0 values to each detector") + self.declareProperty(SUM_PROP, False, + doc="If true then the final output is a single spectrum containing the sum " + "of all of the requested spectra. All detector angles/parameters are " + "averaged over the individual inputs") #---------------------------------------------------------------------------------------- def PyExec(self): self._load_inst_parameters() @@ -90,8 +95,14 @@ def _exec_difference_mode(self): self._normalise_by_monitor() self._normalise_to_foil_out() self._calculate_diffs() - - self._load_ip_file() + # end of differencing loop + + ip_file = self.getPropertyValue(INST_PAR_PROP) + if len(ip_file) > 0: + self._load_ip_file(ip_file) + if self._sumspectra: + self._sum_all_spectra() + self._store_results() finally: # Ensures it happens whether there was an error or not self._cleanup_raw() @@ -216,6 +227,7 @@ def _retrieve_input(self): self._spectra = self.getProperty(SPECTRA_PROP).value.tolist() if self._mon_spectra in self._spectra: self._spectra.remove(self._spectra) + self._sumspectra = self.getProperty(SUM_PROP).value #---------------------------------------------------------------------------------------- @@ -608,14 +620,14 @@ def _calculate_thick_difference(self, ws_index): np.sqrt((eout**2 + ethick**2), eout) # The second argument makes it happen in place #---------------------------------------------------------------------------------------- - def _load_ip_file(self): + def _load_ip_file(self, ip_file): """ If provided, load the instrument parameter file into the result workspace + @param ip_file A string containing the full path to an IP file """ - ip_file = self.getProperty(INST_PAR_PROP).value if ip_file == "": - return + raise ValueError("Empty filename string for IP file") ip_header = self._get_header_format(ip_file) @@ -631,6 +643,22 @@ def _load_ip_file(self): self.foil_out = update_inst.getProperty("Workspace").value +#---------------------------------------------------------------------------------------- + + def _sum_all_spectra(self): + """ + Runs the SumSpectra algorithm to collapse all of the + spectra into 1 + """ + # More verbose until the child algorithm stuff is sorted + sum_spectra = self.createChildAlgorithm("SumSpectra") + sum_spectra.setLogging(_LOGGING_) + sum_spectra.setProperty("InputWorkspace", self.foil_out) + sum_spectra.setProperty("OutputWorkspace", self.foil_out) + sum_spectra.execute() + + self.foil_out = sum_spectra.getProperty("OutputWorkspace").value + #---------------------------------------------------------------------------------------- def _get_header_format(self, ip_filename): """ diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/MaskAngle.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/MaskAngle.py new file mode 100644 index 000000000000..b1f13da793c1 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/MaskAngle.py @@ -0,0 +1,60 @@ +"""*WIKI* +Algorithm to mask detectors with scattering angles in a given interval (in degrees) +By default MinAngle=0, MaxAngle=180, so if no keywords are set, all detectors are going to be masked +Returns a list of detectors that were masked +*WIKI*""" + +import mantid.simpleapi +import mantid.kernel +import mantid.api +import numpy + +class MaskAngle(mantid.api.PythonAlgorithm): + """ Class to generate grouping file + """ + + def category(self): + """ Mantid required + """ + return "PythonAlgorithms;Transforms\\Masking" + + def name(self): + """ Mantid require + """ + return "MaskAngle" + + def PyInit(self): + self.declareProperty(mantid.api.WorkspaceProperty("Workspace", "",direction=mantid.kernel.Direction.Input,validator=mantid.api.InstrumentValidator()), "Input workspace") + angleValidator=mantid.kernel.FloatBoundedValidator() + angleValidator.setBounds(0.,180.) + self.declareProperty(name="MinAngle", defaultValue=0.0, validator=angleValidator, direction=mantid.kernel.Direction.Input, doc="Angles above StartAngle are going to be masked") + self.declareProperty(name="MaxAngle", defaultValue=0.0, validator=angleValidator, direction=mantid.kernel.Direction.Input, doc="Angles above StartAngle are going to be masked") + self.declareProperty(mantid.kernel.IntArrayProperty(name="MaskedDetectors", direction=mantid.kernel.Direction.Output), doc="List of detector masked, with scatterin angles between MinAngle and MaxAngle") + + def PyExec(self): + ws = self.getProperty("Workspace").value + ttmin = self.getProperty("MinAngle").value + ttmax = self.getProperty("MaxAngle").value + if ttmin > ttmax : + raise ValueError("MinAngle > MaxAngle, please check angle range for masking") + + detlist=[] + + + numspec = ws.getNumberHistograms() + source=ws.getInstrument().getSource().getPos() + sample=ws.getInstrument().getSample().getPos() + for i in range(numspec): + det=ws.getDetector(i) + if not det.isMonitor(): + tt=numpy.degrees(det.getTwoTheta(sample,sample-source)) + if tt>= ttmin and tt<= ttmax: + detlist.append(det.getID()) + + if len(detlist)> 0: + mantid.simpleapi.MaskDetectors(Workspace=ws,DetectorList=detlist) + else: + self.log().information("no detectors within this range") + self.setProperty("MaskedDetectors", numpy.array(detlist)) + +mantid.api.AlgorithmFactory.subscribe(MaskAngle) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/MaskBTP.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/MaskBTP.py new file mode 100644 index 000000000000..cb65d22e2b6e --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/MaskBTP.py @@ -0,0 +1,171 @@ +"""*WIKI* +Algorithm to mask detectors in particular banks, tube, or pixels. It applies to the following instruments only: ARCS, CNCS, HYSPEC, SEQUOIA. +If one of Bank, Tube, Pixel entries is left blank, it will apply to all elements of that type. For example: + +MaskBTP(w,Bank = "1") will completely mask all tubes and pixels in bank 1. +MaskBTP(w,Pixel = "1,2") will mask all pixels 1 and 2, in all tubes, in all banks. + +The algorithm allows ranged inputs: Pixel = "1-8,121-128" is equivalent to Pixel = "1,2,3,4,5,6,7,8,121,122,123,124,125,126,127,128" + +Either the input workspace or the instrument must be set + +*WIKI*""" + +import mantid.simpleapi +import mantid.api +import mantid.kernel +import numpy + +class MaskBTP(mantid.api.PythonAlgorithm): + """ Class to generate grouping file + """ + + def category(self): + """ Mantid required + """ + return "PythonAlgorithms;Transforms\\Masking;Inelastic" + + def name(self): + """ Mantid require + """ + return "MaskBTP" + + + def PyInit(self): + self.declareProperty(mantid.api.WorkspaceProperty("Workspace", "",direction=mantid.kernel.Direction.InOut, optional = mantid.api.PropertyMode.Optional), "Input workspace (optional)") + allowedInstrumentList=mantid.kernel.StringListValidator(["","ARCS","CNCS","HYSPEC","SEQUOIA"]) + self.declareProperty("Instrument","",validator=allowedInstrumentList,doc="One of the following instruments: ARCS, CNCS, HYSPEC, SEQUOIA") + self.declareProperty("Bank","",doc="Bank(s) to be masked. If empty, will apply to all banks") + self.declareProperty("Tube","",doc="Tube(s) to be masked. If empty, will apply to all tubes") + self.declareProperty("Pixel","",doc="Pixel(s) to be masked. If empty, will apply to all pixels") + self.declareProperty(mantid.kernel.IntArrayProperty(name="MaskedDetectors", direction=mantid.kernel.Direction.Output), doc="List of masked detectors") + + + def PyExec(self): + ws = self.getProperty("Workspace").value + self.instrument=None + self.instname = self.getProperty("Instrument").value + bankString = self.getProperty("Bank").value + tubeString = self.getProperty("Tube").value + pixelString = self.getProperty("Pixel").value + + if self.instname == "" and ws == None: + raise ValueError("No workspace or instrument were selected" ) + + if ws != None: + self.instrument = ws.getInstrument() + self.instname = self.instrument.getName() + + instrumentList=["ARCS","CNCS","HYSPEC","SEQUOIA"] + try: + instrumentList.index(self.instname) + except: + raise ValueError("Instrument "+self.instname+" not in the allowed list") + + if (self.instrument==None): + IDF=mantid.api.ExperimentInfo.getInstrumentFilename(self.instname) + ws=mantid.simpleapi.LoadEmptyInstrument(IDF,OutputWorkspace=self.instname+"MaskBTP") + self.instrument=ws.getInstrument() + + if (bankString == ""): + if (self.instname == "ARCS"): + banks=numpy.arange(115)+1 + elif (self.instname == "CNCS"): + banks=numpy.arange(50)+1 + elif (self.instname == "HYSPEC"): + banks=numpy.arange(20)+1 + elif (self.instname == "SEQUOIA"): + banks=numpy.arange(113)+38 + else: + banks=self._parseBTPlist(bankString) + + if (tubeString == ""): + tubes=numpy.arange(8)+1 + else: + tubes=self._parseBTPlist(tubeString) + + if(pixelString == ""): + pixels=numpy.arange(128)+1 + else: + pixels=self._parseBTPlist(pixelString) + + + + detlist=[] + for b in banks: + ep=self._getEightPackHandle(b) + for t in tubes: + if ((t<1) or (t>8)): + raise ValueError("Out of range index for tube number") + else: + for p in pixels: + if ((p<1) or (p>128)): + raise ValueError("Out of range index for pixel number") + else: + pid=ep[int(t-1)][int(p-1)].getID() + detlist.append(pid) + if len(detlist)> 0: + mantid.simpleapi.MaskDetectors(Workspace=ws,DetectorList=detlist) + else: + self.log().information("no detectors within this range") + self.setProperty("MaskedDetectors", numpy.array(detlist)) + + def _parseBTPlist(self,value): + """ + Helper function to transform a string into a list of integers + For example "1,2-4,8-10" will become [1,2,3,4,8,9,10] + It will deal with lists as well, so range(1,4) will still be [1,2,3] + """ + parsed = [] + #split the commas + parts = str(value).strip(']').strip('[').split(',') + #now deal with the hyphens + for p in parts: + if len(p) > 0: + elem = p.split("-") + if len(elem) == 1: + parsed.append(int(elem[0])) + if len(elem) == 2: + startelem = int(elem[0]) + endelem = int(elem[1]) + if endelem < startelem: + raise ValueError("The element after the hyphen needs to be greater or equal than the first element") + elemlist = range(startelem,endelem+1) + parsed.extend(elemlist) + return parsed + + def _getEightPackHandle(self,banknum): + """ + Helper function to return the handle to a given eightpack + """ + banknum=int(banknum) + if self.instname=="ARCS": + if (1<=banknum<= 38): + return self.instrument[3][banknum-1][0] + elif(39<=banknum<= 77): + return self.instrument[4][banknum-39][0] + elif(78<=banknum<=115): + return self.instrument[5][banknum-78][0] + else: + raise ValueError("Out of range index for ARCS instrument bank numbers") + elif self.instname=="CNCS": + if (1<=banknum<= 50): + return self.instrument[3][banknum-1][0] + else: + raise ValueError("Out of range index for CNCS instrument bank numbers") + elif self.instname=="HYSPEC": + if (1<=banknum<= 20): + return self.instrument[3][banknum-1][0] + else: + raise ValueError("Out of range index for HYSPEC instrument bank numbers") + elif self.instname=="SEQUOIA": + if (38<=banknum<= 74): + return self.instrument[3][banknum-38][0] + elif(75<=banknum<= 113): + return self.instrument[4][banknum-75][0] + elif(114<=banknum<=150): + return self.instrument[5][banknum-114][0] + else: + raise ValueError("Out of range index for SEQUOIA instrument bank numbers") + +mantid.api.AlgorithmFactory.subscribe(MaskBTP) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/ReflectometryReductionOneAuto.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/ReflectometryReductionOneAuto.py new file mode 100644 index 000000000000..507b9eb3b071 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/ReflectometryReductionOneAuto.py @@ -0,0 +1,180 @@ +"""*WIKI* + +Facade over [[ReflectometryReductionOne]]. Pulls numeric parameters out of the instrument parameters where possible. You can override any of these automatically +applied defaults by providing your own value for the input. + +See [[ReflectometryReductionOne]] for more information on the wrapped algorithm. + +*WIKI*""" + + +import sys +from mantid.simpleapi import ReflectometryReductionOne +from mantid.api import * +from mantid.kernel import * + +class ReflectometryReductionOneAuto(PythonAlgorithm): + + def category(self): + return "Reflectometry\\ISIS" + + def name(self): + return "ReflectometryReductionOneAuto" + + def PyInit(self): + + input_validator = WorkspaceUnitValidator("TOF") + self.declareProperty(MatrixWorkspaceProperty(name="InputWorkspace", defaultValue="", direction=Direction.Input, optional=PropertyMode.Mandatory), "Input run in TOF") + + analysis_modes = ["PointDetectorAnalysis", "MultiDetectorAnalysis"] + analysis_mode_validator = StringListValidator(analysis_modes) + + self.declareProperty(IntArrayProperty(name="RegionOfDirectBeam", direction=Direction.Input), doc="Indices of the spectra a pair (lower, upper) that mark the ranges that correspond to the direct beam in multi-detector mode.") + + self.declareProperty(name="AnalysisMode", defaultValue=analysis_modes[0], validator=analysis_mode_validator, direction = Direction.Input, doc="Analysis Mode to Choose") + + self.declareProperty(MatrixWorkspaceProperty("FirstTransmissionRun", "", Direction.Input, optional=PropertyMode.Optional), "First transmission run workspace in TOF or Wavelength") + self.declareProperty(MatrixWorkspaceProperty("SecondTransmissionRun", "", optional=PropertyMode.Optional, validator=input_validator, direction=Direction.Input), "Second transmission run workspace in TOF") + self.declareProperty(MatrixWorkspaceProperty("OutputWorkspace", "", Direction.Output), "Output workspace in wavelength q") + self.declareProperty(MatrixWorkspaceProperty("OutputWorkspaceWavelength", "", Direction.Output), "Output workspace in wavelength") + + self.declareProperty(FloatArrayProperty(name="Params", values=[sys.maxint]), doc="Rebinning Parameters. See Rebin for format.") + self.declareProperty(name="StartOverlap", defaultValue=sys.float_info.max, doc="Overlap in Q.") + self.declareProperty(name="EndOverlap", defaultValue=sys.float_info.max, doc="End overlap in Q.") + index_bounds = IntBoundedValidator() + index_bounds.setLower(0) + + self.declareProperty(name="I0MonitorIndex", defaultValue=sys.maxint, validator=index_bounds, doc="I0 monitor index" ) + self.declareProperty(name="ProcessingInstructions", direction=Direction.Input, defaultValue="", doc="Processing commands to select and add spectrum to make a detector workspace. See [[PeformIndexOperations]] for syntax.") + self.declareProperty(name="WavelengthMin", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Wavelength Min in angstroms") + self.declareProperty(name="WavelengthMax", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Wavelength Max in angstroms") + self.declareProperty(name="WavelengthStep", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Wavelength step in angstroms") + self.declareProperty(name="MonitorBackgroundWavelengthMin", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Monitor wavelength background min in angstroms") + self.declareProperty(name="MonitorBackgroundWavelengthMax", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Monitor wavelength background max in angstroms") + self.declareProperty(name="MonitorIntegrationWavelengthMin", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Monitor integral min in angstroms") + self.declareProperty(name="MonitorIntegrationWavelengthMax", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Monitor integral max in angstroms") + + self.declareProperty(name="DetectorComponentName", direction=Direction.Input, defaultValue="", doc="Name of the detector component i.e. point-detector. If these are not specified, the algorithm will attempt lookup using a standard naming convention.") + self.declareProperty(name="SampleComponentName", direction=Direction.Input, defaultValue="", doc="Name of the sample component i.e. some-surface-holder. If these are not specified, the algorithm will attempt lookup using a standard naming convention." ) + + self.declareProperty(name="ThetaIn", direction=Direction.Input, defaultValue=sys.float_info.max, doc="Final theta in degrees") + self.declareProperty(name="ThetaOut", direction=Direction.Output, defaultValue=sys.float_info.max, doc="Calculated final theta in degrees.") + + self.declareProperty(name="CorrectDetectorPositions", defaultValue=True, direction=Direction.Input, doc="Correct detector positions using ThetaIn (if given)") + + + def value_or_none(self, prop_name): + property = self.getProperty(prop_name) + if property.isDefault: + return None # TODO: check this + else: + return property.value + + + def value_to_apply(self, prop_name, instrument, idf_name=""): + property = self.getProperty(prop_name) + if property.isDefault: + return instrument.getNumberParameter(idf_name)[0] + else: + return property.value + + def PyExec(self): + + in_ws = self.getProperty("InputWorkspace").value + instrument = in_ws.getInstrument() + + first_ws = self.getProperty("FirstTransmissionRun").value + + ''' + Get all the inputs. + ''' + output_workspace_name = self.getPropertyValue("OutputWorkspace") + + output_workspace_lam_name = self.getPropertyValue("OutputWorkspaceWavelength") + + analysis_mode = self.getPropertyValue("AnalysisMode") + + first_ws = self.value_or_none("FirstTransmissionRun") + + second_ws = self.value_or_none("SecondTransmissionRun") + + start_overlap = self.value_or_none("StartOverlap") + + end_overlap = self.value_or_none("EndOverlap") + + params = self.value_or_none("Params") + + i0_monitor_index = int(self.value_to_apply("I0MonitorIndex", instrument, "I0MonitorIndex")) + + processing_commands = str() + workspace_index_list = list() + if self.getProperty("ProcessingInstructions").isDefault: + if analysis_mode == "PointDetectorAnalysis": + workspace_index_list.append( int(instrument.getNumberParameter("PointDetectorStart")[0]) ) + workspace_index_list.append( int(instrument.getNumberParameter("PointDetectorStop")[0]) ) + else: + workspace_index_list.append( int(instrument.getNumberParameter("MultiDetectorStart")[0]) ) + workspace_index_list.append( first_ws.getNumberHistograms() - 1) + processing_commands = ','.join(map(str, workspace_index_list)) + else: + processing_commands = self.getProperty("ProcessingInstructions").value + + wavelength_min = self.value_to_apply("WavelengthMin", instrument, "LambdaMin") + + wavelength_max = self.value_to_apply("WavelengthMax", instrument, "LambdaMax") + + wavelength_step = self.value_or_none("WavelengthStep") + + wavelength_back_min = self.value_to_apply("MonitorBackgroundWavelengthMin", instrument, "MonitorBackgroundMin") + + wavelength_back_max = self.value_to_apply("MonitorBackgroundWavelengthMax", instrument, "MonitorBackgroundMax") + + wavelength_integration_min = self.value_to_apply("MonitorIntegrationWavelengthMin", instrument, "MonitorIntegralMin") + + wavelength_integration_max = self.value_to_apply("MonitorIntegrationWavelengthMax", instrument, "MonitorIntegralMax") + + detector_component_name = self.value_or_none("DetectorComponentName") + + sample_component_name = self.value_or_none("SampleComponentName") + + theta_in = self.value_or_none("ThetaIn") + + correct_positions = self.value_or_none("CorrectDetectorPositions") + + region_of_direct_beam = self.value_or_none("RegionOfDirectBeam") + + ''' + Pass the arguments and execute the main algorithm. + ''' + new_IvsQ1, new_IvsLam1, thetaOut1 = ReflectometryReductionOne( + InputWorkspace=in_ws, + AnalysisMode = analysis_mode, + FirstTransmissionRun=first_ws, + SecondTransmissionRun=second_ws, + OutputWorkspace=output_workspace_name, + OutputWorkspaceWavelength=output_workspace_lam_name, + StartOverlap=start_overlap, + EndOverlap=end_overlap, + Params=params, + I0MonitorIndex=i0_monitor_index, + ProcessingInstructions=processing_commands, + WavelengthMin=wavelength_min, + WavelengthStep=wavelength_step, + WavelengthMax=wavelength_max, + MonitorBackgroundWavelengthMin=wavelength_back_min, + MonitorBackgroundWavelengthMax=wavelength_back_max, + MonitorIntegrationWavelengthMin=wavelength_integration_min, + MonitorIntegrationWavelengthMax=wavelength_integration_max, + RegionOfDirectBeam=region_of_direct_beam, + DetectorComponentName=detector_component_name, + SampleComponentName=sample_component_name, + ThetaIn=theta_in, + CorrectDetectorPositions=correct_positions) + + self.setProperty("OutputWorkspace", new_IvsQ1) + self.setProperty("OutputWorkspaceWavelength", new_IvsLam1) + self.setProperty("ThetaOut", thetaOut1) + + + +AlgorithmFactory.subscribe(ReflectometryReductionOneAuto) \ No newline at end of file diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SeqRefineProfile.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SeqRefineProfile.py new file mode 100644 index 000000000000..aecd48524c34 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SeqRefineProfile.py @@ -0,0 +1,896 @@ +###################################################################### +# +# This is a partial copy from LeBailFitScript.py +# +# Python Script as Step 1 of Le Bail Fitting to +# 1. Load file +# 2. Create LeBailFitInput +# 3. Fit Peaks +# +# Step 1: Load data, model (HKL list) and starting instrument parameters values, +# and do an initial LeBailFit/calculation to see how much the starting +# values are off; +# +###################################################################### + +from mantid.api import * +import mantid.simpleapi as api +from mantid.api import * +from mantid.kernel import * + + +#from Calibration_ImportInformation import * + + +#-------------------------------------------------------------------------------- +class RefinePowderDiffProfileSeq(PythonAlgorithm): + """ Refine powder diffractometer profile by Le Bail algorithm sequentially + """ + def category(self): + """ Category + """ + return "Diffraction" + + def name(self): + """ Algorithm name + """ + return "RefinePowderDiffProfileSeq" + + def PyInit(self): + """ Declare properties + """ + self.setWikiSummary("""Refine powder diffractomer profile parameters sequentially.""") + + self.declareProperty(MatrixWorkspaceProperty("InputWorkspace", "", Direction.Input, PropertyMode.Optional), + "Name of data workspace containing the diffraction pattern in .prf file. ") + + self.declareProperty("WorkspaceIndex", 0, "Spectrum (workspace index starting from 0) of the data to refine against in input workspace.") + + self.declareProperty(ITableWorkspaceProperty("SeqControlInfoWorkspace", "", Direction.InOut, PropertyMode.Optional), + "Name of table workspace containing sequential refinement information.") + + self.declareProperty(ITableWorkspaceProperty("InputProfileWorkspace", "", Direction.Input, PropertyMode.Optional), + "Name of table workspace containing starting profile parameters.") + + self.declareProperty(ITableWorkspaceProperty("InputBraggPeaksWorkspace", "", Direction.Input, PropertyMode.Optional), + "Name of table workspace containing a list of reflections. ") + + self.declareProperty(ITableWorkspaceProperty("InputBackgroundParameterWorkspace", "", Direction.Input, + PropertyMode.Optional), "Name of table workspace containing a list of reflections. ") + + self.declareProperty("StartX", -0., "Start X (TOF) to refine diffraction pattern.") + self.declareProperty("EndX", -0., "End X (TOF) to refine diffraction pattern.") + + funcoptions = ["Setup", "Refine", "Save", "Load"] + self.declareProperty("FunctionOption", "Refine", StringListValidator(funcoptions), "Options of functionality") + + #refoptions = ["Levenberg-Marquardt", "Random Walk", "Single Peak Fit"] + refoptions = ["Random Walk"] + self.declareProperty("RefinementOption", "Random Walk", StringListValidator(refoptions), + "Options of algorithm to refine. ") + + self.declareProperty(StringArrayProperty("ParametersToRefine", values=[], direction=Direction.Input), + "List of parameters to refine.") + + self.declareProperty("NumRefineCycles", 1, "Number of refinement cycles.") + + peaktypes = ["", "Neutron Back-to-back exponential convoluted with pseudo-voigt", + "Thermal neutron Back-to-back exponential convoluted with pseudo-voigt"] + self.declareProperty("ProfileType", "", StringListValidator(peaktypes), "Type of peak profile function.") + + bkgdtypes = ["", "Polynomial", "Chebyshev", "FullprofPolynomial"] + self.declareProperty("BackgroundType", "", StringListValidator(bkgdtypes), "Type of background function.") + + self.declareProperty("FromStep", -1, "If non-negative, the previous code is not set from last step, but the step specified.") + + # Property for save project + self.declareProperty(FileProperty("OutputProjectFilename","", FileAction.OptionalSave, ['.nxs']), + "Name of sequential project file.") + + # Property for save project + self.declareProperty(FileProperty("InputProjectFilename","", FileAction.OptionalLoad, ['.nxs']), + "Name of sequential project file.") + + # Project ID + self.declareProperty("ProjectID", "", "Project ID.") + + return + + + def PyExec(self): + """ Main + """ + # Process input + self._processInputProperties() + + # Instantiaze sequential refinement + seqrefine = SeqRefineProfile(self._projectID, self.log()) + + # Execute + if self.functionoption == "Setup": + # Set up + if seqrefine.isSetup() is True: + raise NotImplementedError("Impossible to have it set up already.") + + seqrefine.initSetup(self.dataws, self.wsindex, self.peaktype, self.profilews, self.braggpeakws, self.bkgdtype, + self.bkgdparws, self.startx, self.endx) + + elif self.functionoption == "Refine": + # Refine + if seqrefine.isSetup() is False: + raise NotImplementedError("Exception because sequential refinement is not set up.") + seqrefine.refine(self.dataws, self.wsindex, self.paramstofit, self.numcycles, self.startx, self.endx, self._lastStep) + + elif self.functionoption == "Save": + # Save the current state to a project file + seqrefine.saveProject(str(self.dataws), self.wsindex, self.outprojectfilename) + + elif self.functionoption == "Load": + # Set up from an exiting project file + if seqrefine.isSetup() is True: + raise NotImplementedError("Impossible to have it set up already.") + + seqrefine.loadProject(self.inprojectfilename) + + else: + # None-support + raise NotImplementedError("Function is not supported.") + + return + + + def _processInputProperties(self): + """ Process input properties + """ + # Input data workspace and related + self.dataws = self.getProperty("InputWorkspace").value + self.wsindex = self.getProperty("WorkspaceIndex").value + + self.startx = self.getProperty("StartX").value + self.endx = self.getProperty("EndX").value + + self._lastStep = self.getProperty("FromStep").value + + self._projectID = self.getProperty("ProjectID").value + if len(self._projectID) == 0: + raise NotImplementedError("User must specify project ID.") + + self.functionoption = self.getProperty("FunctionOption").value + if self.functionoption == "Setup": + # Request on 'Setup' + ptype = self.getProperty("ProfileType").value + if ptype == "Neutron Back-to-back exponential convoluted with pseudo-voigt": + self.peaktype = "NeutronBk2BkExpConvPVoigt" + elif ptype == "Thermal neutron Back-to-back exponential convoluted with pseudo-voigt": + self.peaktype = "ThermalNeutronBk2BkExpConvPVoigt" + else: + raise NotImplementedError("Peak profile is not supported.") + + self.bkgdtype = self.getProperty("BackgroundType").value + self.bkgdparws = self.getProperty("InputBackgroundParameterWorkspace").value + self.profilews = self.getProperty("InputProfileWorkspace").value + self.braggpeakws = self.getProperty("InputBraggPeaksWorkspace").value + + + elif self.functionoption == "Refine": + self.paramstofit = self.getProperty("ParametersToRefine").value + self.numcycles = self.getProperty("NumRefineCycles").value + + elif self.functionoption == "Save": + self.outprojectfilename = self.getProperty("OutputProjectFilename").value + + elif self.functionoption == "Load": + self.inprojectfilename = self.getProperty("InputProjectFilename").value + + else: + raise NotImplementedError("Unsupported function mode %s. " % (self.functionoption)) + + if self.functionoption != "Load": + self.datawsname = str(self.dataws) + if self.wsindex < 0 or self.wsindex >= self.dataws.getNumberHistograms(): + raise NotImplementedError("Input workspace index %d is out of range (0, %d)." % + (self.wsindex, self.dataws.getNumberHistograms())) + + return + + +#-------------------------------------------------------------------- +# +#-------------------------------------------------------------------- + +class SeqRefineProfile: + """ A class to do sequential refinement on peak profile + + Use case: + 1. Set class object such as : ID/StartDate/Directory + 2. Run main to refine some parameters + 3. Check result + 4. If no further instruction, only need to set up parameters to refine + the input/starting values should be from the last + """ + def __init__(self, ID, glog): + """ + """ + # Set up log + self.glog = glog + + # Set up ID + self._ID = str(ID) + self.glog.information("SeqRefineProfile is initialized with ID = %s" % (str(ID))) + + # Standard record table and check its existence + self._recordwsname = "Record%sTable" % (str(ID)) + self.glog.notice("Using record table %s" % (self._recordwsname)) + + if AnalysisDataService.doesExist(self._recordwsname): + # Record workspace exists: has been set up + self._isSetup = True + else: + # Record workspace does not exist: first time or need to load from file + self._isSetup = False + + self._recordWSLastRowInvalid = False + + # Result workspace group + self._wsgroupName = self._ID + "_Group" + if AnalysisDataService.doesExist(self._wsgroupName): + self._wsgroupCreated = True + else: + self._wsgroupCreated = False + + return + + + def initSetup(self, dataws, wsindex, peaktype, profilews, braggpeakws, bkgdtype, bkgdparws, startx, endx): + """ Set up the properties for LeBailFit as the first time including + do a Le bail calculation based on the input parameters + including profilews, braggpeakws, and etc + """ + # Data and data range + self._datawsname = str(dataws) + if startx <= 0.: + startx = dataws.readX(wsindex)[0] + if endx <= 0.: + endx = dataws.readX(wsindex)[-1] + + # Profile + self._peakType = peaktype + self._profileWS = profilews + self._braggpeakws = braggpeakws + self._bkgdtype = bkgdtype + self._bkgdparws = bkgdparws + + # Generate record table + self._genRecordTable() + + # Check input parameters, i.e., verification/examine input parameters + runner = RefineProfileParameters(self.glog) + + outwsname = self._datawsname+"_Init" + + runner.setInputs(self._datawsname, self._peakType, self._profileWS, self._braggpeakws, self._bkgdtype, self._bkgdparws) + # FIXME - Need to verify whether input and output background parameter ws name can be same + runner.setOutputs(outwsname, self._profileWS, self._braggpeakws, self._bkgdparws) + + self._recordPreRefineInfo(runner, -1) + runner.calculate(startx, endx) + self._recordPostRefineInfo(runner) + + # Group the newly generated workspace and do some record + api.GroupWorkspaces(InputWorkspaces="%s, %s, %s, %s" % (outwsname, self._profileWS, self._braggpeakws, self._bkgdparws), + OutputWorkspace=self._wsgroupName) + self._wsgroupCreated = True + + # Repository + + # Replace 'Refine' of step 0 to ID (it is always empty) + self._recordws.setCell(0, 5, self._ID) + # Replace 'InputProfileWorkspace' by profile type (it is alwasy same as output) + self._recordws.setCell(0, 9, self._peakType) + + self._isSetup = True + + return + + def loadProject(self, projectfilename): + """ Load the project from a saved project file + """ + # Load workspace group + api.LoadNexusProcessed(Filename=projectfilename, OutputWorkspace=self._wsgroupName) + self._wsgroup = AnalysisDataService.retrieve(self._wsgroupName) + + if self._wsgroup.__class__.__name__ != "WorkspaceGroup": + raise NotImplementedError("Input is not a workspace group but a %s" % (self._wsgroup.__class__.__name__)) + else: + self._wsgroupCreated = True + + # Parse README + wsnames = self._wsgroup.getNames() + readmewsname = None + for wsname in wsnames: + if wsname.startswith("READ"): + readmewsname = wsname + break + if readmewsname is None: + raise NotImplementedError("No README workspace is found in loaded workspace group.") + + readmews = AnalysisDataService.retrieve(readmewsname) + infodict = {} + numrows = readmews.rowCount() + self.glog.information("Found %d rows in workspace %s" % (numrows, str(readmews))) + for r in xrange(numrows): + functioncat = str(readmews.cell(r, 0)).strip() + functiontype = str(readmews.cell(r, 1)).strip() + infodict[functioncat] = functiontype.strip() + self.glog.information("README keys: %s" % (infodict.keys())) + self._peakType = infodict["Peak"] + self.datawsname = infodict["Data"] + self.wsindex = infodict["Spectrum"] + if self._ID != infodict["ID"]: + raise NotImplementedError("ID mismatch!") + + self._recordwsname = infodict["Record"] + + self._isSetup = True + + return + + def refine(self, dataws, wsindex, parametersToFit, numcycles, startx, endx, laststepindex): + """ Refine parameters + """ + # Range of fit + if startx <= 0.: + startx = dataws.readX(wsindex)[0] + if endx <= 0.: + endx = dataws.readX(wsindex)[-1] + + # Set up RefineProfileParameters object + runner = RefineProfileParameters(self.glog) + + # Locate refinement record table + profilewsname, braggpeakwsname, bkgdtype, bkgdparamwsname, laststep = self._parseRecordTable(laststepindex) + + # Set up runner and refine + runner.setupMonteCarloRefine(numcycles, parametersToFit) + + outwsname, outprofilewsname, outbraggpeakwsname = self._genOutputWorkspace(str(dataws), profilewsname, braggpeakwsname) + + # Set up input and output + runner.setInputs(str(dataws), self._peakType, profilewsname, braggpeakwsname, bkgdtype, bkgdparamwsname) + # FIXME - Need to verify whether input and output background parameter ws name can be same + runner.setOutputs(outwsname, outprofilewsname, outbraggpeakwsname, bkgdparamwsname) + + + # Refine and record pre and post refinement information + self._recordPreRefineInfo(runner, laststep) + runner.refine(numcycles, parametersToFit, startx, endx) + self._recordPostRefineInfo(runner) + + # Group newly generated workspaces and add name to reposiotry + if self._wsgroupCreated is True: + api.GroupWorkspaces(InputWorkspaces="%s, %s, %s, %s" % (outwsname, outprofilewsname, outbraggpeakwsname, self._wsgroupName), + OutputWorkspace=self._wsgroupName) + else: + wsgroup = AnalysisDataService.retrieve(self._wsgroupName) + hasbkgd = list(wsgroup.getNames()).count(bkgdparamwsname) + if hasbkgd == 1: + api.GroupWorkspaces(InputWorkspaces="%s, %s, %s" % (outwsname, outprofilewsname, outbraggpeakwsname), + OutputWorkspace=self._wsgroupName) + elif hasbkgd == 0: + api.GroupWorkspaces(InputWorkspaces="%s, %s, %s, %s" % (outwsname, outprofilewsname, outbraggpeakwsname, bkgdparamwsname), + OutputWorkspace=self._wsgroupName) + else: + raise NotImplementedError("Impossible to have 1 workspace appeared twice in a workspace group.") + + return + + def isSetup(self): + """ Status whether refinement is set up. + """ + return self._isSetup + + + def saveProject(self, datawsname, wsindex, projectfname): + """ Save current to a project file + Note: MC setup table workspace is not generated in this class. So it won't be saved + """ + import os + + # FIXME - Find out a good way to remove existing files/directories + if os.path.exists(projectfname) is True: + import shutil + try: + os.remove(projectfname) + except RuntimeError: + shutil.rmtree(projectfname) + except IOError: + shutil.rmtree(projectfname) + except OSError: + shutil.rmtree(projectfname) + + api.SaveNexusProcessed(InputWorkspace=self._wsgroupName, Filename=projectfname, Append=False) + + # Add data workspace, tracking record table to workspaces + # api.GroupWorkspaces(InputWorkspaces="%s, %s, %s" % (datawsname, self._recordwsname, self._wsgroupName), + # OutputWorkspace=self._wsgroupName) + self.glog.notice("Append record workspace %s" % (self._recordwsname)) + api.SaveNexusProcessed(InputWorkspace=self._recordwsname, Filename=projectfname, Append=True) + + self.glog.notice("Append data workspace %s" % (datawsname)) + api.SaveNexusProcessed(InputWorkspace=datawsname, Filename=projectfname, Append=True) + + # Create a new README table workspace for some other information + readmewsname = "READ_%s" % (self._ID) + readmews = api.CreateEmptyTableWorkspace(OutputWorkspace=readmewsname) + readmews.addColumn("str", "Function") + readmews.addColumn("str", "Type") + + readmews.addRow(["Peak", "Not Important"]) + readmews.addRow(["Background", "Not Important"]) + readmews.addRow(["ID", str(self._ID)]) + readmews.addRow(["Record", self._recordwsname]) + readmews.addRow(["Data", str(datawsname)]) + readmews.addRow(["Spectrum", str(wsindex)]) + + api.SaveNexusProcessed(InputWorkspace=readmewsname, Filename=projectfname, Append=True) + + return + + def _genRecordTable(self): + """ Generate record table + """ + tablews = api.CreateEmptyTableWorkspace(OutputWorkspace=self._recordwsname) + + tablews.addColumn("int", "Step") + tablews.addColumn("str", "OutProfile") + tablews.addColumn("str", "OutReflection") + tablews.addColumn("str", "OutBackgroud") + tablews.addColumn("str", "OutBckgroundParam") + tablews.addColumn("str", "Refine") + tablews.addColumn("double", "RwpOut") + tablews.addColumn("int", "LastStep") + tablews.addColumn("double", "RwpIn") + tablews.addColumn("str", "InProfile") + tablews.addColumn("str", "InReflection") + tablews.addColumn("str", "InBackgroud") + tablews.addColumn("str", "InBckgroundParam") + + self._recordws = tablews + + return + + def _parseRecordTable(self, laststep): + """ Parse record table and return the last refinement result + Notice that 'last row' in record table might not be a valid row (incomplete). + It might be caused by an exception raised in refinement or its setup. + Class variable _recordWSLastRowInvalid is used to indicate this + """ + # Retrieve record workspace + self._recordws = AnalysisDataService.retrieve(str(self._recordwsname)) + numrows = self._recordws.rowCount() + if numrows == 0: + raise NotImplementedError("Empty record table workspace. ") + + # Find last valid row + lastvalidrow = -1 + lastrow = numrows-1 + self._recordwsLastRowValid = False + while self._recordwsLastRowValid is False and lastrow >= 0: + profilewsname = self._recordws.cell(lastrow, 1) + if profilewsname == "": + self.glog.warning("Profile workspace name is emtpy in row %d!" % (lastrow)) + lastrow -= 1 + else: + self._recordwsLastRowValid = True + lastvalidrow = lastrow + # ENDWHILE + if lastvalidrow < 0: + raise NotImplementedError("XXX") + + # Find out last step row + lastrecordedstep = self._recordws.cell(lastvalidrow, 0) + self.glog.notice("Last recorded valid step is %d. " % (lastrecordedstep)) + + self._lastValidStep = lastrecordedstep + self._lastValidRowIndex = lastvalidrow + + if laststep > lastrecordedstep: + self.glog.warning("Last step %d is not recorded. Using step %d instead. " % + (laststep, lastrecordedstep)) + laststep = lastrecordedstep + elif laststep < 0: + self.glog.notice("Using default last valid step %d. " % (self._lastValidStep)) + laststep = self._lastValidStep + + profilewsname = "" + while lastvalidrow >= 0: + step = self._recordws.cell(lastvalidrow, 0) + if step != laststep: + lastvalidrow -= 1 + else: + profilewsname = self._recordws.cell(lastvalidrow, 1).strip() + reflectwsname = self._recordws.cell(lastvalidrow, 2).strip() + bkgdtype = self._recordws.cell(lastrow, 3).strip() + bkgdparamwsname = self._recordws.cell(lastrow, 4).strip() + if profilewsname == "": + raise NotImplementedError("Profile workspace name is emtpy in row %d. It is not supposed to happen." % + (lastvalidrow)) + break + # ENDWHILE + if profilewsname == "": + raise NotImplementedError("Step %d is not found in record table. It is impossible. " % + (laststep)) + + # Current step + self._currstep = self._lastValidStep + 1 + self.glog.notice("Current step is %d" % (self._currstep)) + + # Set up for other informatin + # Peak type + self._peakType = self._recordws.cell(0, 9).strip() + # Background type + self._bkgdType = bkgdtype.strip() + + return (profilewsname, reflectwsname, bkgdtype, bkgdparamwsname, laststep) + + + def _recordPreRefineInfo(self, refiner, laststep): + """ Record pre-refinement information + """ + rectablews = mtd[self._recordwsname] + numrows = rectablews.rowCount() + + if self._recordWSLastRowInvalid is False: + self._currstep = numrows + rectablews.addRow([self._currstep, "", "", "", "", "", -1.0, laststep, -1.0, "profilews", + "reflectionws", "Polynomial", "BkgdParm"]) + else: + self._currstep = numrows-1 + laststep = self._lastValidStep + + # print "*** Record workspace has %d rows. current step = %d. " % (rectablews.rowCount(), self._currstep) + + if len(refiner.paramToFit) > 0: + rectablews.setCell(self._currstep, 5, str(refiner.paramToFit)) + rectablews.setCell(self._currstep, 9, str(refiner.inprofilewsname)) + rectablews.setCell(self._currstep, 10, str(refiner.inreflectionwsname)) + rectablews.setCell(self._currstep, 11, str(refiner.bkgdtype)) + rectablews.setCell(self._currstep, 12, str(refiner.bkgdtablewsname)) + + return + + def _recordPostRefineInfo(self, refiner): + """ Record post-refinement information, i.e., refinement result + """ + # Parse profile table workspace + # print "****** outprofilews type = ", type(refiner.outprofilewsname) + outprofilews = AnalysisDataService.retrieve(str(refiner.outprofilewsname)) + # outprofilews = api.mtd[refiner.outprofilewsname] + # FIXME - Use Name[0], Value[1] as default + numpars = outprofilews.rowCount() + rwp = None + for i in xrange(numpars): + parname = outprofilews.cell(i, 0) + if parname.lower() == "rwp": + rwp = outprofilews.cell(i, 1) + break + + # Set the record table workspace + rectablews = mtd[self._recordwsname] + numrows = rectablews.rowCount() + # currstep = numrows-1 + + rectablews.setCell(self._currstep, 1, str(refiner.outprofilewsname)) + rectablews.setCell(self._currstep, 2, str(refiner.outreflectionwsname)) + rectablews.setCell(self._currstep, 3, str(refiner.bkgdtype)) + rectablews.setCell(self._currstep, 4, str(refiner.bkgdtablewsname)) + if rwp is not None: + rectablews.setCell(self._currstep, 6, rwp) + + return + + def _genOutputWorkspace(self, datawsname, profilewsname, braggpeakwsname): + """ + """ + outwsname = "%s_%s_Step%d" % (datawsname, self._ID, self._currstep) + + if profilewsname.count(self._ID) > 0: + outprofilewsname = profilewsname.split(self._ID)[0] + else: + outprofilewsname = profilewsname + outprofilewsname = "%s%s_Step%d" % (outprofilewsname, self._ID, self._currstep) + + if braggpeakwsname.count(str(self._ID)) > 0: + outbpwsname = braggpeakwsname.split(self._ID)[0] + else: + outbpwsname = braggpeakwsname + outbpwsname = "%s%s_Step%d"%(outbpwsname, self._ID, self._currstep) + + return (outwsname, outprofilewsname, outbpwsname) + +#-------------------------------------------------------------------- + +def generateMCSetupTableProf9(wsname): + """ Generate a Le Bail fit Monte Carlo random walk setup table + """ + tablews = api.CreateEmptyTableWorkspace(OutputWorkspace=str(wsname)) + + tablews.addColumn("str", "Name") + tablews.addColumn("double", "A0") + tablews.addColumn("double", "A1") + tablews.addColumn("int", "NonNegative") + tablews.addColumn("int", "Group") + + group = 0 + tablews.addRow(["Dtt1" , 5.0, 0.0, 0, group]) + tablews.addRow(["Dtt2" , 1.0, 0.0, 0, group]) + tablews.addRow(["Zero" , 5.0, 0.0, 0, group]) + + group = 1 + tablews.addRow(["Beta0" , 0.50, 1.0, 0, group]) + tablews.addRow(["Beta1" , 0.05, 1.0, 0, group]) + + group = 2 + tablews.addRow(["Alph0" , 0.05, 1.0, 0, group]) + tablews.addRow(["Alph1" , 0.02, 1.0, 0, group]) + + group = 3 + tablews.addRow(["Sig0", 2.0, 1.0, 1, group]) + tablews.addRow(["Sig1", 2.0, 1.0, 1, group]) + tablews.addRow(["Sig2", 2.0, 1.0, 1, group]) + + group = 4 + tablews.addRow(["Gam0", 2.0, 1.0, 0, group]) + tablews.addRow(["Gam1", 2.0, 1.0, 0, group]) + tablews.addRow(["Gam2", 2.0, 1.0, 0, group]) + + return tablews + +def generateMCSetupTableProf10(wsname): + """ Generate a Le Bail fit Monte Carlo random walk setup table + """ + import mantid.simpleapi as api + + tablews = api.CreateEmptyTableWorkspace(OutputWorkspace=str(wsname)) + + tablews.addColumn("str", "Name") + tablews.addColumn("double", "A0") + tablews.addColumn("double", "A1") + tablews.addColumn("int", "NonNegative") + tablews.addColumn("int", "Group") + + group = 0 + tablews.addRow(["Dtt1" , 5.0, 0.0, 0, group]) + tablews.addRow(["Dtt1t" , 5.0, 0.0, 0, group]) + tablews.addRow(["Dtt2t" , 1.0, 0.0, 0, group]) + tablews.addRow(["Zero" , 5.0, 0.0, 0, group]) + tablews.addRow(["Zerot" , 5.0, 0.0, 0, group]) + tablews.addRow(["Width" , 0.0, 0.1, 1, group]) + tablews.addRow(["Tcross", 0.0, 1.0, 1, group]) + + group = 1 + tablews.addRow(["Beta0" , 0.50, 1.0, 0, group]) + tablews.addRow(["Beta1" , 0.05, 1.0, 0, group]) + tablews.addRow(["Beta0t", 0.50, 1.0, 0, group]) + tablews.addRow(["Beta1t", 0.05, 1.0, 0, group]) + + group = 2 + tablews.addRow(["Alph0" , 0.05, 1.0, 0, group]) + tablews.addRow(["Alph1" , 0.02, 1.0, 0, group]) + tablews.addRow(["Alph0t", 0.10, 1.0, 0, group]) + tablews.addRow(["Alph1t", 0.05, 1.0, 0, group]) + + group = 3 + tablews.addRow(["Sig0", 2.0, 1.0, 1, group]) + tablews.addRow(["Sig1", 2.0, 1.0, 1, group]) + tablews.addRow(["Sig2", 2.0, 1.0, 1, group]) + + group = 4 + tablews.addRow(["Gam0", 2.0, 1.0, 0, group]) + tablews.addRow(["Gam1", 2.0, 1.0, 0, group]) + tablews.addRow(["Gam2", 2.0, 1.0, 0, group]) + + return tablews + +def breakParametersGroups(tablews): + """ Break the parameter groups. Such that each parameter/row has an individual group + """ + numrows = tablews.rowCount() + for ir in xrange(numrows): + tablews.setCell(ir, 4, ir) + + return + +def resetParametersGroups(tablews): + """ Set the group number to original setup + """ + numrows = tablews.rowCount() + for ir in xrange(numrows): + parname = tablews.cell(ir, 0) + if parname in ["Dtt1", "Dtt1t", "Dtt2t", "Zero", "Zerot", "Width", "Tcross"]: + group = 0 + elif parname in ["Beta0", "Beta1", "Beta0t", "Beta1t"]: + group = 1 + elif parname in ["Alph0", "Alph1", "Alph0t", "Alph1t"]: + group = 2 + elif parname in ["Sig0", "Sig1", "Sig2"]: + group = 3 + else: + group = 4 + tablews.setCell(ir, 4, group) + return + + +class RefineProfileParameters: + """ Class to refine profile parameters ONE step + """ + def __init__(self, glog): + """ Initialization + """ + self.peaktype = "NOSETUP" + + # Output + self.outwsname = None + + self.glog = glog + + self.numsteps = 0 + + # Refine + self.paramToFit = [] + + # Flags + self._inputIsSetup = False + self._outputIsSetup = False + + return + + def setInputs(self, datawsname, peaktype, profilewsname, braggpeakwsname, bkgdtype, bkgdparwsname): + """ + """ + self.datawsname = datawsname + self.peaktype = peaktype + self.inprofilewsname = profilewsname + self.inreflectionwsname = braggpeakwsname + self.bkgdtype = bkgdtype + self.bkgdtablewsname = bkgdparwsname + + self._inputIsSetup = True + + return + + def setOutputs(self, outwsname, profilewsname, braggpeakwsname, bkgdparwsname): + """ Set up the variables for output + """ + self.outwsname = outwsname + self.outprofilewsname = profilewsname + self.outreflectionwsname = braggpeakwsname + self.outbkgdtablewsname = bkgdparwsname + + self._outputIsSetup = True + + return + + def setupMonteCarloRefine(self, numcycles, parametersToFit): + """ Set up refinement parameters + """ + if numcycles <= 0: + raise NotImplementedError("It is not allowed to set up a 0 or a negative number to MonteCarloRefine") + else: + self.numsteps = numcycles + + self.paramToFit = parametersToFit + + return + + def calculate(self, startx, endx): + """ Do Le bail calculation + """ + if (self._inputIsSetup and self._outputIsSetup) is False: + raise NotImplementedError("Either input or output is not setup: inputIsStepUp = %s, outputIsSetup = %s" % + (str(self._inputIsSetup), str(self._outputIsSetup))) + + self.glog.information("**** Calculate: DataWorksapce = %s" % (str(self.datawsname))) + self.glog.information("**** Fit range: %f, %f" % (startx, endx)) + self.glog.information("**** Profile workspace = %s, Reflection workspace = %s" % ( + self.inprofilewsname, self.inreflectionwsname)) + + api.LeBailFit( + Function = 'Calculation', + InputWorkspace = self.datawsname, + OutputWorkspace = self.outwsname, + InputParameterWorkspace = self.inprofilewsname, + OutputParameterWorkspace= self.outprofilewsname, + InputHKLWorkspace = self.inreflectionwsname, + OutputPeaksWorkspace = self.outreflectionwsname, + FitRegion = '%f, %f' % (startx, endx), + PeakType = self.peaktype, + BackgroundType = self.bkgdtype, + UseInputPeakHeights = False, + PeakRadius = '8', + BackgroundParametersWorkspace = self.bkgdtablewsname + ) + + return + + + def refine(self, numsteps, parameternames, startx, endx): + """ Main execution body (doStep4) + """ + # Check validity + if (self._inputIsSetup and self._outputIsSetup) is False: + raise NotImplementedError("Either input or output is not setup.") + + self.glog.debug("[Refine] Input profile workspace = %s" % (self.inprofilewsname)) + + # Update parameters' fit table + if numsteps > 0: + # Set up the default parameters to refine + + # Set up the parameters to refine + # FIXME - It is found that in the 'load' mode, a ID???_Group_2 might be generated by running + # UpdatePeakParameterTableValue(). It is not a real new table workspace, but a link + # to the 'inprofilewsname' + # There must be something wrong in AnalysisDataService. + api.UpdatePeakParameterTableValue( + InputWorkspace = self.inprofilewsname, + Column = "FitOrTie", + NewStringValue = "tie") + api.UpdatePeakParameterTableValue( + InputWorkspace = self.inprofilewsname, + Column = "FitOrTie", + ParameterNames = parameternames, + NewStringValue = "fit") + + # Limit the range of MC + if parameternames.count("Width") > 0: + #self.cwl = 1.33 + UpdatePeakParameterTableValue( + InputWorkspace = self.inprofilewsname, + Column = "Min", + ParameterNames = ["Width"], + NewFloatValue = 0.50) #cwl*0.25) + + UpdatePeakParameterTableValue( + InputWorkspace = self.inprofilewsname, + Column = "Max", + ParameterNames = ["Width"], + NewFloatValue = 1.25) #cwl*4.0) + + # Generate Monte carlo table + wsname = "MCSetupParameterTable" + if self.peaktype == "NeutronBk2BkExpConvPVoigt": + tablews = generateMCSetupTableProf9(wsname) + elif self.peaktype == "ThermalNeutronBk2BkExpConvPVoigt": + tablews = generateMCSetupTableProf10(wsname) + else: + raise NotImplementedError("Peak type %s is not supported to set up MC table." % (self.peaktype)) + + api.LeBailFit( + InputWorkspace = self.datawsname, + OutputWorkspace = self.outwsname, + InputParameterWorkspace = self.inprofilewsname, + OutputParameterWorkspace = self.outprofilewsname, + InputHKLWorkspace = self.inreflectionwsname, + OutputPeaksWorkspace = self.outreflectionwsname, + FitRegion = '%f, %f' % (startx, endx), + Function = 'MonteCarlo', + NumberMinimizeSteps = numsteps, + PeakType = self.peaktype, + BackgroundType = self.bkgdtype, + BackgroundParametersWorkspace = self.bkgdtablewsname, + UseInputPeakHeights = False, + PeakRadius ='8', + Minimizer = 'Levenberg-Marquardt', + MCSetupWorkspace = str(wsname), + Damping = '5.0', + RandomSeed = 0, + AnnealingTemperature = 100.0, + DrunkenWalk = True) + # ENDIF (step) + + + return + +# Register algorithm with Mantid +AlgorithmFactory.subscribe(RefinePowderDiffProfileSeq) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SortDetectors.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SortDetectors.py new file mode 100644 index 000000000000..87804ea7c435 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SortDetectors.py @@ -0,0 +1,71 @@ +"""*WIKI* +Algorithm to sort detectors by distance. Will return arrays for upstream (downstrem) spectrum number and detector distances, ordered by distance. +*WIKI*""" + +from mantid.api import PythonAlgorithm, AlgorithmFactory,WorkspaceProperty,PropertyMode +from mantid.kernel import Direction,IntArrayProperty, FloatArrayProperty +import mantid,math,numpy + + + +class SortDetectors(PythonAlgorithm): + """ Sort detectors by distance + """ + def category(self): + """ Return category + """ + return "PythonAlgorithms;Utility" + + def name(self): + """ Return name + """ + return "SortDetectors" + + def PyInit(self): + """ Declare properties + """ + self.declareProperty(mantid.api.WorkspaceProperty("Workspace","",direction=mantid.kernel.Direction.Input, validator=mantid.api.InstrumentValidator()), "Input workspace") + + self.declareProperty(IntArrayProperty("UpstreamSpectra",Direction.Output)) + self.declareProperty(FloatArrayProperty("UpstreamDetectorDistances",Direction.Output)) + self.declareProperty(IntArrayProperty("DownstreamSpectra",Direction.Output)) + self.declareProperty(FloatArrayProperty("DownstreamDetectorDistances",Direction.Output)) + return + + def PyExec(self): + """ Main execution body + """ + w = self.getProperty("Workspace").value + samplePos=w.getInstrument().getSample().getPos() + moderatorPos=w.getInstrument().getSource().getPos() + incident=samplePos-moderatorPos + + upstream=[] + upinds=[] + updist=[] + downstream=[] + downinds=[] + downdist=[] + for i in range(w.getNumberHistograms()): + detPos=w.getDetector(i).getPos() + scattered=detPos-samplePos + if abs(scattered.angle(incident))>0.999*math.pi: + upstream.append((i,scattered.norm())) + else: + downstream.append((i,scattered.norm())) + + if len(upstream)>0: + upstream.sort(key=lambda x: x[1]) + upinds=zip(*upstream)[0] + updist=zip(*upstream)[1] + if len(downstream)>0: + downstream.sort(key=lambda x: x[1]) + downinds=zip(*downstream)[0] + downdist=zip(*downstream)[1] + + self.setProperty("UpstreamSpectra", numpy.array(upinds)) + self.setProperty("UpstreamDetectorDistances", numpy.array(updist)) + self.setProperty("DownstreamSpectra", numpy.array(downinds)) + self.setProperty("DownstreamDetectorDistances", numpy.array(downdist)) + +AlgorithmFactory.subscribe(SortDetectors) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SortXAxis.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SortXAxis.py index 2c847c26d22a..98f8aeb35be0 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SortXAxis.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SortXAxis.py @@ -1,6 +1,7 @@ """*WIKI* Clones the input [[MatrixWorkspace|Matrix Workspaces]] and orders the x-axis in an ascending fashion. Ensures that the y-axis and error data is sorted in a consistent way with the x-axis. +All x-values of the input workspace MUST be in either a descending or ascending fashion before passing to this algorithm. This algorithm is for use with small workspaces loaded. It is particularly suitable for reformatting workspaces loaded via [[LoadASCII]]. Input workspaces must be a distribution. @@ -10,6 +11,7 @@ from mantid.api import * from mantid.kernel import * import numpy as np +import operator class SortXAxis(PythonAlgorithm): @@ -20,9 +22,8 @@ def name(self): return "SortXAxis" def PyInit(self): - distributionValidator = HistogramValidator(False) - self.declareProperty(MatrixWorkspaceProperty("InputWorkspace", "", validator=distributionValidator, direction=Direction.Input), doc="Input workspace") - self.declareProperty(MatrixWorkspaceProperty("OutputWorkspace", "", Direction.Output), doc="Sorted Output Workspace") + self.declareProperty(MatrixWorkspaceProperty("InputWorkspace", defaultValue="", direction=Direction.Input), doc="Input workspace") + self.declareProperty(MatrixWorkspaceProperty("OutputWorkspace", defaultValue="", direction=Direction.Output), doc="Sorted Output Workspace") def PyExec(self): inputws = self.getProperty("InputWorkspace").value @@ -34,8 +35,12 @@ def PyExec(self): e = inputws.readE(i) indexes = x.argsort() xordered = x[indexes] + if inputws.isHistogramData(): + max_index = np.argmax(indexes) + indexes = np.delete(indexes, max_index) yordered = y[indexes] eordered = e[indexes] + outws.setX(i, xordered) outws.setY(i, yordered) outws.setE(i, eordered) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectTransmission.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectTransmission.py new file mode 100644 index 000000000000..024daf448b3c --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectTransmission.py @@ -0,0 +1,100 @@ +"""*WIKI* + +Calculates the scattering & transmission for Indirect Geometry spectrometers. The sample chemical formula is input for the SetSampleMaterial algorithm to calculate the cross-sections. +The instrument analyser reflection is selected to obtain the wavelength to calculate the absorption cross-section. The sample number density & thickness is input to then calculate the percentage scattering & transmission. + +*WIKI*""" + +from mantid.simpleapi import * +from mantid.api import * +from mantid.kernel import * +from mantid import config +import os.path, math + +class IndirectTransmission(PythonAlgorithm): + + def category(self): + return "Workflow\\MIDAS;PythonAlgorithms" + + def PyInit(self): + self.declareProperty(name='Instrument',defaultValue='IRIS',validator=StringListValidator(['IRIS','OSIRIS']), doc='Instrument') + self.declareProperty(name='Analyser',defaultValue='graphite',validator=StringListValidator(['graphite','fmica']), doc='Analyser') + self.declareProperty(name='Reflection',defaultValue='002',validator=StringListValidator(['002','004']), doc='Reflection') + self.declareProperty(name='ChemicalFormula',defaultValue='',validator=StringMandatoryValidator(), doc='Sample chemical formula') + self.declareProperty(name='NumberDensity', defaultValue=0.1, doc='Number denisty. Default=0.1') + self.declareProperty(name='Thickness', defaultValue=0.1, doc='Sample thickness. Default=0.1') + self.declareProperty(WorkspaceProperty('OutputWorkspace', "", Direction.Output), doc="The name of the output workspace.") + + def PyExec(self): + from IndirectCommon import StartTime, EndTime + + StartTime('IndirectTransmission') + + instrumentName = self.getPropertyValue('Instrument') + analyser = self.getPropertyValue('Analyser') + reflection = self.getPropertyValue('Reflection') + formula = self.getPropertyValue('ChemicalFormula') + density = self.getPropertyValue('NumberDensity') + thickness = self.getPropertyValue('Thickness') + + #Load instrument defintion file + idfDir = config['instrumentDefinition.directory'] + idf = idfDir + instrumentName + '_Definition.xml' + workspace = '__empty_'+instrumentName + LoadEmptyInstrument(OutputWorkspace=workspace, Filename=idf) + + #Load instrument parameter file + nameStem = instrumentName + '_' + analyser + '_' + reflection + ipf = idfDir + nameStem + '_Parameters.xml' + LoadParameterFile(Workspace=workspace, Filename=ipf) + + #Get efixed value + instrument = mtd[workspace].getInstrument() + efixed = instrument.getNumberParameter('efixed-val')[0] + + logger.notice('Analyser : ' +analyser+reflection +' with energy = ' + str(efixed)) + + result = SetSampleMaterial(InputWorkspace=workspace,ChemicalFormula=formula) + + #elastic wavelength + wave=1.8*math.sqrt(25.2429/efixed) + + absorptionXSection = result[5]*wave/1.7982 + coherentXSection = result[4] + incoherentXSection = result[3] + scatteringXSection = result[3] + + thickness = float(thickness) + density = float(density) + + totalXSection = absorptionXSection + scatteringXSection + + transmission = math.exp(-density*totalXSection*thickness) + scattering = 1.0 - math.exp(-density*scatteringXSection*thickness) + + #Create table workspace to store calculations + tableWs = self.getPropertyValue('OutputWorkspace') + tableWs = CreateEmptyTableWorkspace(OutputWorkspace=tableWs) + tableWs.addColumn("str", "Name") + tableWs.addColumn("double", "Value") + + # Names for each of the output values + outputNames = ['Wavelength', 'Absorption Xsection', 'Coherent Xsection', 'Incoherent Xsection', + 'Total scattering Xsection', 'Number density', 'Thickness', 'Transmission (abs+scatt)', 'Total scattering'] + + # List of the calculated values + outputValues = [wave, absorptionXSection, coherentXSection, incoherentXSection, + scatteringXSection, density, thickness, transmission, scattering] + + #build table of values + for data in zip (outputNames, outputValues): + tableWs.addRow(list(data)) + logger.information(': '.join(map(str,list(data)))) + + #remove idf/ipf workspace + DeleteWorkspace(workspace) + self.setProperty("OutputWorkspace", tableWs) + EndTime('IndirectTransmission') + +# Register algorithm with Mantid +AlgorithmFactory.subscribe(IndirectTransmission) \ No newline at end of file diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/OSIRISDiffractionReduction.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/OSIRISDiffractionReduction.py index b266dfa7c87b..b71d8c4d6aeb 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/OSIRISDiffractionReduction.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/OSIRISDiffractionReduction.py @@ -50,10 +50,20 @@ def addWs(self, wsname): ", which has a time regime of " + str(timeRegime)) # Add the workspace to the map, alongside its DRange. - if dRange in self._map: - self._map[dRange].append(wsname) - else: + if dRange not in self._map: self._map[dRange] = [wsname] + else: + #check if x ranges matchs and existing run + for ws_name in self._map[dRange]: + map_lastx = mtd[ws_name].readX(0)[-1] + ws_lastx = ws.readX(0)[-1] + + #if it matches ignore it + if map_lastx == ws_lastx: + DeleteWorkspace(ws) + return + + self._map[dRange].append(wsname) def setItem(self, dRange, wsname): """ Set a dRange and corresponding *single* ws. @@ -78,11 +88,14 @@ def averageWsList(wsList): for name in wsList: avName += "_" + name + numWorkspaces = len(wsList) + # Compute the average and put into "__temp_avg". - __temp_avg = mtd[wsList[0]] + mtd[wsList[1]] - for i in range(2, len(wsList) ): + __temp_avg = mtd[wsList[0]] + for i in range(1, numWorkspaces): __temp_avg += mtd[wsList[i]] - __temp_avg /= len(wsList) + + __temp_avg /= numWorkspaces # Rename the average ws and return it. RenameWorkspace(InputWorkspace=__temp_avg, OutputWorkspace=avName) @@ -240,6 +253,7 @@ def execDiffOnly(self, sampleRuns): # Create scalar data to cope with where merge has combined overlapping data. intersections = getIntersectionsOfRanges(self._samMap.getMap().keys()) + dataX = result.dataX(0) dataY = []; dataE = [] for i in range(0, len(dataX)-1): @@ -248,7 +262,7 @@ def execDiffOnly(self, sampleRuns): dataY.append(2); dataE.append(2) else: dataY.append(1); dataE.append(1) - + # apply scalar data to result workspace for i in range(0, result.getNumberHistograms()): resultY = result.dataY(i) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/functions/StretchedExpFT.py b/Code/Mantid/Framework/PythonInterface/plugins/functions/StretchedExpFT.py index 06ecbfd50a6b..d8a9d7cda272 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/functions/StretchedExpFT.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/functions/StretchedExpFT.py @@ -43,7 +43,7 @@ class StretchedExpFT(IFunction1D): def __init__(self): '''declare some constants''' super(StretchedExpFT, self).__init__() - self._meV2ps = 4.136 + self._h = 4.135665616 #meV*Thz self._parmset = set(['height','tau','beta']) #valid syntaxfor python >= 2.6 self._parm2index = {'height':0,'tau':1,'beta':2} #order in which they were defined @@ -86,8 +86,8 @@ def function1D(self, xvals, **optparms): 1/(M*dt) = xvals[1]-xvals[0] N/(M*dt) = max(abs(xvals)) Thus: - dt = 1/[M*(xvals[1]-xvals[0])] # M=2*N+1 N = max(abs(xvals)) / (xvals[1]-xvals[0]) + dt = 1/[M*(xvals[1]-xvals[0])] # M=2*N+1 Its Fourier transform is real by definition, thus we return the real part of the Fast Fourier Transform (FFT). The FFT step is meant to produce @@ -107,12 +107,15 @@ def function1D(self, xvals, **optparms): if optparms: if self._parmset.issubset( set(optparms.keys()) ): for name in self._parmset: p[name] = optparms[name] - de = xvals[1]-xvals[0] # meV (or ueV) , energy step - # make sure M > len(xvals) so that we can use interp1d later - N = 1+ int( max(np.abs(xvals)) / de ) - M = 2*N+1 - dt = self._meV2ps / (M*de) # ps ( or ns), time step - sampled_times = dt * np.arange(-N, N+1) + + de = (xvals[1]-xvals[0]) / 2 # increase the long-time range, increase the low-frequency resolution + emax = max( abs(xvals) ) + Nmax = 16000 # increase short-times resolution, increase the resolution of the structure factor tail + while ( emax / de ) < Nmax: + emax = 2 * emax + N = int( emax / de ) + dt = ( float(N) / ( 2 * N + 1 ) ) * (self._h / emax) # extent to negative times and t==0 + sampled_times = dt * np.arange(-N, N+1) # len( sampled_times ) < 64000 exponent = -(np.abs(sampled_times)/p['tau'])**p['beta'] freqs = de * np.arange(-N, N+1) fourier = p['height']*np.abs( scipy.fftpack.fft( np.exp(exponent) ).real ) diff --git a/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/ITableWorkspaceTest.py b/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/ITableWorkspaceTest.py index fbb13778cffa..c2d94fcb267d 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/ITableWorkspaceTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/ITableWorkspaceTest.py @@ -2,6 +2,7 @@ from testhelpers import run_algorithm from mantid.kernel import std_vector_str from mantid.api import WorkspaceFactory +import numpy class ITableWorkspaceTest(unittest.TestCase): @@ -108,6 +109,18 @@ def test_set_and_extract_boolean_columns(self): self.assertTrue(table.cell(0, 0)) self.assertFalse(table.cell(1, 0)) + + def test_set_and_extract_vector_columns(self): + table = WorkspaceFactory.createTable() + table.addColumn(type='vector_int', name='values') + + # Settings from general Python list + table.addRow([ [1,2,3,4,5] ]) + # Setting from numpy array + table.addRow([ numpy.array([6,7,8,9,10]) ]) + + self.assertTrue( numpy.array_equal( table.cell(0,0), numpy.array([1,2,3,4,5]) ) ) + self.assertTrue( numpy.array_equal( table.cell(1,0), numpy.array([6,7,8,9,10]) ) ) if __name__ == '__main__': unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/CMakeLists.txt b/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/CMakeLists.txt index 69847603cb80..43aec357253a 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/CMakeLists.txt @@ -5,6 +5,7 @@ set ( TEST_PY_FILES IComponentTest.py InstrumentTest.py OrientedLatticeTest.py + RectangularDetectorTest.py ReferenceFrameTest.py UnitCellTest.py ) diff --git a/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/RectangularDetectorTest.py b/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/RectangularDetectorTest.py new file mode 100644 index 000000000000..ab426a4ccd49 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/RectangularDetectorTest.py @@ -0,0 +1,33 @@ +import unittest +from mantid.geometry import * +from testhelpers import can_be_instantiated, WorkspaceCreationHelper + +class RectangularDetectorTest(unittest.TestCase): + + def test_RectangularDetector_cannot_be_instantiated(self): + self.assertFalse(can_be_instantiated(RectangularDetector)) + + def test_RectangularDetector_has_expected_attributes(self): + attrs = dir(RectangularDetector) + expected_attrs = ['idfillbyfirst_y','idstart', 'idstep','idstepbyrow', 'maxDetectorID', 'minDetectorID', 'xpixels', 'xsize', 'xstart', 'xstep', 'ypixels', 'ysize', 'ystart', 'ystep','type','nelements'] + for att in expected_attrs: + self.assertTrue(att in attrs) + + def test_RectangularDetector_getattributes(self): + testws = WorkspaceCreationHelper.create2DWorkspaceWithRectangularInstrument(3,5,5) + i = testws.getInstrument() + self.assertEquals(i[2].getName(),'bank3') + self.assertEquals(i[2][2].getName(),'bank3(x=2)') + self.assertEquals(i[2][2][2].getName(),'bank3(2,2)') + self.assertEquals(i[2].nelements(),5) + self.assertEquals(i[2].xstart()+i[2].xstep()*i[2].xpixels(),0.04) + self.assertEquals(i[1].ystart()+i[1].ystep()*i[1].ypixels(),0.04) + self.assertEquals(i[0].xsize(),0.04) + self.assertEquals(i[2].idstart(),75) + self.assertEquals(i[0].idstep(),1) + self.assertEquals(i[1].idstepbyrow(),5) + self.assertEquals(i[1].maxDetectorID(),74) + self.assertEquals(i[1].minDetectorID(),50) + +if __name__ == '__main__': + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt index c68b6d8ba1f8..e13a661fb622 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt @@ -5,21 +5,29 @@ set ( TEST_PY_FILES CheckForSampleLogsTest.py ConjoinSpectraTest.py + CorrectLogTimesTest.py CreateLeBailFitInputTest.py CreateWorkspaceTest.py + CreateTransmissionWorkspaceTest.py + CreateTransmissionWorkspaceAutoTest.py DakotaChiSquaredTest.py FilterLogByTimeTest.py FindReflectometryLinesTest.py + GetEiT0atSNSTest.py LoadFullprofFileTest.py LoadLiveDataTest.py LoadLogPropertyTableTest.py LoadMultipleGSSTest.py + MaskAngleTest.py + MaskBTPTest.py MaskWorkspaceToCalFileTest.py MeanTest.py MergeCalFilesTest.py ReflectometryReductionOneTest.py + ReflectometryReductionOneAutoTest.py RetrieveRunInfoTest.py SANSWideAngleCorrectionTest.py + SortDetectorsTest.py SortXAxisTest.py Stitch1DTest.py Stitch1DManyTest.py diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CorrectLogTimesTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CorrectLogTimesTest.py new file mode 100644 index 000000000000..1a33b125b19f --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CorrectLogTimesTest.py @@ -0,0 +1,39 @@ +import unittest +from mantid.simpleapi import * +from mantid.api import * +from testhelpers import * +from numpy import * + +class CorrectLogTimesTest(unittest.TestCase): + + def testCLTWrongLog(self): + w=CreateSingleValuedWorkspace(DataValue='1',ErrorValue='1') + LoadNexusLogs(Workspace=w,Filename='CNCS_7860_event.nxs') + + try: + CorrectLogTimes(Workspace=w,LogNames="s1") + self.fail("Should not have got here. Should throw because wrong instrument.") + except RuntimeError: + pass + finally: + DeleteWorkspace(w) + + def testCLTsingle(self): + w=CreateSingleValuedWorkspace(DataValue='1',ErrorValue='1') + LoadNexusLogs(Workspace=w,Filename='CNCS_7860_event.nxs') + self.assertFalse(w.getRun()['proton_charge'].firstTime()==w.getRun()['Speed4'].firstTime()) + CorrectLogTimes(Workspace=w,LogNames="Speed4") + self.assertTrue(w.getRun()['proton_charge'].firstTime()==w.getRun()['Speed4'].firstTime()) + self.assertFalse(w.getRun()['proton_charge'].firstTime()==w.getRun()['Speed5'].firstTime()) + DeleteWorkspace(w) + + def testCLTall(self): + w=CreateSingleValuedWorkspace(DataValue='1',ErrorValue='1') + LoadNexusLogs(Workspace=w,Filename='CNCS_7860_event.nxs') + self.assertFalse(w.getRun()['proton_charge'].firstTime()==w.getRun()['Speed4'].firstTime()) + CorrectLogTimes(Workspace=w,LogNames="") + self.assertTrue(w.getRun()['proton_charge'].firstTime()==w.getRun()['Speed4'].firstTime()) + self.assertTrue(w.getRun()['proton_charge'].firstTime()==w.getRun()['Speed5'].firstTime()) + DeleteWorkspace(w) +if __name__ == '__main__': + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateTransmissionWorkspaceAutoTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateTransmissionWorkspaceAutoTest.py new file mode 100644 index 000000000000..f24fac72c738 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateTransmissionWorkspaceAutoTest.py @@ -0,0 +1,47 @@ +import unittest +from CreateTransmissionWorkspaceTest import CreateTransmissionWorkspaceTest +from mantid.simpleapi import * + +from CreateTransmissionWorkspaceBaseTest import CreateTransmissionWorkspaceBaseTest + +class CreateTransmissionWorkspaceAutoTest(unittest.TestCase, CreateTransmissionWorkspaceBaseTest): + + def algorithm_type(self): + return CreateTransmissionWorkspaceAuto + + def setUp(self): + CreateTransmissionWorkspaceBaseTest.setUp(self) + + def tearDown(self): + CreateTransmissionWorkspaceBaseTest.setUp(self) + + def __init__(self, *args, **kwargs): + super(CreateTransmissionWorkspaceAutoTest,self).__init__(*args, **kwargs) + + def test_minimal(self): + + trans1 = Load('INTER00013463.nxs', OutputWorkspace="trans1") + + inst = trans1.getInstrument() + + out_ws = CreateTransmissionWorkspaceAuto(FirstTransmissionRun=trans1, AnalysisMode="PointDetectorAnalysis") + history = out_ws.getHistory() + alg = history.lastAlgorithm() + + ''' + Here we are checking that the applied values (passed to CreateTransmissionWorkspace come from the instrument parameters. + ''' + self.assertEqual(inst.getNumberParameter("LambdaMin")[0], alg.getProperty("WavelengthMin").value) + self.assertEqual(inst.getNumberParameter("LambdaMax")[0], alg.getProperty("WavelengthMax").value) + self.assertEqual(inst.getNumberParameter("MonitorBackgroundMin")[0], alg.getProperty("MonitorBackgroundWavelengthMin").value) + self.assertEqual(inst.getNumberParameter("MonitorBackgroundMax")[0], alg.getProperty("MonitorBackgroundWavelengthMax").value) + self.assertEqual(inst.getNumberParameter("MonitorIntegralMin")[0], alg.getProperty("MonitorIntegrationWavelengthMin").value) + self.assertEqual(inst.getNumberParameter("MonitorIntegralMax")[0], alg.getProperty("MonitorIntegrationWavelengthMax").value) + self.assertEqual(inst.getNumberParameter("I0MonitorIndex")[0], alg.getProperty("I0MonitorIndex").value) + self.assertEqual(inst.getNumberParameter("PointDetectorStart")[0], float(alg.getProperty("ProcessingInstructions").value.split(',')[0])) + self.assertEqual(inst.getNumberParameter("PointDetectorStop")[0], float(alg.getProperty("ProcessingInstructions").value.split(',')[1])) + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateTransmissionWorkspaceBaseTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateTransmissionWorkspaceBaseTest.py new file mode 100644 index 000000000000..9056ae792f6b --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateTransmissionWorkspaceBaseTest.py @@ -0,0 +1,201 @@ +import unittest +import mantid.api +import abc +from mantid.simpleapi import CreateWorkspace, DeleteWorkspace, Load +from testhelpers.algorithm_decorator import make_decorator + +import inspect +import re + +class CreateTransmissionWorkspaceBaseTest(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def algorithm_type(self): + raise RuntimeError("Not implemented") + + def setUp(self): + tof = CreateWorkspace(UnitX="TOF", DataX=[0,0,0,0], DataY=[0,0,0], NSpec=1) + not_tof = CreateWorkspace(UnitX="1/q", DataX=[0,0,0,0], DataY=[0,0,0], NSpec=1) + self.__tof = tof + self.__not_tof = not_tof + + def tearDown(self): + DeleteWorkspace(self.__tof) + DeleteWorkspace(self.__not_tof) + + def construct_standard_algorithm(self): + alg = make_decorator(self.algorithm_type()) + alg.set_FirstTransmissionRun(self.__tof) + alg.set_WavelengthMin(0.0) + alg.set_WavelengthMax(1.0) + alg.set_I0MonitorIndex(0) + alg.set_ProcessingInstructions("0, 1") + alg.set_MonitorBackgroundWavelengthMin(0.0) + alg.set_MonitorBackgroundWavelengthMax(1.0) + alg.set_MonitorIntegrationWavelengthMin(0.0) + alg.set_MonitorIntegrationWavelengthMax(1.0) + return alg + + def test_input_workspace_not_tof_throws(self): + alg = self.construct_standard_algorithm() + alg.set_FirstTransmissionRun(self.__not_tof) + self.assertRaises(Exception, alg.execute) + + def test_second_transmission_workspace_not_tof_throws(self): + alg = self.construct_standard_algorithm() + alg.set_SecondTransmissionRun(self.__not_tof) + self.assertRaises(Exception, alg.execute) + + def test_provide_second_transmission_run_without_params_throws(self): + alg = self.construct_standard_algorithm() + alg.set_SecondTransmissionRun(self.__tof) + self.assertRaises(Exception, alg.execute) + + def test_provide_second_transmission_run_without_start_overlap_q_throws(self): + alg = self.construct_standard_algorithm() + alg.set_SecondTransmissionRun(self.__tof) + alg.set_Params([0, 0.1, 1]) + alg.set_EndOverlap( 0.4 ) + self.assertRaises(Exception, alg.execute) + + def test_provide_end_transmission_run_without_end_overlap_q_throws(self): + alg = self.construct_standard_algorithm() + alg.set_SecondTransmissionRun(self.__tof) + alg.set_Params([0, 0.1, 1]) + alg.set_StartOverlap( 0.4 ) + self.assertRaises(Exception, alg.execute) + + def test_end_overlap_q_must_be_greater_than_start_overlap_q_or_throw(self): + alg = self.construct_standard_algorithm() + alg.set_SecondTransmissionRun(self.__tof) + alg.set_Params([0, 0.1, 1]) + alg.set_StartOverlap( 0.6 ) + alg.set_EndOverlap( 0.4 ) + self.assertRaises(Exception, alg.execute) + + def test_must_provide_wavelengths(self): + self.assertRaises(RuntimeError, self.algorithm_type(), FirstTransmissionRun=self.__tof, SecondTransmissionRun=self.__tof, WavelengthMin=1.0) + self.assertRaises(RuntimeError, self.algorithm_type(), FirstTransmissionRun=self.__tof, SecondTransmissionRun=self.__tof, WavelengthMax=1.0) + + def test_wavelength_min_greater_wavelength_max_throws(self): + alg = self.construct_standard_algorithm() + alg.set_WavelengthMin(1.0) + alg.set_WavelengthMax(0.0) + self.assertRaises(Exception, alg.execute) + + def test_monitor_background_wavelength_min_greater_monitor_background_wavelength_max_throws(self): + alg = self.construct_standard_algorithm() + alg.set_MonitorBackgroundWavelengthMin(1.0) + alg.set_MonitorBackgroundWavelengthMax(0.0) + self.assertRaises(Exception, alg.execute) + + def test_monitor_integration_wavelength_min_greater_monitor_integration_wavelength_max_throws(self): + alg = self.construct_standard_algorithm() + alg.set_MonitorIntegrationWavelengthMin(1.0) + alg.set_MonitorIntegrationWavelengthMax(0.0) + self.assertRaises(Exception, alg.execute) + + def test_monitor_index_positive(self): + alg = self.construct_standard_algorithm() + alg.set_I0MonitorIndex(-1) + self.assertRaises(Exception, alg.execute) + + def test_workspace_index_list_throw_if_not_pairs(self): + alg = self.construct_standard_algorithm() + alg.set_ProcessingInstructions("0") + self.assertRaises(Exception, alg.execute) + + def test_workspace_index_list_values_not_positive_throws(self): + alg = self.construct_standard_algorithm() + alg.set_ProcessingInstructions("-1, 0") # -1 is not acceptable. + self.assertRaises(Exception, alg.execute) + + def test_workspace_index_list_min_max_pairs_throw_if_min_greater_than_max(self): + alg = self.construct_standard_algorithm() + alg.set_ProcessingInstructions("1, 0") # 1 > 0 + self.assertRaises(Exception, alg.execute) + + def test_spectrum_map_mismatch_throws(self): + alg = self.construct_standard_algorithm() + trans_run1 = Load('INTER00013463.nxs') + trans_run2 = self.__tof + + alg.set_ProcessingInstructions("3,4") + alg.set_FirstTransmissionRun(trans_run1) + alg.set_SecondTransmissionRun(trans_run2) + alg.set_Params([0, 0.1, 1]) + alg.set_StartOverlap(1) + alg.set_EndOverlap(2) + self.assertRaises(Exception, alg.execute) + + DeleteWorkspace(trans_run1) + + def test_execute_one_tranmission(self): + alg = make_decorator(self.algorithm_type()) + + trans_run1 = Load('INTER00013463.nxs') + + alg.set_ProcessingInstructions("3,4") + alg.set_FirstTransmissionRun(trans_run1) + alg.set_I0MonitorIndex(0) + alg.set_WavelengthMin(0.0) + alg.set_WavelengthMax(17.9) + alg.set_WavelengthStep(0.5) + alg.set_MonitorBackgroundWavelengthMin(15.0) + alg.set_MonitorBackgroundWavelengthMax(17.0) + alg.set_MonitorIntegrationWavelengthMin(4.0) + alg.set_MonitorIntegrationWavelengthMax(10.0) + + transmission_ws = alg.execute() + + self.assertTrue(isinstance(transmission_ws, mantid.api.MatrixWorkspace), "Should be a matrix workspace") + self.assertEqual("Wavelength", transmission_ws.getAxis(0).getUnit().unitID()) + + # Because we have one transmission workspaces, binning should come from the WavelengthStep. + x = transmission_ws.readX(0) + actual_binning = x[1] - x[0] + step = alg.get_WavelengthStep() + self.assertAlmostEqual( actual_binning, step, 6) + + DeleteWorkspace(trans_run1) + DeleteWorkspace(transmission_ws) + + def test_execute_two_tranmissions(self): + alg = make_decorator(self.algorithm_type()) + + trans_run1 = Load('INTER00013463.nxs') + trans_run2 = Load('INTER00013464.nxs') + + alg.set_ProcessingInstructions("3,4") + alg.set_FirstTransmissionRun(trans_run1) + alg.set_SecondTransmissionRun(trans_run2) + alg.set_I0MonitorIndex(0) + alg.set_WavelengthMin(0.0) + alg.set_WavelengthMax(17.9) + alg.set_WavelengthStep(0.5) + alg.set_MonitorBackgroundWavelengthMin(15.0) + alg.set_MonitorBackgroundWavelengthMax(17.0) + alg.set_MonitorIntegrationWavelengthMin(4.0) + alg.set_MonitorIntegrationWavelengthMax(10.0) + alg.set_Params([1.5, 0.02, 17]) + alg.set_StartOverlap( 10.0 ) + alg.set_EndOverlap( 12.0 ) + + transmission_ws = alg.execute() + + self.assertTrue(isinstance(transmission_ws, mantid.api.MatrixWorkspace), "Should be a matrix workspace") + self.assertEqual("Wavelength", transmission_ws.getAxis(0).getUnit().unitID()) + + # Because we have two transmission workspaces, binning should come from the Params for stitching. + x = transmission_ws.readX(0) + actual_binning = x[1] - x[0] + params = alg.get_Params() + self.assertAlmostEqual( actual_binning, params[1], 6) + self.assertAlmostEqual( 1.5, params[0], 6) + self.assertAlmostEqual( 17, params[2], 6) + + DeleteWorkspace(trans_run1) + DeleteWorkspace(trans_run2) + DeleteWorkspace(transmission_ws) + \ No newline at end of file diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateTransmissionWorkspaceTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateTransmissionWorkspaceTest.py new file mode 100644 index 000000000000..1369a7bf630d --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateTransmissionWorkspaceTest.py @@ -0,0 +1,21 @@ +import unittest +from mantid.simpleapi import CreateTransmissionWorkspace +from CreateTransmissionWorkspaceBaseTest import CreateTransmissionWorkspaceBaseTest + +class CreateTransmissionWorkspaceTest(unittest.TestCase, CreateTransmissionWorkspaceBaseTest): + + def setUp(self): + CreateTransmissionWorkspaceBaseTest.setUp(self) + + def tearDown(self): + CreateTransmissionWorkspaceBaseTest.setUp(self) + + def __init__(self, *args, **kwargs): + super(CreateTransmissionWorkspaceTest,self).__init__(*args, **kwargs) + + def algorithm_type(self): + return CreateTransmissionWorkspace + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/GetEiT0atSNSTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/GetEiT0atSNSTest.py new file mode 100644 index 000000000000..a186eec6f676 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/GetEiT0atSNSTest.py @@ -0,0 +1,26 @@ +import unittest +from mantid.simpleapi import * +from mantid.api import * +from testhelpers import * +from numpy import * +from string import * + +class GetEiT0atSNSTest(unittest.TestCase): + + def testGETS(self): + w=Load('ADARAMonitors.nxs') + LoadInstrument(Workspace=w,InstrumentName='SEQUOIA',RewriteSpectraMap='0') + AddSampleLog(Workspace=w,LogName='vChTrans',LogText='1',LogType='Number Series') + AddSampleLog(Workspace=w,LogName='EnergyRequest',LogText='20',LogType='Number Series') + res=GetEiT0atSNS(w) + self.assertAlmostEqual(res[0],20.09,places=2) + self.assertAlmostEqual(res[1],30.415,places=2) + try: + res=GetEiT0atSNS(w,0.1) + except Exception as e: + s="Could not get Ei, and this is not a white beam run\nNo peak found for the monitor1" + self.assertEquals(find(e.message,s),0) + DeleteWorkspace(w) + +if __name__ == '__main__': + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/MaskAngleTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/MaskAngleTest.py new file mode 100644 index 000000000000..2563a9dd46af --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/MaskAngleTest.py @@ -0,0 +1,59 @@ +import unittest +from mantid.simpleapi import * +from mantid.api import * +from testhelpers import * +from numpy import * + +class MaskAngleTest(unittest.TestCase): + + def testMaskAngle(self): + w=WorkspaceCreationHelper.create2DWorkspaceWithFullInstrument(30,5,False,False) + AnalysisDataService.add('w',w) + masklist = MaskAngle(w,10,20) + for i in arange(w.getNumberHistograms())+1: + if (i<10) or (i>19): + self.assertTrue(not w.getInstrument().getDetector(int(i)).isMasked()) + else: + self.assertTrue(w.getInstrument().getDetector(int(i)).isMasked()) + DeleteWorkspace(w) + self.assertTrue(array_equal(masklist,arange(10)+10)) + + def testFailNoInstrument(self): + w1=CreateWorkspace(arange(5),arange(5)) + try: + MaskAngle(w1,10,20) + self.fail("Should not have got here. Should throw because no instrument.") + except ValueError: + pass + finally: + DeleteWorkspace(w1) + + def testFailLimits(self): + w2=WorkspaceCreationHelper.create2DWorkspaceWithFullInstrument(30,5,False,False) + AnalysisDataService.add('w2',w2) + w3=CloneWorkspace('w2') + w4=CloneWorkspace('w2') + try: + MaskAngle(w2,-100,20) + self.fail("Should not have got here. Wrong angle.") + except ValueError: + pass + finally: + DeleteWorkspace('w2') + try: + MaskAngle(w3,10,200) + self.fail("Should not have got here. Wrong angle.") + except ValueError: + pass + finally: + DeleteWorkspace('w3') + try: + MaskAngle(w4,100,20) + self.fail("Should not have got here. Wrong angle.") + except RuntimeError: + pass + finally: + DeleteWorkspace('w4') + +if __name__ == '__main__': + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/MaskBTPTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/MaskBTPTest.py new file mode 100644 index 000000000000..6189f4931421 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/MaskBTPTest.py @@ -0,0 +1,65 @@ +import unittest +from mantid.simpleapi import * +from mantid.api import * +from testhelpers import * +from numpy import * + +class MaskBTPTest(unittest.TestCase): + + def testMaskBTPWrongInstrument(self): + w=WorkspaceCreationHelper.create2DWorkspaceWithFullInstrument(30,5,False,False) + AnalysisDataService.add('w',w) + try: + masklist = MaskBTP(Workspace=w,Pixel="1") + self.fail("Should not have got here. Should throw because wrong instrument.") + except RuntimeError: + pass + finally: + DeleteWorkspace(w) + + def testMaskBTPWrongLimits(self): + try: + MaskBTP(Instrument='ARCS', Pixel="129") + self.fail("Should not have got here.") + except RuntimeError: + pass + try: + MaskBTP(Instrument='SEQUOIA', Bank="1") + self.fail("Should not have got here.") + except RuntimeError: + pass + try: + MaskBTP(Instrument='HYSPEC', Tube="18") + self.fail("Should not have got here.") + except RuntimeError: + pass + DeleteWorkspace("ARCSMaskBTP") + DeleteWorkspace("HYSPECMaskBTP") + DeleteWorkspace("SEQUOIAMaskBTP") + + def testMaskBTP(self): + m1=MaskBTP(Instrument='CNCS', Pixel="1-3,5") + m2=MaskBTP(Workspace='CNCSMaskBTP', Bank="1-2") + m3=MaskBTP(Workspace='CNCSMaskBTP', Bank='5-7', Tube='3') + p1=arange(400)*128 + m1p=sort(concatenate((p1,p1+1,p1+2,p1+4))) + self.assertTrue(array_equal(m1,m1p)) + self.assertTrue(array_equal(m2,arange(2048))) + b5t3=arange(128)+4*1024+2*128 + self.assertTrue(array_equal(m3,concatenate((b5t3,b5t3+1024,b5t3+2048)))) + #check whether some pixels are masked when they should + w=mtd['CNCSMaskBTP'] + self.assertTrue(w.getInstrument().getDetector(29696).isMasked()) #pixel1 + self.assertTrue(w.getInstrument().getDetector(29697).isMasked()) #pixel2 + self.assertTrue(w.getInstrument().getDetector(29698).isMasked()) #pixel3 + self.assertTrue(not w.getInstrument().getDetector(29699).isMasked()) #pixel4 + self.assertTrue(w.getInstrument().getDetector(29700).isMasked()) #pixel5 + + self.assertTrue(w.getInstrument().getDetector(1020).isMasked()) #bank 1 + self.assertTrue(not w.getInstrument().getDetector(3068).isMasked()) #bank3, tube 8 + + self.assertTrue(w.getInstrument().getDetector(4400).isMasked()) #bank5, tube 3 + DeleteWorkspace(w) + +if __name__ == '__main__': + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneAutoTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneAutoTest.py new file mode 100644 index 000000000000..f10ae5f4c494 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneAutoTest.py @@ -0,0 +1,48 @@ +import unittest +from mantid.simpleapi import ReflectometryReductionOneAuto, Load, DeleteWorkspace +from ReflectometryReductionOneBaseTest import ReflectometryReductionOneBaseTest + +class ReflectometryReductionOneAutoTest(unittest.TestCase, ReflectometryReductionOneBaseTest): + + def __init__(self, *args, **kwargs): + super(ReflectometryReductionOneAutoTest, self).__init__(*args, **kwargs) + + def setUp(self): + ReflectometryReductionOneBaseTest.setUp(self) + + def tearDown(self): + ReflectometryReductionOneBaseTest.setUp(self) + + def algorithm_type(self): + return ReflectometryReductionOneAuto + + def test_minimal_inputs(self): + + in_ws = Load('INTER00013460.nxs', OutputWorkspace="13460") + trans1 = Load('INTER00013463.nxs', OutputWorkspace="trans1") + + inst = trans1.getInstrument() + + out_ws, out_wsl_lam, thetafinal = ReflectometryReductionOneAuto(InputWorkspace=in_ws, AnalysisMode="PointDetectorAnalysis" + ,OutputWorkspace="InQ", OutputWorkspaceWavelength="InLam") + history = out_ws.getHistory() + alg = history.lastAlgorithm() + + ''' + Here we are checking that the applied values (passed to CreateTransmissionWorkspace come from the instrument parameters. + ''' + self.assertEqual(inst.getNumberParameter("LambdaMin")[0], alg.getProperty("WavelengthMin").value) + self.assertEqual(inst.getNumberParameter("LambdaMax")[0], alg.getProperty("WavelengthMax").value) + self.assertEqual(inst.getNumberParameter("MonitorBackgroundMin")[0], alg.getProperty("MonitorBackgroundWavelengthMin").value) + self.assertEqual(inst.getNumberParameter("MonitorBackgroundMax")[0], alg.getProperty("MonitorBackgroundWavelengthMax").value) + self.assertEqual(inst.getNumberParameter("MonitorIntegralMin")[0], alg.getProperty("MonitorIntegrationWavelengthMin").value) + self.assertEqual(inst.getNumberParameter("MonitorIntegralMax")[0], alg.getProperty("MonitorIntegrationWavelengthMax").value) + self.assertEqual(inst.getNumberParameter("I0MonitorIndex")[0], alg.getProperty("I0MonitorIndex").value) + self.assertEqual(inst.getNumberParameter("PointDetectorStart")[0], float(alg.getProperty("ProcessingInstructions").value.split(',')[0])) + self.assertEqual(inst.getNumberParameter("PointDetectorStop")[0], float(alg.getProperty("ProcessingInstructions").value.split(',')[1])) + + DeleteWorkspace(in_ws) + DeleteWorkspace(trans1) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneBaseTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneBaseTest.py new file mode 100644 index 000000000000..e9356f94c338 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneBaseTest.py @@ -0,0 +1,254 @@ +import unittest +from mantid.simpleapi import * +from testhelpers.algorithm_decorator import make_decorator +import mantid.api +import math +import abc + +class ReflectometryReductionOneBaseTest(object): + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def algorithm_type(self): + raise RuntimeError("Not implemented") + + def construct_standard_algorithm(self): + alg = make_decorator(self.algorithm_type()) + alg.set_InputWorkspace(self.__tof) + alg.set_WavelengthMin(0.0) + alg.set_WavelengthMax(1.0) + alg.set_I0MonitorIndex(0) + alg.set_ProcessingInstructions("0, 1") + alg.set_MonitorBackgroundWavelengthMin(0.0) + alg.set_MonitorBackgroundWavelengthMax(1.0) + alg.set_MonitorIntegrationWavelengthMin(0.0) + alg.set_MonitorIntegrationWavelengthMax(1.0) + alg.set_additional({'OutputWorkspaceWavelength': 'out_ws_wav'}) + return alg + + def setUp(self): + tof = CreateWorkspace(UnitX="TOF", DataX=[0,0,0,0], DataY=[0,0,0], NSpec=1) + not_tof = CreateWorkspace(UnitX="1/q", DataX=[0,0,0,0], DataY=[0,0,0], NSpec=1) + self.__tof = tof + self.__not_tof = not_tof + + def tearDown(self): + DeleteWorkspace(self.__tof) + DeleteWorkspace(self.__not_tof) + + + def test_check_input_workpace_not_tof_throws(self): + alg = self.construct_standard_algorithm() + alg.set_InputWorkspace(self.__not_tof) + self.assertRaises(Exception, alg.execute) + + def test_check_first_transmission_workspace_not_tof_or_wavelength_throws(self): + alg = self.construct_standard_algorithm() + alg.set_FirstTransmissionRun(self.__not_tof) + self.assertRaises(Exception, alg.execute) + + def test_check_second_transmission_workspace_not_tof_throws(self): + alg = self.construct_standard_algorithm() + alg.set_SecondTransmissionRun(self.__not_tof) + self.assertRaises(Exception, alg.execute) + + def test_proivde_second_transmission_run_without_first_throws(self): + alg = self.construct_standard_algorithm() + alg.set_SecondTransmissionRun(self.__tof) + self.assertRaises(Exception, alg.execute) + + def test_provide_second_transmission_run_without_params_throws(self): + alg = self.construct_standard_algorithm() + alg.set_FirstTransmissionRun(self.__tof) + alg.set_SecondTransmissionRun(self.__tof) + self.assertRaises(Exception, alg.execute) + + def test_provide_second_transmission_run_without_start_overlap_q_throws(self): + alg = self.construct_standard_algorithm() + alg.set_FirstTransmissionRun(self.__tof) + alg.set_SecondTransmissionRun(self.__tof) + alg.set_Params([0, 0.1, 1]) + alg.set_EndOverlap( 0.4 ) + self.assertRaises(Exception, alg.execute) + + def test_provide_end_transmission_run_without_end_overlap_q_throws(self): + alg = self.construct_standard_algorithm() + alg.set_FirstTransmissionRun(self.__tof) + alg.set_SecondTransmissionRun(self.__tof) + alg.set_Params([0, 0.1, 1]) + alg.set_StartOverlap( 0.4 ) + self.assertRaises(Exception, alg.execute) + + def test_end_overlap_q_must_be_greater_than_start_overlap_q_or_throw(self): + alg = self.construct_standard_algorithm() + alg.set_FirstTransmissionRun(self.__tof) + alg.set_SecondTransmissionRun(self.__tof) + alg.set_Params([0, 0.1, 1]) + alg.set_StartOverlap( 0.6 ) + alg.set_EndOverlap( 0.4 ) + self.assertRaises(Exception, alg.execute) + + def test_must_provide_wavelengths(self): + self.assertRaises(RuntimeError, self.algorithm_type(), InputWorkspace=self.__tof, FirstTransmissionRun=self.__tof, SecondTransmissionRun=self.__tof, WavelengthMin=1.0) + self.assertRaises(RuntimeError, self.algorithm_type(), InputWorkspace=self.__tof, FirstTransmissionRun=self.__tof, SecondTransmissionRun=self.__tof, WavelengthMax=1.0) + + def test_wavelength_min_greater_wavelength_max_throws(self): + alg = self.construct_standard_algorithm() + alg.set_WavelengthMin(1.0) + alg.set_WavelengthMax(0.0) + self.assertRaises(Exception, alg.execute) + + def test_monitor_background_wavelength_min_greater_monitor_background_wavelength_max_throws(self): + alg = self.construct_standard_algorithm() + alg.set_MonitorBackgroundWavelengthMin(1.0) + alg.set_MonitorBackgroundWavelengthMax(0.0) + self.assertRaises(Exception, alg.execute) + + def test_monitor_integration_wavelength_min_greater_monitor_integration_wavelength_max_throws(self): + alg = self.construct_standard_algorithm() + alg.set_MonitorIntegrationWavelengthMin(1.0) + alg.set_MonitorIntegrationWavelengthMax(0.0) + self.assertRaises(Exception, alg.execute) + + def test_monitor_index_positive(self): + alg = self.construct_standard_algorithm() + alg.set_I0MonitorIndex(-1) + self.assertRaises(Exception, alg.execute) + + def test_workspace_index_list_throw_if_not_pairs(self): + alg = self.construct_standard_algorithm() + alg.set_ProcessingInstructions("0") + self.assertRaises(Exception, alg.execute) + + def test_workspace_index_list_values_not_positive_throws(self): + alg = self.construct_standard_algorithm() + alg.set_ProcessingInstructions("-1, 0") # -1 is not acceptable. + self.assertRaises(Exception, alg.execute) + + def test_workspace_index_list_min_max_pairs_throw_if_min_greater_than_max(self): + alg = self.construct_standard_algorithm() + alg.set_ProcessingInstructions("1, 0") # 1 > 0 + self.assertRaises(Exception, alg.execute) + + def test_region_of_interest_throws_if_i0monitor_index_negative(self): + alg = self.construct_standard_algorithm() + alg.set_I0MonitorIndex(-1) + self.assertRaises(Exception, alg.execute) + + def test_cannot_set_direct_beam_region_of_interest_without_multidetector_run(self): + alg = self.construct_standard_algorithm() + alg.set_AnalysisMode("PointDetectorAnalysis") + alg.set_RegionOfDirectBeam([1, 2]) + self.assertRaises(Exception, alg.execute) + + def test_region_of_direct_beam_indexes_cannot_be_negative_or_throws(self): + alg = self.construct_standard_algorithm() + alg.set_AnalysisMode("MultiDetectorAnalysis") + alg.set_RegionOfDirectBeam([0, -1]); + self.assertRaises(Exception, alg.execute) + + def test_region_of_direct_beam_indexes_must_be_provided_as_min_max_order_or_throws(self): + alg = self.construct_standard_algorithm() + alg.set_AnalysisMode("MultiDetectorAnalysis") + alg.set_RegionOfDirectBeam([1, 0]); + self.assertRaises(Exception, alg.execute) + + def test_bad_detector_component_name_throws(self): + alg = self.construct_standard_algorithm() + alg.set_DetectorComponentName("made-up") + self.assertRaises(Exception, alg.execute) + + def test_bad_sample_component_name_throws(self): + alg = self.construct_standard_algorithm() + alg.set_SampleComponentName("made-up") + self.assertRaises(Exception, alg.execute) + + def test_point_detector_run_with_single_transmission_workspace(self): + alg = self.construct_standard_algorithm() + real_run = Load('INTER00013460.nxs') + alg.set_InputWorkspace(real_run) + alg.set_ProcessingInstructions("3,4") + alg.set_FirstTransmissionRun(real_run) # Currently a requirement that one transmisson correction is provided. + alg.set_ThetaIn(0.2) + + out_ws_q, out_ws_lam, theta = alg.execute() + self.assertEqual(0.2, theta, "Theta in and out should be the same") + + self.assertTrue(isinstance(out_ws_lam, mantid.api.MatrixWorkspace), "Should be a matrix workspace") + self.assertEqual("Wavelength", out_ws_lam.getAxis(0).getUnit().unitID()) + + self.assertTrue(isinstance(out_ws_q, mantid.api.MatrixWorkspace), "Should be a matrix workspace") + self.assertEqual("MomentumTransfer", out_ws_q.getAxis(0).getUnit().unitID()) + + self.assertEqual(2, out_ws_lam.getNumberHistograms()) + DeleteWorkspace(real_run) + + def test_point_detector_run_with_two_transmission_workspaces(self): + alg = self.construct_standard_algorithm() + real_run = Load('INTER00013460.nxs') + trans_run1 = Load('INTER00013463.nxs') + trans_run2 = Load('INTER00013464.nxs') + + alg.set_InputWorkspace(real_run) + alg.set_ProcessingInstructions("3,4") + alg.set_FirstTransmissionRun(trans_run1) + alg.set_SecondTransmissionRun(trans_run2) + + alg.set_Params([1.5, 0.02, 17]) + alg.set_StartOverlap( 10.0 ) + alg.set_EndOverlap( 12.0 ) + alg.set_ThetaIn(0.2) + + out_ws_q, out_ws_lam, theta = alg.execute() + + DeleteWorkspace(real_run) + DeleteWorkspace(trans_run1) + DeleteWorkspace(trans_run2) + + def test_spectrum_map_mismatch_throws(self): + alg = self.construct_standard_algorithm() + real_run = Load('INTER00013460.nxs') + trans_run1 = Load('INTER00013463.nxs') + trans_run2 = self.__tof + + alg.set_InputWorkspace(real_run) + alg.set_ProcessingInstructions("3,4") + alg.set_FirstTransmissionRun(trans_run1) + alg.set_SecondTransmissionRun(trans_run2) + + self.assertRaises(Exception, alg.execute) + + DeleteWorkspace(real_run) + DeleteWorkspace(trans_run1) + + def test_calculate_theta(self): + alg = self.construct_standard_algorithm() + real_run = Load('INTER00013460.nxs') + alg.set_InputWorkspace(real_run) + alg.set_ProcessingInstructions("3,4") + alg.set_FirstTransmissionRun(real_run) # Currently a requirement that one transmisson correction is provided. + + out_ws_q, out_ws_lam, theta = alg.execute() + self.assertAlmostEqual(0.7, theta*(180/math.pi), 1) + + DeleteWorkspace(real_run) + + def test_multidetector_run(self): + alg = self.construct_standard_algorithm() + real_run = Load('POLREF00004699.nxs') + alg.set_InputWorkspace(real_run[0]) + alg.set_AnalysisMode("MultiDetectorAnalysis") + alg.set_DetectorComponentName('lineardetector') + alg.set_ProcessingInstructions("3, 10") # Fictional values + alg.set_RegionOfDirectBeam("20, 30") # Fictional values + alg.set_ThetaIn(0.1) # Fictional values + + out_ws_q, out_ws_lam, theta = alg.execute() + + self.assertTrue(isinstance(out_ws_lam, mantid.api.MatrixWorkspace), "Should be a matrix workspace") + self.assertEqual("Wavelength", out_ws_lam.getAxis(0).getUnit().unitID()) + + self.assertTrue(isinstance(out_ws_q, mantid.api.MatrixWorkspace), "Should be a matrix workspace") + self.assertEqual("MomentumTransfer", out_ws_q.getAxis(0).getUnit().unitID()) + diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneTest.py index 5b72284d0046..f69604f9bae0 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ReflectometryReductionOneTest.py @@ -1,327 +1,21 @@ import unittest -from mantid.simpleapi import * -import mantid.api +from mantid.simpleapi import ReflectometryReductionOne +from ReflectometryReductionOneBaseTest import ReflectometryReductionOneBaseTest -import inspect -import re -import math - -def make_decorator(algorithm_to_decorate): - """ - Dynamically create a builder pattern style decorator around a Mantid algorithm. - This allows you to separate out setting algorithm parameters from the actual method execution. Parameters may be reset multiple times. - """ - - class Decorator(object): - - def __init__(self, alg_subject): - self.__alg_subject = alg_subject - self.__parameters__ = dict() - - def execute(self, additional=None, verbose=False): - if verbose: - print "Algorithm Parameters:" - print self.__parameters__ - print - out = self.__alg_subject(**self.__parameters__) - return out - - def set_additional(self, additional): - self.__parameters__.update(**additional) - - def add_getter_setter(type, name): - - def setter(self, x): - self.__parameters__[name] = x - - def getter(self): - return self.__parameters__[name] - - setattr(type, "set_" + name, setter) - setattr(type, "get_" + name, getter) - - - argspec = inspect.getargspec(algorithm_to_decorate) - for parameter in argspec.varargs.split(','): - m = re.search('(^\w+)', parameter) # Take the parameter key part from the defaults given as 'key=value' - if m: - parameter = m.group(0).strip() - m = re.search('\w+$', parameter) # strip off any leading numerical values produced by argspec - if m: - parameter = m.group(0).strip() - add_getter_setter(Decorator, m.group(0).strip()) - - return Decorator(algorithm_to_decorate) - - -class ReflectometryReductionOneTest(unittest.TestCase): +class ReflectometryReductionOneTest(unittest.TestCase, ReflectometryReductionOneBaseTest): - def construct_standard_algorithm(self): - alg = make_decorator(ReflectometryReductionOne) - alg.set_InputWorkspace(self.__tof) - alg.set_WavelengthMin(0.0) - alg.set_WavelengthMax(1.0) - alg.set_I0MonitorIndex(0) - alg.set_WorkspaceIndexList([0, 1]) - alg.set_MonitorBackgroundWavelengthMin(0.0) - alg.set_MonitorBackgroundWavelengthMax(1.0) - alg.set_MonitorIntegrationWavelengthMin(0.0) - alg.set_MonitorIntegrationWavelengthMax(1.0) - alg.set_additional({'OutputWorkspaceWavelength': 'out_ws_wav'}) - return alg + def __init__(self, *args, **kwargs): + super(ReflectometryReductionOneTest, self).__init__(*args, **kwargs) def setUp(self): - tof = CreateWorkspace(UnitX="TOF", DataX=[0,0,0,0], DataY=[0,0,0], NSpec=1) - not_tof = CreateWorkspace(UnitX="1/q", DataX=[0,0,0,0], DataY=[0,0,0], NSpec=1) - self.__tof = tof - self.__not_tof = not_tof + ReflectometryReductionOneBaseTest.setUp(self) def tearDown(self): - DeleteWorkspace(self.__tof) - DeleteWorkspace(self.__not_tof) - - - def test_check_input_workpace_not_tof_throws(self): - alg = self.construct_standard_algorithm() - alg.set_InputWorkspace(self.__not_tof) - self.assertRaises(ValueError, alg.execute) - - def test_check_first_transmission_workspace_not_tof_throws(self): - alg = self.construct_standard_algorithm() - alg.set_FirstTransmissionRun(self.__not_tof) - self.assertRaises(ValueError, alg.execute) - - def test_check_second_transmission_workspace_not_tof_throws(self): - alg = self.construct_standard_algorithm() - alg.set_SecondTransmissionRun(self.__not_tof) - self.assertRaises(ValueError, alg.execute) - - def test_proivde_second_transmission_run_without_first_throws(self): - alg = self.construct_standard_algorithm() - alg.set_SecondTransmissionRun(self.__tof) - self.assertRaises(ValueError, alg.execute) - - def test_provide_second_transmission_run_without_params_throws(self): - alg = self.construct_standard_algorithm() - alg.set_FirstTransmissionRun(self.__tof) - alg.set_SecondTransmissionRun(self.__tof) - self.assertRaises(ValueError, alg.execute) - - def test_provide_second_transmission_run_without_start_overlap_q_throws(self): - alg = self.construct_standard_algorithm() - alg.set_FirstTransmissionRun(self.__tof) - alg.set_SecondTransmissionRun(self.__tof) - alg.set_Params([0, 0.1, 1]) - alg.set_EndOverlapQ( 0.4 ) - self.assertRaises(ValueError, alg.execute) - - def test_provide_end_transmission_run_without_end_overlap_q_throws(self): - alg = self.construct_standard_algorithm() - alg.set_FirstTransmissionRun(self.__tof) - alg.set_SecondTransmissionRun(self.__tof) - alg.set_Params([0, 0.1, 1]) - alg.set_StartOverlapQ( 0.4 ) - self.assertRaises(ValueError, alg.execute) - - def test_end_overlap_q_must_be_greater_than_start_overlap_q_or_throw(self): - alg = self.construct_standard_algorithm() - alg.set_FirstTransmissionRun(self.__tof) - alg.set_SecondTransmissionRun(self.__tof) - alg.set_Params([0, 0.1, 1]) - alg.set_StartOverlapQ( 0.6 ) - alg.set_EndOverlapQ( 0.4 ) - self.assertRaises(ValueError, alg.execute) - - def test_must_provide_wavelengths(self): - self.assertRaises(RuntimeError, ReflectometryReductionOne, InputWorkspace=self.__tof, FirstTransmissionRun=self.__tof, SecondTransmissionRun=self.__tof, WavelengthMin=1.0) - self.assertRaises(RuntimeError, ReflectometryReductionOne, InputWorkspace=self.__tof, FirstTransmissionRun=self.__tof, SecondTransmissionRun=self.__tof, WavelengthMax=1.0) - - def test_wavelength_min_greater_wavelength_max_throws(self): - alg = self.construct_standard_algorithm() - alg.set_WavelengthMin(1.0) - alg.set_WavelengthMax(0.0) - self.assertRaises(ValueError, alg.execute) - - def test_monitor_background_wavelength_min_greater_monitor_background_wavelength_max_throws(self): - alg = self.construct_standard_algorithm() - alg.set_MonitorBackgroundWavelengthMin(1.0) - alg.set_MonitorBackgroundWavelengthMax(0.0) - self.assertRaises(ValueError, alg.execute) - - def test_monitor_integration_wavelength_min_greater_monitor_integration_wavelength_max_throws(self): - alg = self.construct_standard_algorithm() - alg.set_MonitorIntegrationWavelengthMin(1.0) - alg.set_MonitorIntegrationWavelengthMax(0.0) - self.assertRaises(ValueError, alg.execute) - - def test_monitor_index_positive(self): - alg = self.construct_standard_algorithm() - alg.set_I0MonitorIndex(-1) - self.assertRaises(ValueError, alg.execute) - - def test_workspace_index_list_throw_if_not_pairs(self): - alg = self.construct_standard_algorithm() - alg.set_WorkspaceIndexList([0]) - self.assertRaises(ValueError, alg.execute) - - def test_workspace_index_list_values_not_positive_throws(self): - alg = self.construct_standard_algorithm() - alg.set_WorkspaceIndexList([-1, 0]) # -1 is not acceptable. - self.assertRaises(ValueError, alg.execute) - - def test_workspace_index_list_min_max_pairs_throw_if_min_greater_than_max(self): - alg = self.construct_standard_algorithm() - alg.set_WorkspaceIndexList([1, 0]) # 1 > 0 - self.assertRaises(ValueError, alg.execute) - - def test_region_of_interest_throws_if_i0monitor_index_negative(self): - alg = self.construct_standard_algorithm() - alg.set_I0MonitorIndex(-1) - self.assertRaises(ValueError, alg.execute) - - def test_cannot_set_region_of_interest_without_multidetector_run(self): - alg = self.construct_standard_algorithm() - alg.set_AnalysisMode("PointDetectorAnalysis") - alg.set_RegionOfInterest([1, 2]) - self.assertRaises(ValueError, alg.execute) - - def test_region_of_interest_indexes_cannot_be_negative_or_throws(self): - alg = self.construct_standard_algorithm() - alg.set_AnalysisMode("MultiDetectorAnalysis") - alg.set_RegionOfInterest([0, -1]); - self.assertRaises(ValueError, alg.execute) - - def test_region_of_integrest_indexes_must_be_provided_as_min_max_order_or_throws(self): - alg = self.construct_standard_algorithm() - alg.set_AnalysisMode("MultiDetectorAnalysis") - alg.set_RegionOfInterest([1, 0]); - self.assertRaises(ValueError, alg.execute) - - def test_cannot_set_direct_beam_region_of_interest_without_multidetector_run(self): - alg = self.construct_standard_algorithm() - alg.set_AnalysisMode("PointDetectorAnalysis") - alg.set_RegionOfDirectBeam([1, 2]) - self.assertRaises(ValueError, alg.execute) - - def test_region_of_direct_beam_indexes_cannot_be_negative_or_throws(self): - alg = self.construct_standard_algorithm() - alg.set_AnalysisMode("MultiDetectorAnalysis") - alg.set_RegionOfDirectBeam([0, -1]); - self.assertRaises(ValueError, alg.execute) - - def test_region_of_direct_beam_indexes_must_be_provided_as_min_max_order_or_throws(self): - alg = self.construct_standard_algorithm() - alg.set_AnalysisMode("MultiDetectorAnalysis") - alg.set_RegionOfDirectBeam([1, 0]); - self.assertRaises(ValueError, alg.execute) - - def test_bad_detector_component_name_throws(self): - alg = self.construct_standard_algorithm() - alg.set_DetectorComponentName("made-up") - self.assertRaises(ValueError, alg.execute) + ReflectometryReductionOneBaseTest.setUp(self) - def test_bad_sample_component_name_throws(self): - alg = self.construct_standard_algorithm() - alg.set_SampleComponentName("made-up") - self.assertRaises(ValueError, alg.execute) - - def test_point_detector_run_with_single_transmission_workspace(self): - alg = self.construct_standard_algorithm() - real_run = Load('INTER00013460.nxs') - alg.set_InputWorkspace(real_run) - alg.set_WorkspaceIndexList([3,4]) - alg.set_FirstTransmissionRun(real_run) # Currently a requirement that one transmisson correction is provided. - alg.set_ThetaIn(0.2) - - out_ws_q, out_ws_lam, theta = alg.execute() - self.assertEqual(0.2, theta, "Theta in and out should be the same") - - self.assertTrue(isinstance(out_ws_lam, mantid.api.MatrixWorkspace), "Should be a matrix workspace") - self.assertEqual("Wavelength", out_ws_lam.getAxis(0).getUnit().unitID()) - - self.assertTrue(isinstance(out_ws_q, mantid.api.MatrixWorkspace), "Should be a matrix workspace") - self.assertEqual("MomentumTransfer", out_ws_q.getAxis(0).getUnit().unitID()) - - self.assertEqual(2, out_ws_lam.getNumberHistograms()) - DeleteWorkspace(real_run) + def algorithm_type(self): + return ReflectometryReductionOne - def test_point_detector_run_with_two_transmission_workspaces(self): - alg = self.construct_standard_algorithm() - real_run = Load('INTER00013460.nxs') - trans_run1 = Load('INTER00013463.nxs') - trans_run2 = Load('INTER00013464.nxs') - - alg.set_InputWorkspace(real_run) - alg.set_WorkspaceIndexList([3,4]) - alg.set_FirstTransmissionRun(trans_run1) - alg.set_SecondTransmissionRun(trans_run2) - alg.set_Params([1.5, 0.02, 17]) - alg.set_StartOverlapQ( 10.0 ) - alg.set_EndOverlapQ( 12.0 ) - alg.set_ThetaIn(0.2) - - out_ws_q, out_ws_lam, theta = alg.execute() - - self.assertTrue(isinstance(out_ws_lam, mantid.api.MatrixWorkspace), "Should be a matrix workspace") - self.assertEqual("Wavelength", out_ws_lam.getAxis(0).getUnit().unitID()) - - self.assertTrue(isinstance(out_ws_q, mantid.api.MatrixWorkspace), "Should be a matrix workspace") - self.assertEqual("MomentumTransfer", out_ws_q.getAxis(0).getUnit().unitID()) - - - self.assertEqual(2, out_ws_lam.getNumberHistograms()) - DeleteWorkspace(real_run) - DeleteWorkspace(trans_run1) - DeleteWorkspace(trans_run2) - - def test_calculate_theta(self): - alg = self.construct_standard_algorithm() - real_run = Load('INTER00013460.nxs') - alg.set_InputWorkspace(real_run) - alg.set_WorkspaceIndexList([3,4]) - alg.set_FirstTransmissionRun(real_run) # Currently a requirement that one transmisson correction is provided. - - out_ws_q, out_ws_lam, theta = alg.execute() - self.assertAlmostEqual(0.7, theta*(180/math.pi), 1) - - DeleteWorkspace(real_run) - - def test_throw_if_no_roi_for_multidetector_run(self): - alg = self.construct_standard_algorithm() - real_run = Load('POLREF00004699.nxs') - alg.set_InputWorkspace(real_run[0]) - alg.set_AnalysisMode("MultiDetectorAnalysis") - alg.set_RegionOfDirectBeam([0,1]) - alg.set_DetectorComponentName('lineardetector') - self.assertRaises(ValueError, alg.execute) - - def test_throw_if_no_db_for_multidetector_run(self): - alg = self.construct_standard_algorithm() - real_run = Load('POLREF00004699.nxs') - alg.set_InputWorkspace(real_run[0]) - alg.set_AnalysisMode("MultiDetectorAnalysis") - alg.set_DetectorComponentName('lineardetector') - alg.set_RegionOfInterest([0,1]) - self.assertRaises(ValueError, alg.execute) - - def test_multidetector_run(self): - alg = self.construct_standard_algorithm() - real_run = Load('POLREF00004699.nxs') - alg.set_InputWorkspace(real_run[0]) - alg.set_AnalysisMode("MultiDetectorAnalysis") - alg.set_DetectorComponentName('lineardetector') - alg.set_RegionOfInterest([3, 10]) # Fictional values - alg.set_RegionOfDirectBeam([20, 30]) # Fictional values - alg.set_ThetaIn(0.1) # Fictional values - - out_ws_q, out_ws_lam, theta = alg.execute() - - self.assertTrue(isinstance(out_ws_lam, mantid.api.MatrixWorkspace), "Should be a matrix workspace") - self.assertEqual("Wavelength", out_ws_lam.getAxis(0).getUnit().unitID()) - - self.assertTrue(isinstance(out_ws_q, mantid.api.MatrixWorkspace), "Should be a matrix workspace") - self.assertEqual("MomentumTransfer", out_ws_q.getAxis(0).getUnit().unitID()) - - + if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/SortDetectorsTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/SortDetectorsTest.py new file mode 100644 index 000000000000..032aee57c3b5 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/SortDetectorsTest.py @@ -0,0 +1,22 @@ +import unittest +from mantid.simpleapi import * +from mantid.api import * +from testhelpers import * +from numpy import * + + +class SortDetectorsTest(unittest.TestCase): + + def testSortDetectors(self): + w=WorkspaceCreationHelper.create2DWorkspaceWithFullInstrument(10,2,False,False) + AnalysisDataService.add('w',w) + MoveInstrumentComponent(w,DetectorID=3,X=-7.,Y=0,Z=0,RelativePosition=0) + x=SortDetectors(w) + DeleteWorkspace(w) + self.assertTrue(array_equal(x[0],array([2]))) + self.assertTrue(array_equal(x[1],array([7.]))) + self.assertTrue(array_equal(x[2],array([0, 1, 3, 4, 5, 6, 7, 8, 9]))) + self.assertTrue(x[3][0]==5.) + +if __name__ == '__main__': + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/SortXAxisTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/SortXAxisTest.py index 16afcfa1115f..bf8598669537 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/SortXAxisTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/SortXAxisTest.py @@ -5,16 +5,8 @@ class SortXAxisTest(unittest.TestCase): - def test_throw_if_not_distribution(self): - dataX = [1, 2, 3, 4] # In descending order, so y and e will need to be reversed. - dataY = [1, 2, 3] - dataE = [1, 2, 3] - unsortedws = CreateWorkspace(DataX=dataX,DataY=dataY,DataE=dataE,UnitX='TOF',Distribution=False) - # Test that the algorithm throws because it's being passed histogram data. - self.assertRaises(ValueError, SortXAxis, unsortedws) - def test_x_ascending(self): - dataX = [1, 2, 3] # In descending order, so y and e will need to be reversed. + dataX = [1, 2, 3] # In ascending order, so y and e will need to be reversed. dataY = [1, 2, 3] dataE = [1, 2, 3] unsortedws = CreateWorkspace(DataX=dataX,DataY=dataY,DataE=dataE,UnitX='TOF',Distribution=True) @@ -27,6 +19,8 @@ def test_x_ascending(self): self.assertEqual(dataX, sortedX.tolist()) self.assertEqual(dataY, sortedY.tolist()) self.assertEqual(dataE, sortedE.tolist()) + DeleteWorkspace(unsortedws) + DeleteWorkspace(sortedws) def test_x_descending(self): @@ -45,6 +39,8 @@ def test_x_descending(self): dataE.reverse() self.assertEqual(dataY, sortedY.tolist()) self.assertEqual(dataE, sortedE.tolist()) + DeleteWorkspace(unsortedws) + DeleteWorkspace(sortedws) def test_on_multiple_spectrum(self): dataX = [3, 2, 1, 3, 2, 1] # In descending order, so y and e will need to be reversed. @@ -69,6 +65,49 @@ def test_on_multiple_spectrum(self): self.assertEqual(sorted(dataX[3:]), sortedX.tolist()) self.assertEqual(dataY[3:], sortedY.tolist()) self.assertEqual(dataE[3:], sortedE.tolist()) + DeleteWorkspace(unsortedws) + DeleteWorkspace(sortedws) + + + def test_sorts_x_histogram_ascending(self): + dataX = [1, 2, 3, 4] + dataY = [1, 2, 3] + dataE = [1, 2, 3] + unsortedws = CreateWorkspace(DataX=dataX,DataY=dataY,DataE=dataE,UnitX='TOF',Distribution=False) + # Run the algorithm + sortedws = SortXAxis(InputWorkspace=unsortedws) + sortedX = sortedws.readX(0) + sortedY = sortedws.readY(0) + sortedE = sortedws.readE(0) + # Check the resulting data values. Sorting operation should have resulted in no changes + self.assertEqual(dataX, sortedX.tolist()) + self.assertEqual(dataY, sortedY.tolist()) + self.assertEqual(dataE, sortedE.tolist()) + + DeleteWorkspace(unsortedws) + DeleteWorkspace(sortedws) + + def test_sorts_x_histogram_descending(self): + dataX = [4, 3, 2, 1] + dataY = [1, 2, 3] + dataE = [1, 2, 3] + unsortedws = CreateWorkspace(DataX=dataX,DataY=dataY,DataE=dataE,UnitX='TOF',Distribution=False) + # Run the algorithm + sortedws = SortXAxis(InputWorkspace=unsortedws) + sortedX = sortedws.readX(0) + sortedY = sortedws.readY(0) + sortedE = sortedws.readE(0) + # Check the resulting data values. Sorting operation should have resulted in no changes + self.assertEqual(sorted(dataX), sortedX.tolist()) + dataY.reverse() + dataE.reverse() + self.assertEqual(dataY, sortedY.tolist()) + self.assertEqual(dataE, sortedE.tolist()) + + DeleteWorkspace(unsortedws) + DeleteWorkspace(sortedws) + + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/functions/StretchedExpFTTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/functions/StretchedExpFTTest.py index 7f76e8d1121b..66de05c7fd61 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/functions/StretchedExpFTTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/functions/StretchedExpFTTest.py @@ -4,122 +4,126 @@ from mantid.simpleapi import Fit import testhelpers -from pdb import set_trace as tr - -class _InternalMakeSEFTData(PythonAlgorithm): - - def PyInit(self): - self.declareProperty('height', 1.0, validator=FloatBoundedValidator(lower=0)) - self.declareProperty('tau', 1.0, validator=FloatBoundedValidator(lower=0)) - self.declareProperty('beta', 1.0, validator=FloatBoundedValidator(lower=0)) - self.declareProperty('nhalfbins', 100) - self.declareProperty('de', 0.0004) # energy step in meV - self.declareProperty(MatrixWorkspaceProperty('OutputWorkspace', '', direction = Direction.Output)) - - def PyExec(self): - '''Create the data workspace containing one spectrum resulting from the - Fourier transform of an stretched exponential, plus some noise''' - import numpy as np - from scipy.fftpack import fft - from scipy.interpolate import interp1d - - meV2ps = 4.136 # from meV (or ueV) to ps (or ns) - - nhalfbins = self.getProperty('nhalfbins').value - de = self.getProperty('de').value #bin width, in meV or ueV - nbins = 2*nhalfbins - xvals = de*np.arange(-nhalfbins, nhalfbins) + de/2 # energy values - - #Find the time domain - N = 1+nhalfbins - M = 2*N+1 - dt = meV2ps / (M*de) # ps ( or ns), time step - sampled_times = dt * np.arange(-N, N+1) - - height = self.getProperty('height').value - tau = self.getProperty('tau').value - beta = self.getProperty('beta').value - - #Define the stretched exponential on the time domain, then do its Fourier transform - exponent = -(np.abs(sampled_times)/tau)**beta - freqs = de * np.arange(-N, N+1) - fourier = height*np.abs( fft( np.exp(exponent) ).real ) - fourier = np.concatenate( (fourier[N+1:],fourier[0:N+1]) ) - interpolator = interp1d(freqs, fourier) - ynominal = interpolator(xvals) - - wspace = WorkspaceFactory.create('Workspace2D', NVectors=1, XLength=nbins, YLength=nbins) - # white noise in the [0.2, 1) range, average is 0.6 - noise = 0.2 + 0.8*np.random.random(nbins) - sign = np.random.random(nbins) - sign = np.where(sign>0.5, 1, -1) # random sign - # average noise is 6% of the signal local intensity - - yvals = (1+0.1*noise*sign) * ynominal - error = 0.2*noise*ynominal - wspace.dataX(0)[:] = xvals - wspace.dataY(0)[:] = yvals - # average error is 12% of the signal local intensity - wspace.dataE(0)[:] = error - - self.setProperty('OutputWorkspace', wspace) # Stores the workspace as the given name - -class StretchedExpFTTest(unittest.TestCase): - - def skipTest(self): - try: - import scipy.fftpack # Scipy not available in windows debug - return False - except ImportError: - return True - - def test_registered(self): - try: - FunctionFactory.createFunction('StretchedExpFT') - except RuntimeError, exc: - self.fail('Could not create StretchedExpFT function: %s' % str(exc)) - - def test_fit(self): - if self.skipTest(): # python2.6 doesn't have skipping decorators - return - - from random import random - variation = lambda x: x*( 1+(random()-0.5)/5. ) # range [x*0.9, x*1.1] should be bigger but not until parameter constraints have been exposed to python - #Generate a data workspace using random parameters around {'height':0.1,'tau':100,'beta':1} - parms={'height':variation(0.1), 'tau':variation(100), 'beta':variation(1)} - - Nh = 4000 - de = 0.0004 - AlgorithmFactory.subscribe(_InternalMakeSEFTData) - alg = testhelpers.run_algorithm('_InternalMakeSEFTData', nhalfbins=Nh, de=de, - OutputWorkspace='_test_seft_data', **parms) - input_ws = alg.getProperty('OutputWorkspace').value - - sx = -Nh*de + de/2 - ex = (Nh-1)*de + de/2 - func_string = 'name=StretchedExpFT,height=0.1,tau=100,beta=1' - Fit(Function=func_string, InputWorkspace='_test_seft_data', - StartX=sx, EndX=ex, CreateOutput=1, MaxIterations=20) - - ws = mtd['_test_seft_data_Parameters'] - fitted='' - for irow in range( ws.rowCount() ): - row = ws.row(irow) - name = row['Name'] - if name == 'Cost function value': - value = row['Value'] - elif name in parms.keys(): - fitted += '{0}={1}, '.format(name, row['Value']) - - target = ', '.join( '{0}={1}'.format(key,val) for key,val in parms.items() ) - msg='Cost function {0} too high\nTargets were {1},\nbut obtained {2}'.format(value,target,fitted) - self.assertTrue(value < 5.0, msg) - msg='Cost function {0}\nStarted with height=0.1, tau=100, beta=1\nTargets were {1},\nobtained {2}'.format(value,target,fitted) - - mtd.remove('_test_seft_data') - mtd.remove('_test_seft_data_NormalisedCovarianceMatrix') - mtd.remove('_test_seft_data_Parameters') - mtd.remove('_test_seft_data_Workspace') +#from pdb import set_trace as tr + +class _InternalMakeSEFTData( PythonAlgorithm ): + + def PyInit( self ): + self.declareProperty( 'height', 1.0, validator = FloatBoundedValidator( lower = 0 ) ) + self.declareProperty( 'tau', 1.0, validator = FloatBoundedValidator( lower = 0 ) ) + self.declareProperty( 'beta', 1.0, validator = FloatBoundedValidator( lower = 0 ) ) + self.declareProperty( 'nhalfbins', 100 ) + self.declareProperty( 'de', 0.0004 ) # energy step in meV + self.declareProperty( MatrixWorkspaceProperty( 'OutputWorkspace', '', direction = Direction.Output ) ) + + def PyExec( self ): + '''Create the data workspace containing one spectrum resulting from the + Fourier transform of an stretched exponential, plus some noise''' + import numpy as np + from scipy.fftpack import fft + from scipy.interpolate import interp1d + + plank_constant = 4.135665616 # in units of meV*THz + + nhalfbins = self.getProperty( 'nhalfbins' ).value + de = self.getProperty( 'de' ).value #bin width, in meV or ueV + nbins = 2 * nhalfbins + xvals = de * np.arange( -nhalfbins, nhalfbins + 1) + de/2 # + + #Find the time domain + M = 2 * nhalfbins + 1 + dt = plank_constant / ( M * de ) # ps ( or ns), time step + sampled_times = dt * np.arange( -nhalfbins, nhalfbins + 1 ) + + height = self.getProperty( 'height' ).value + tau = self.getProperty( 'tau' ).value + beta = self.getProperty( 'beta' ).value + + #Define the stretched exponential on the time domain, then do its Fourier transform + exponent = -( np.abs( sampled_times ) / tau )**beta + freqs = de * np.arange( -nhalfbins, nhalfbins + 1 ) + fourier = height * np.abs( fft( np.exp( exponent ) ).real ) + fourier = np.concatenate( ( fourier[ nhalfbins + 1 : ],fourier[ 0 : nhalfbins + 1 ] ) ) + interpolator = interp1d( freqs, fourier ) + N = nhalfbins / 2 + ynominal = interpolator( xvals[ N : -N-1 ] ) # high energies are not well represented, avoid them + + wspace = WorkspaceFactory.create( 'Workspace2D' , NVectors = 1, XLength = 2 * N, YLength = 2 * N ) + # white noise in the [0.2, 1) range, average is 0.6 + noise = 0.2 + 0.8 * np.random.random( 2 * N ) + sign = np.random.random( 2 * N ) + sign = np.where( sign > 0.5, 1, -1 ) # random sign + # average noise is 6% of the signal local intensity + + yvals = ( 1 + 0.05 * noise * sign ) * ynominal + error = 0.1 * noise * ynominal + wspace.dataX(0)[:] = xvals[ N : -N-1 ] + wspace.dataY(0)[:] = yvals + # average error is 12% of the signal local intensity + wspace.dataE(0)[:] = error + + """ # Only for debugging purposes + buffer = '#h = {0}, tau = {1}, beta = {2}\n'.format( height, tau, beta ) + for iy in range( len(yvals) ): + buffer += '{0} {1} {2}\n'.format( xvals[N+iy], yvals[iy], error[iy] ) + open( '/tmp/junk.dat', 'w' ).write( buffer ) + """ + + self.setProperty( 'OutputWorkspace', wspace ) # Stores the workspace as the given name + +class StretchedExpFTTest( unittest.TestCase ): + + def skipTest( self ): + try: + import scipy.fftpack # Scipy not available in windows debug + return False + except ImportError: + return True + + def test_registered( self ): + try: + FunctionFactory.createFunction( 'StretchedExpFT' ) + except RuntimeError, exc: + self.fail( 'Could not create StretchedExpFT function: %s' % str( exc ) ) + + def test_fit( self ): + if self.skipTest(): # python2.6 doesn't have skipping decorators + return + + from random import random + variation = lambda x: x * ( 1 + ( random() - 0.5 ) / 5. ) # range [x*0.9, x*1.1] should be bigger but not until parameter constraints have been exposed to python + #Generate a data workspace using random parameters around {'height':0.1,'tau':100,'beta':1} + parms={ 'height' : variation( 0.1 ), 'tau' : variation( 100 ), 'beta' : variation( 1 ) } + + Nh = 2000 + de = 0.0004 + AlgorithmFactory.subscribe( _InternalMakeSEFTData ) + alg = testhelpers.run_algorithm( '_InternalMakeSEFTData', nhalfbins = Nh, de = de, OutputWorkspace = '_test_seft_data', **parms ) + input_ws = alg.getProperty( 'OutputWorkspace' ).value + + sx = -Nh * de + de / 2 + ex = ( Nh-1 ) * de + de / 2 + func_string = 'name=StretchedExpFT,height=0.1,tau=100,beta=1' + Fit( Function = func_string, InputWorkspace= '_test_seft_data', StartX = sx, EndX = ex, CreateOutput = 1, MaxIterations = 20 ) + + ws = mtd[ '_test_seft_data_Parameters' ] + fitted = '' + for irow in range( ws.rowCount() ): + row = ws.row( irow ) + name = row[ 'Name' ] + if name == 'Cost function value': + value = row[ 'Value' ] + elif name in parms.keys(): + fitted += '{0}={1}, '.format( name, row[ 'Value' ] ) + target = ', '.join( '{0}={1}'.format( key, val ) for key, val in parms.items() ) + msg = 'Cost function {0} too high\nTargets were {1},\nbut obtained {2}'.format( value, target, fitted ) + self.assertTrue( value < 5.0, msg ) + msg='Cost function {0}\nStarted with height=0.1, tau=100, beta=1\nTargets were {1},\nobtained {2}'.format( value, target, fitted ) + + mtd.remove( '_test_seft_data' ) + mtd.remove( '_test_seft_data_NormalisedCovarianceMatrix' ) + mtd.remove( '_test_seft_data_Parameters' ) + mtd.remove( '_test_seft_data_Workspace' ) if __name__ == '__main__': - unittest.main() + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/testhelpers/CMakeLists.txt b/Code/Mantid/Framework/PythonInterface/test/testhelpers/CMakeLists.txt index 1da906f087d7..487f06eda1dc 100644 --- a/Code/Mantid/Framework/PythonInterface/test/testhelpers/CMakeLists.txt +++ b/Code/Mantid/Framework/PythonInterface/test/testhelpers/CMakeLists.txt @@ -4,6 +4,7 @@ set ( PY_FILES __init__.py + algorithm_decorator.py ) # Copy python files to output directory diff --git a/Code/Mantid/Framework/PythonInterface/test/testhelpers/WorkspaceCreationHelperModule.cpp b/Code/Mantid/Framework/PythonInterface/test/testhelpers/WorkspaceCreationHelperModule.cpp index 9037799535ed..28e4e94c7e5f 100644 --- a/Code/Mantid/Framework/PythonInterface/test/testhelpers/WorkspaceCreationHelperModule.cpp +++ b/Code/Mantid/Framework/PythonInterface/test/testhelpers/WorkspaceCreationHelperModule.cpp @@ -17,8 +17,11 @@ using namespace WorkspaceCreationHelper; using namespace Mantid::MDEvents::MDEventsTestHelper; BOOST_PYTHON_FUNCTION_OVERLOADS(create2DWorkspaceWithFullInstrument_overloads, create2DWorkspaceWithFullInstrument, 2, 4); + BOOST_PYTHON_FUNCTION_OVERLOADS(makeFakeMDHistoWorkspace_overloads, makeFakeMDHistoWorkspace, 2, 7); +BOOST_PYTHON_FUNCTION_OVERLOADS(create2DWorkspaceWithRectangularInstrument_overloads, create2DWorkspaceWithRectangularInstrument, 3, 3); + namespace { /** @@ -45,9 +48,16 @@ BOOST_PYTHON_MODULE(WorkspaceCreationHelper) typedef MatrixWorkspace_sptr (*Signature1_2D)(int nHist, int nBins, bool includeMonitors, bool startYNegative); + // Forces it to return a MatrixWorkspace pointer rather than Workspace2D + typedef MatrixWorkspace_sptr (*Signature2_2D)(int numBanks, + int numPixels, + int numBins); def("create2DWorkspaceWithFullInstrument", (Signature1_2D)&create2DWorkspaceWithFullInstrument, create2DWorkspaceWithFullInstrument_overloads()); + def("create2DWorkspaceWithRectangularInstrument", (Signature2_2D)&create2DWorkspaceWithRectangularInstrument, + create2DWorkspaceWithRectangularInstrument_overloads()); + //=================================== Event Workspaces =================================== diff --git a/Code/Mantid/Framework/PythonInterface/test/testhelpers/algorithm_decorator.py b/Code/Mantid/Framework/PythonInterface/test/testhelpers/algorithm_decorator.py new file mode 100644 index 000000000000..91c0fd54546d --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/testhelpers/algorithm_decorator.py @@ -0,0 +1,61 @@ +import inspect +import re + +def make_decorator(algorithm_to_decorate): + """ + Dynamically create a builder pattern style decorator around a Mantid algorithm. + This allows you to separate out setting algorithm parameters from the actual method execution. Parameters may be reset multiple times. + + Usage: + rebin = make_decorator(Rebin) + rebin.set_Params([0, 0.1, 1]) + .... + rebin.execute() + + Arguments: + algorithm_to_decorate: The mantid.simpleapi algorithm to decorate. + + + + """ + + class Decorator(object): + + def __init__(self, alg_subject): + self.__alg_subject = alg_subject + self.__parameters__ = dict() + + def execute(self, additional=None, verbose=False): + if verbose: + print "Algorithm Parameters:" + print self.__parameters__ + print + out = self.__alg_subject(**self.__parameters__) + return out + + def set_additional(self, additional): + self.__parameters__.update(**additional) + + def add_getter_setter(type, name): + + def setter(self, x): + self.__parameters__[name] = x + + def getter(self): + return self.__parameters__[name] + + setattr(type, "set_" + name, setter) + setattr(type, "get_" + name, getter) + + + argspec = inspect.getargspec(algorithm_to_decorate) + for parameter in argspec.varargs.split(','): + m = re.search('(^\w+)', parameter) # Take the parameter key part from the defaults given as 'key=value' + if m: + parameter = m.group(0).strip() + m = re.search('\w+$', parameter) # strip off any leading numerical values produced by argspec + if m: + parameter = m.group(0).strip() + add_getter_setter(Decorator, m.group(0).strip()) + + return Decorator(algorithm_to_decorate) diff --git a/Code/Mantid/Framework/WorkflowAlgorithms/CMakeLists.txt b/Code/Mantid/Framework/WorkflowAlgorithms/CMakeLists.txt index a77011d32e40..650b41cd4654 100644 --- a/Code/Mantid/Framework/WorkflowAlgorithms/CMakeLists.txt +++ b/Code/Mantid/Framework/WorkflowAlgorithms/CMakeLists.txt @@ -19,6 +19,7 @@ set ( SRC_FILES src/HFIRLoad.cpp src/HFIRSANSNormalise.cpp src/MuonCalculateAsymmetry.cpp + src/MuonLoad.cpp src/RefReduction.cpp src/RefRoi.cpp src/SANSBeamFinder.cpp @@ -56,6 +57,7 @@ set ( INC_FILES inc/MantidWorkflowAlgorithms/HFIRLoad.h inc/MantidWorkflowAlgorithms/HFIRSANSNormalise.h inc/MantidWorkflowAlgorithms/MuonCalculateAsymmetry.h + inc/MantidWorkflowAlgorithms/MuonLoad.h inc/MantidWorkflowAlgorithms/RefReduction.h inc/MantidWorkflowAlgorithms/RefRoi.h inc/MantidWorkflowAlgorithms/SANSBeamFinder.h @@ -70,6 +72,7 @@ set ( INC_FILES set ( TEST_FILES MuonCalculateAsymmetryTest.h + MuonLoadTest.h SANSSolidAngleCorrectionTest.h StepScanTest.h ) diff --git a/Code/Mantid/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/MuonLoad.h b/Code/Mantid/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/MuonLoad.h new file mode 100644 index 000000000000..1a2e4b471f6c --- /dev/null +++ b/Code/Mantid/Framework/WorkflowAlgorithms/inc/MantidWorkflowAlgorithms/MuonLoad.h @@ -0,0 +1,73 @@ +#ifndef MANTID_WORKFLOWALGORITHMS_MUONLOAD_H_ +#define MANTID_WORKFLOWALGORITHMS_MUONLOAD_H_ + +#include "MantidKernel/System.h" +#include "MantidAPI/Algorithm.h" +#include "MantidDataObjects/TableWorkspace.h" + +namespace Mantid +{ +namespace WorkflowAlgorithms +{ + using namespace Kernel; + using namespace API; + using namespace DataObjects; + + /** MuonLoad : loads Muon workspace ready for analysis. + + Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: + Code Documentation is available at: + */ + class DLLExport MuonLoad : public API::Algorithm + { + public: + MuonLoad(); + virtual ~MuonLoad(); + + virtual const std::string name() const; + virtual int version() const; + virtual const std::string category() const; + + private: + virtual void initDocs(); + void init(); + void exec(); + + /// Returns a workspace for the first period as specified using FirstPeriod property. + MatrixWorkspace_sptr getFirstPeriodWS(WorkspaceGroup_sptr ws); + + /// Returns a workspace for the second period as specified using SecondPeriod property. + MatrixWorkspace_sptr getSecondPeriodWS(WorkspaceGroup_sptr ws); + + /// Groups specified workspace according to specified DetectorGroupingTable. + MatrixWorkspace_sptr groupWorkspace(MatrixWorkspace_sptr ws); + + /// Applies dead time correction to the workspace. + MatrixWorkspace_sptr applyDTC(MatrixWorkspace_sptr ws, TableWorkspace_sptr dt); + + /// Applies offset, crops and rebin the workspace according to specified params + MatrixWorkspace_sptr correctWorkspace(MatrixWorkspace_sptr ws, double loadedTimeZero); + }; + + +} // namespace WorkflowAlgorithms +} // namespace Mantid + +#endif /* MANTID_WORKFLOWALGORITHMS_MUONLOAD_H_ */ diff --git a/Code/Mantid/Framework/WorkflowAlgorithms/src/MuonLoad.cpp b/Code/Mantid/Framework/WorkflowAlgorithms/src/MuonLoad.cpp new file mode 100644 index 000000000000..4205786760c6 --- /dev/null +++ b/Code/Mantid/Framework/WorkflowAlgorithms/src/MuonLoad.cpp @@ -0,0 +1,377 @@ +/*WIKI* +TODO: Enter a full wiki-markup description of your algorithm here. You can then use the Build/wiki_maker.py script to generate your full wiki page. +*WIKI*/ + +#include "MantidWorkflowAlgorithms/MuonLoad.h" + +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/ListValidator.h" +#include "MantidAPI/FileProperty.h" +#include "MantidDataObjects/TableWorkspace.h" + +namespace Mantid +{ +namespace WorkflowAlgorithms +{ + using namespace Kernel; + using namespace API; + using namespace DataObjects; + + // Register the algorithm into the AlgorithmFactory + DECLARE_ALGORITHM(MuonLoad) + + //---------------------------------------------------------------------------------------------- + /** + * Constructor + */ + MuonLoad::MuonLoad() + { + } + + //---------------------------------------------------------------------------------------------- + /** + * Destructor + */ + MuonLoad::~MuonLoad() + { + } + + + //---------------------------------------------------------------------------------------------- + /// Algorithm's name for identification. @see Algorithm::name + const std::string MuonLoad::name() const { return "MuonLoad";}; + + /// Algorithm's version for identification. @see Algorithm::version + int MuonLoad::version() const { return 1;}; + + /// Algorithm's category for identification. @see Algorithm::category + const std::string MuonLoad::category() const { return "Workflow\\Muon";} + + //---------------------------------------------------------------------------------------------- + + /// Sets documentation strings for this algorithm + void MuonLoad::initDocs() + { + this->setWikiSummary("Loads Muon workspace ready for analysis."); + this->setOptionalMessage("Loads Muon workspace ready for analysis."); + } + + //---------------------------------------------------------------------------------------------- + /* + * Initialize the algorithm's properties. + */ + void MuonLoad::init() + { + declareProperty(new FileProperty("Filename", "", FileProperty::Load, ".nxs"), + "The name of the Nexus file to load" ); + + declareProperty("FirstPeriod", 0, "Group index of the first period workspace to use"); + declareProperty("SecondPeriod", EMPTY_INT(), "Group index of the first period workspace to use"); + + std::vector allowedOperations; + allowedOperations.push_back("+"); + allowedOperations.push_back("-"); + declareProperty("PeriodOperation","+", boost::make_shared(allowedOperations), + "If two periods specified, what operation to apply to workspaces to get a final one."); + + declareProperty("ApplyDeadTimeCorrection", false, + "Whether dead time correction should be applied to loaded workspace"); + declareProperty(new WorkspaceProperty("CustomDeadTimeTable", "",Direction::Input, + PropertyMode::Optional), "Table with dead time information. See LoadMuonNexus for format expected."); + + declareProperty(new WorkspaceProperty("DetectorGroupingTable","",Direction::Input), + "Table with detector grouping information. See LoadMuonNexus for format expected."); + + declareProperty("TimeZero", EMPTY_DBL(), "Value used for Time Zero correction."); + declareProperty(new ArrayProperty("RebinParams"), + "Params used for rebinning. If empty - rebinning is not done."); + declareProperty("Xmin", EMPTY_DBL(), "Minimal X value to include"); + declareProperty("Xmax", EMPTY_DBL(), "Maximal X value to include"); + + std::vector allowedTypes; + allowedTypes.push_back("PairAsymmetry"); + allowedTypes.push_back("GroupAsymmetry"); + allowedTypes.push_back("GroupCounts"); + declareProperty("OutputType", "PairAsymmetry", boost::make_shared(allowedTypes), + "What kind of workspace required for analysis."); + + declareProperty("PairFirstIndex", EMPTY_INT(), "Workspace index of the first pair group"); + declareProperty("PairSecondIndex", EMPTY_INT(), "Workspace index of the second pair group"); + declareProperty("Alpha", 1.0, "Alpha value of the pair"); + + declareProperty("GroupIndex", EMPTY_INT(), "Workspace index of the group"); + + declareProperty(new WorkspaceProperty("OutputWorkspace","",Direction::Output), + "An output workspace."); + } + + //---------------------------------------------------------------------------------------------- + /** + * Execute the algorithm. + */ + void MuonLoad::exec() + { + const std::string filename = getProperty("Filename"); + + // Load the file + IAlgorithm_sptr load = createChildAlgorithm("LoadMuonNexus"); + load->setProperty("Filename", filename); + load->setProperty("DeadTimeTable", "__YouDontSeeMeIAmNinja"); // Name is not used (child alg.) + load->execute(); + + Workspace_sptr loadedWS = load->getProperty("OutputWorkspace"); + + MatrixWorkspace_sptr firstPeriodWS, secondPeriodWS; + + // Deal with single-period workspace + if ( auto ws = boost::dynamic_pointer_cast(loadedWS) ) + { + if ( static_cast( getProperty("FirstPeriod") ) != 0 ) + throw std::invalid_argument("Single period data but first period is not 0."); + + if ( static_cast( getProperty("SecondPeriod") ) != EMPTY_INT() ) + throw std::invalid_argument("Single period data but second period specified"); + + firstPeriodWS = ws; + } + // Deal with multi-period workspace + else if ( auto group = boost::dynamic_pointer_cast(loadedWS) ) + { + firstPeriodWS = getFirstPeriodWS(group); + secondPeriodWS = getSecondPeriodWS(group); + } + // Unexpected workspace type + else + { + throw std::runtime_error("Loaded workspace is of invalid type"); + } + + + // Deal with dead time correction (if required + bool applyDtc = getProperty("ApplyDeadTimeCorrection"); + if ( applyDtc ) + { + TableWorkspace_sptr deadTimes = getProperty("CustomDeadTimeTable"); + + if ( ! deadTimes ) + { + // If no dead times specified - try to use ones from the file + Workspace_sptr loadedDeadTimes = load->getProperty("DeadTimeTable"); + + if ( auto table = boost::dynamic_pointer_cast(loadedDeadTimes) ) + { + deadTimes = table; + } + else if ( auto group = boost::dynamic_pointer_cast(loadedDeadTimes) ) + { + deadTimes = boost::dynamic_pointer_cast( group->getItem(0) ); + } + + if ( ! deadTimes ) + throw std::runtime_error("File doesn't contain any dead times"); + } + + firstPeriodWS = applyDTC(firstPeriodWS, deadTimes); + + if ( secondPeriodWS ) + secondPeriodWS = applyDTC(secondPeriodWS, deadTimes); + } + + // Deal with grouping + firstPeriodWS = groupWorkspace(firstPeriodWS); + + if ( secondPeriodWS ) + secondPeriodWS = groupWorkspace(secondPeriodWS); + + // Correct bin values + double loadedTimeZero = load->getProperty("TimeZero"); + + firstPeriodWS = correctWorkspace(firstPeriodWS, loadedTimeZero); + + if ( secondPeriodWS ) + secondPeriodWS = correctWorkspace(secondPeriodWS, loadedTimeZero); + + IAlgorithm_sptr calcAssym = createChildAlgorithm("MuonCalculateAsymmetry"); + + // Set first period workspace + calcAssym->setProperty("FirstPeriodWorkspace", firstPeriodWS); + + // Set second period workspace, if have one + if ( secondPeriodWS ) + calcAssym->setProperty("SecondPeriodWorkspace", secondPeriodWS); + + // Copy similar properties over + calcAssym->setProperty("PeriodOperation", + static_cast( getProperty("PeriodOperation") ) ); + calcAssym->setProperty("OutputType", + static_cast( getProperty("OutputType") ) ); + calcAssym->setProperty("PairFirstIndex", + static_cast( getProperty("PairFirstIndex") ) ); + calcAssym->setProperty("PairSecondIndex", + static_cast( getProperty("PairSecondIndex") ) ); + calcAssym->setProperty("Alpha", + static_cast( getProperty("Alpha") ) ); + calcAssym->setProperty("GroupIndex", + static_cast( getProperty("GroupIndex") ) ); + + calcAssym->execute(); + + MatrixWorkspace_sptr outWS = calcAssym->getProperty("OutputWorkspace"); + setProperty("OutputWorkspace", outWS); + } + + /** + * Returns a workspace for the first period as specified using FirstPeriod property. + * @param group :: Loaded group of workspaces to use + * @return Workspace for the period + */ + MatrixWorkspace_sptr MuonLoad::getFirstPeriodWS(WorkspaceGroup_sptr group) + { + int firstPeriod = getProperty("FirstPeriod"); + + MatrixWorkspace_sptr resultWS; + + if ( firstPeriod < 0 || firstPeriod >= static_cast( group->size() ) ) + throw std::invalid_argument("Workspace doesn't contain specified first period"); + + resultWS = boost::dynamic_pointer_cast( group->getItem(firstPeriod) ); + + if ( ! resultWS ) + throw std::invalid_argument("First period workspace is not a MatrixWorkspace"); + + return resultWS; + } + + /** + * Returns a workspace for the second period as specified using SecondPeriod property. + * @param group :: Loaded group of workspaces to use + * @return Workspace for the period + */ + MatrixWorkspace_sptr MuonLoad::getSecondPeriodWS(WorkspaceGroup_sptr group) + { + int secondPeriod = getProperty("SecondPeriod"); + + MatrixWorkspace_sptr resultWS; + + if ( secondPeriod != EMPTY_INT() ) + { + if ( secondPeriod < 0 || secondPeriod >= static_cast( group->size() ) ) + throw std::invalid_argument("Workspace doesn't contain specified second period"); + + resultWS = boost::dynamic_pointer_cast( group->getItem(secondPeriod) ); + + if ( ! resultWS ) + throw std::invalid_argument("Second period workspace is not a MatrixWorkspace"); + } + + return resultWS; + } + + /** + * Groups specified workspace according to specified DetectorGroupingTable. + * @param ws :: Workspace to group + * @return Grouped workspace + */ + MatrixWorkspace_sptr MuonLoad::groupWorkspace(MatrixWorkspace_sptr ws) + { + TableWorkspace_sptr grouping = getProperty("DetectorGroupingTable"); + + IAlgorithm_sptr group = createChildAlgorithm("MuonGroupDetectors"); + group->setProperty("InputWorkspace", ws); + group->setProperty("DetectorGroupingTable", grouping); + group->execute(); + + return group->getProperty("OutputWorkspace"); + } + + /** + * Applies dead time correction to the workspace. + * @param ws :: Workspace to apply correction + * @param dt :: Dead time table to use + * @return Corrected workspace + */ + MatrixWorkspace_sptr MuonLoad::applyDTC(MatrixWorkspace_sptr ws, TableWorkspace_sptr dt) + { + IAlgorithm_sptr dtc = createChildAlgorithm("ApplyDeadTimeCorr"); + dtc->setProperty("InputWorkspace", ws); + dtc->setProperty("DeadTimeTable", dt); + dtc->execute(); + + return dtc->getProperty("OutputWorkspace"); + } + + /** + * Applies offset, crops and rebin the workspace according to specified params. + * @param ws :: Workspace to correct + * @param loadedTimeZero :: Time zero of the data, so we can calculate the offset + * @return Corrected workspace + */ + MatrixWorkspace_sptr MuonLoad::correctWorkspace(MatrixWorkspace_sptr ws, double loadedTimeZero) + { + // Offset workspace, if need to + double timeZero = getProperty("TimeZero"); + if ( timeZero != EMPTY_DBL() ) + { + double offset = loadedTimeZero - timeZero; + + IAlgorithm_sptr changeOffset = createChildAlgorithm("ChangeBinOffset"); + changeOffset->setProperty("InputWorkspace", ws); + changeOffset->setProperty("Offset", offset); + changeOffset->execute(); + + ws = changeOffset->getProperty("OutputWorkspace"); + } + + // Crop workspace, if need to + double Xmin = getProperty("Xmin"); + double Xmax = getProperty("Xmax"); + if ( Xmin != EMPTY_DBL() || Xmax != EMPTY_DBL() ) + { + IAlgorithm_sptr crop = createChildAlgorithm("CropWorkspace"); + crop->setProperty("InputWorkspace", ws); + + if ( Xmin != EMPTY_DBL() ) + crop->setProperty("Xmin", Xmin); + + if ( Xmax != EMPTY_DBL() ) + crop->setProperty("Xmax", Xmax); + + crop->execute(); + + ws = crop->getProperty("OutputWorkspace"); + } + + // Rebin workspace if need to + std::vector rebinParams = getProperty("RebinParams"); + if ( ! rebinParams.empty() ) + { + IAlgorithm_sptr rebin = createChildAlgorithm("Rebin"); + rebin->setProperty("InputWorkspace", ws); + rebin->setProperty("Params", rebinParams); + rebin->execute(); + + ws = rebin->getProperty("OutputWorkspace"); + + // TODO: following should be removed when FullBinsOnly function is added to Rebin algorithm + + // Muon group don't want last bin if shorter than previous bins + double binSize = ws->dataX(0)[1] - ws->dataX(0)[0]; + size_t numBins = ws->dataX(0).size(); + + if ( (ws->dataX(0)[numBins-1] - ws->dataX(0)[numBins-2]) != binSize ) + { + IAlgorithm_sptr crop2 = createChildAlgorithm("CropWorkspace"); + crop2->setProperty("InputWorkspace", ws); + crop2->setProperty("Xmax", ws->dataX(0)[numBins-2]); + crop2->execute(); + + ws = crop2->getProperty("OutputWorkspace"); + } + } + + + return ws; + } + +} // namespace WorkflowAlgorithms +} // namespace Mantid diff --git a/Code/Mantid/Framework/WorkflowAlgorithms/test/MuonLoadTest.h b/Code/Mantid/Framework/WorkflowAlgorithms/test/MuonLoadTest.h new file mode 100644 index 000000000000..8ebb33e6044c --- /dev/null +++ b/Code/Mantid/Framework/WorkflowAlgorithms/test/MuonLoadTest.h @@ -0,0 +1,283 @@ +#ifndef MANTID_WORKFLOWALGORITHMS_MUONLOADTEST_H_ +#define MANTID_WORKFLOWALGORITHMS_MUONLOADTEST_H_ + +#include + +#include "MantidWorkflowAlgorithms/MuonLoad.h" +#include "MantidAPI/ScopedWorkspace.h" +#include "MantidAPI/TableRow.h" +#include "MantidDataObjects/TableWorkspace.h" + +using Mantid::WorkflowAlgorithms::MuonLoad; + +using namespace Mantid::Kernel; +using namespace Mantid::API; +using namespace Mantid::DataObjects; + +class MuonLoadTest : public CxxTest::TestSuite +{ +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static MuonLoadTest *createSuite() { return new MuonLoadTest(); } + static void destroySuite( MuonLoadTest *suite ) { delete suite; } + + + void test_Init() + { + MuonLoad alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + } + + void test_simpleLoad() + { + ScopedWorkspace output; + + std::vector group1, group2; + + for ( int i = 1; i <= 16; ++i ) + group1.push_back(i); + + for ( int i = 17; i <= 32; ++i ) + group2.push_back(i); + + TableWorkspace_sptr grouping = createGroupingTable(group1, group2); + + MuonLoad alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", "emu00006473.nxs") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("DetectorGroupingTable", grouping) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("OutputType", "GroupCounts") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("GroupIndex", 0) ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", output.name()) ); + TS_ASSERT_THROWS_NOTHING( alg.execute(); ); + TS_ASSERT( alg.isExecuted() ); + + // Retrieve the workspace from data service. + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast( output.retrieve() ); + + TS_ASSERT(ws); + if (ws) + { + TS_ASSERT_EQUALS( ws->getNumberHistograms(), 1 ); + TS_ASSERT_EQUALS( ws->blocksize(), 2000 ); + + TS_ASSERT_EQUALS( ws->readY(0)[0], 461 ); + TS_ASSERT_EQUALS( ws->readY(0)[1000], 192 ); + TS_ASSERT_EQUALS( ws->readY(0)[1752], 5 ); + + TS_ASSERT_DELTA( ws->readE(0)[0], 21.471, 0.001 ); + TS_ASSERT_DELTA( ws->readE(0)[1000], 13.856, 0.001 ); + TS_ASSERT_DELTA( ws->readE(0)[1752], 2.236, 0.001 ); + + TS_ASSERT_DELTA( ws->readX(0)[0], -0.254, 0.001 ); + TS_ASSERT_DELTA( ws->readX(0)[1000], 15.746, 0.001 ); + TS_ASSERT_DELTA( ws->readX(0)[1752], 27.778, 0.001 ); + } + } + + void test_multiPeriod() + { + ScopedWorkspace output; + + std::vector group1, group2; + + for ( int i = 33; i <= 64; ++i ) + group1.push_back(i); + for ( int i = 1; i <= 32; ++i ) + group2.push_back(i); + + TableWorkspace_sptr grouping = createGroupingTable(group1, group2); + + MuonLoad alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", "MUSR00015189.nxs") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("FirstPeriod", 0) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("SecondPeriod", 1) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("PeriodOperation", "+") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("DetectorGroupingTable", grouping) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("OutputType", "GroupCounts") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("GroupIndex", 1) ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", output.name()) ); + TS_ASSERT_THROWS_NOTHING( alg.execute(); ); + TS_ASSERT( alg.isExecuted() ); + + // Retrieve the workspace from data service. + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast( output.retrieve() ); + + TS_ASSERT(ws); + if (ws) + { + TS_ASSERT_EQUALS( ws->getNumberHistograms(), 1 ); + TS_ASSERT_EQUALS( ws->blocksize(), 2000 ); + + TS_ASSERT_EQUALS( ws->readY(0)[0], 23); + TS_ASSERT_EQUALS( ws->readY(0)[1000], 3); + TS_ASSERT_EQUALS( ws->readY(0)[1701], 1); + + TS_ASSERT_DELTA( ws->readE(0)[0], 4.796, 0.001 ); + TS_ASSERT_DELTA( ws->readE(0)[1000], 1.732, 0.001 ); + TS_ASSERT_DELTA( ws->readE(0)[1701], 1.000, 0.001 ); + + TS_ASSERT_DELTA( ws->readX(0)[0], -0.550, 0.001 ); + TS_ASSERT_DELTA( ws->readX(0)[1000], 15.450, 0.001 ); + TS_ASSERT_DELTA( ws->readX(0)[1701], 26.666, 0.001 ); + } + } + + void test_binCorrectionParams() + { + ScopedWorkspace output; + + std::vector group1, group2; + + for ( int i = 1; i <= 16; ++i ) + group1.push_back(i); + for ( int i = 17; i <= 32; ++i ) + group2.push_back(i); + + TableWorkspace_sptr grouping = createGroupingTable(group1, group2); + + MuonLoad alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", "emu00006473.nxs") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("DetectorGroupingTable", grouping) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("OutputType", "GroupCounts") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("GroupIndex", 0) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("TimeZero", 0.5) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("Xmin", 0.1) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("Xmax", 16.0) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("RebinParams", "0.08") ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", output.name()) ); + TS_ASSERT_THROWS_NOTHING( alg.execute(); ); + TS_ASSERT( alg.isExecuted() ); + + // Retrieve the workspace from data service. + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast( output.retrieve() ); + + TS_ASSERT(ws); + if (ws) + { + TS_ASSERT_EQUALS( ws->getNumberHistograms(), 1 ); + TS_ASSERT_EQUALS( ws->blocksize(), 198 ); + + TS_ASSERT_DELTA( ws->readX(0)[0], 0.102, 0.001 ); + TS_ASSERT_DELTA( ws->readX(0)[100], 8.102, 0.001 ); + TS_ASSERT_DELTA( ws->readX(0)[198], 15.942, 0.001 ); + + TS_ASSERT_DELTA( ws->readY(0)[0], 1024372.2, 0.1); + TS_ASSERT_DELTA( ws->readY(0)[100], 24589.0, 0.1); + TS_ASSERT_DELTA( ws->readY(0)[197], 730.0, 0.1); + + TS_ASSERT_DELTA( ws->readE(0)[0], 1012.113, 0.001 ); + TS_ASSERT_DELTA( ws->readE(0)[100], 156.809, 0.001 ); + TS_ASSERT_DELTA( ws->readE(0)[197], 27.019, 0.001 ); + } + } + + void test_deadTimeCorrection() + { + ScopedWorkspace output; + + std::vector group1, group2; + + for ( int i = 1; i <= 16; ++i ) + group1.push_back(i); + for ( int i = 17; i <= 32; ++i ) + group2.push_back(i); + + TableWorkspace_sptr grouping = createGroupingTable(group1, group2); + + auto deadTimes = boost::make_shared(); + deadTimes->addColumn("int", "spectrum"); + deadTimes->addColumn("double", "dead-time"); + + for ( int i = 0; i < 32; ++i ) + { + TableRow newRow = deadTimes->appendRow(); + newRow << (i+1) << 1.0; + } + + MuonLoad alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", "emu00006473.nxs") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("DetectorGroupingTable", grouping) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("ApplyDeadTimeCorrection", true) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("CustomDeadTimeTable", deadTimes) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("OutputType", "GroupCounts") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("GroupIndex", 0) ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", output.name()) ); + TS_ASSERT_THROWS_NOTHING( alg.execute(); ); + TS_ASSERT( alg.isExecuted() ); + + // Retrieve the workspace from data service. + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast( output.retrieve() ); + + TS_ASSERT(ws); + if (ws) + { + TS_ASSERT_EQUALS( ws->getNumberHistograms(), 1 ); + TS_ASSERT_EQUALS( ws->blocksize(), 2000 ); + + TS_ASSERT_DELTA( ws->readY(0)[0], 463.383, 0.001 ); + TS_ASSERT_DELTA( ws->readY(0)[1000], 192.468, 0.001 ); + TS_ASSERT_DELTA( ws->readY(0)[1752], 5.00075, 0.00001 ) ; + + TS_ASSERT_DELTA( ws->readE(0)[0], 21.471, 0.001 ); + TS_ASSERT_DELTA( ws->readE(0)[1000], 13.856, 0.001 ); + TS_ASSERT_DELTA( ws->readE(0)[1752], 2.236, 0.001 ); + + TS_ASSERT_DELTA( ws->readX(0)[0], -0.254, 0.001 ); + TS_ASSERT_DELTA( ws->readX(0)[1000], 15.746, 0.001 ); + TS_ASSERT_DELTA( ws->readX(0)[1752], 27.778, 0.001 ); + } + } + + void test_errorReporting() + { + ScopedWorkspace output; + + auto emptyGrouping = createGroupingTable(std::vector(), std::vector()); + + MuonLoad alg; + alg.setRethrows(true); + + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + + TS_ASSERT_THROWS( alg.setPropertyValue("Filename", "non-existent-file.nxs"), std::invalid_argument ); + + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("Filename", "emu00006473.nxs") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("DetectorGroupingTable", emptyGrouping) ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("OutputType", "GroupCounts") ); + TS_ASSERT_THROWS_NOTHING( alg.setProperty("GroupIndex", 0) ); + TS_ASSERT_THROWS_NOTHING( alg.setPropertyValue("OutputWorkspace", output.name()) ); + + TS_ASSERT_THROWS( alg.execute(), std::invalid_argument ); + TS_ASSERT( ! alg.isExecuted() ); + } + + TableWorkspace_sptr createGroupingTable(const std::vector& group1, const std::vector& group2) + { + auto t = boost::make_shared(); + + t->addColumn("vector_int", "Detectors"); + + TableRow row1 = t->appendRow(); + row1 << group1; + + TableRow row2 = t->appendRow(); + row2 << group2; + + return t; + } + +}; + + +#endif /* MANTID_WORKFLOWALGORITHMS_MUONLOADTEST_H_ */ diff --git a/Code/Mantid/MantidPlot/CMakeLists.txt b/Code/Mantid/MantidPlot/CMakeLists.txt index 91f93b6caef4..1c4dd82b4455 100644 --- a/Code/Mantid/MantidPlot/CMakeLists.txt +++ b/Code/Mantid/MantidPlot/CMakeLists.txt @@ -232,6 +232,7 @@ set ( MANTID_SRCS src/Mantid/AlgorithmMonitor.cpp src/Mantid/InstrumentWidget/SimpleWidget.cpp src/Mantid/InstrumentWidget/Viewport.cpp src/Mantid/InstrumentWidget/DetXMLFile.cpp + src/Mantid/InstrumentWidget/UCorrectionDialog.cpp ) ########################################################################### @@ -465,6 +466,7 @@ set ( MANTID_HDRS src/Mantid/AlgorithmMonitor.h src/Mantid/InstrumentWidget/SimpleWidget.h src/Mantid/InstrumentWidget/Viewport.h src/Mantid/InstrumentWidget/DetXMLFile.h + src/Mantid/InstrumentWidget/UCorrectionDialog.h ) ########################################################################### @@ -709,6 +711,7 @@ set ( MANTID_MOC_FILES src/Mantid/AlgorithmMonitor.h src/Mantid/InstrumentWidget/Projection3D.h src/Mantid/InstrumentWidget/Shape2DCollection.h src/Mantid/InstrumentWidget/UnwrappedSurface.h + src/Mantid/InstrumentWidget/UCorrectionDialog.h ) set ( UI_FILES src/SendToProgramDialog.ui @@ -720,6 +723,7 @@ set ( UI_FILES src/SendToProgramDialog.ui src/Mantid/ManageCustomMenus.ui src/Mantid/ManageInterfaceCategories.ui src/Mantid/MantidMDCurveDialog.ui + src/Mantid/InstrumentWidget/UCorrectionDialog.ui ) qt4_wrap_cpp ( MOCCED_FILES ${QTIPLOT_MOC_FILES} ${MANTID_MOC_FILES} ) diff --git a/Code/Mantid/MantidPlot/src/ApplicationWindow.cpp b/Code/Mantid/MantidPlot/src/ApplicationWindow.cpp index 43d64450abcd..a79e34c4f9d3 100644 --- a/Code/Mantid/MantidPlot/src/ApplicationWindow.cpp +++ b/Code/Mantid/MantidPlot/src/ApplicationWindow.cpp @@ -9297,6 +9297,17 @@ void ApplicationWindow::windowsMenuAboutToShow() reloadCustomActions(); } +namespace // anonymous +{ + /** + * Helper function used with Qt's qSort to make sure interfaces are in alphabetical order. + */ + bool interfaceNameComparator(const QPair & lhs, const QPair & rhs) + { + return lhs.first.toLower() < rhs.first.toLower(); + } +} // anonymous namespace + void ApplicationWindow::interfaceMenuAboutToShow() { interfaceMenu->clear(); @@ -9321,6 +9332,10 @@ void ApplicationWindow::interfaceMenuAboutToShow() categoryMenus[category] = categoryMenu; } + // Show the interfaces in alphabetical order in their respective submenus. + qSort(m_interfaceNameDataPairs.begin(), m_interfaceNameDataPairs.end(), + interfaceNameComparator); + // Turn the name/data pairs into QActions with which we populate the menus. foreach(const auto interfaceNameDataPair, m_interfaceNameDataPairs) { @@ -16338,6 +16353,7 @@ void ApplicationWindow::executeScriptFile(const QString & filename, const Script code += in.readLine() + "\n"; } Script *runner = scriptingEnv()->newScript(filename, this, Script::NonInteractive); + connect(runner, SIGNAL(error(const QString &, const QString &, int)), this, SLOT(onScriptExecuteError(const QString &, const QString &, int))); runner->redirectStdOut(false); scriptingEnv()->redirectStdOut(false); if(execMode == Script::Asynchronous) @@ -16358,6 +16374,24 @@ void ApplicationWindow::executeScriptFile(const QString & filename, const Script delete runner; } +/** + * This is the slot for handing script execution errors. It is only + * attached by ::executeScriptFile which is only done in the '-xq' + * command line option. + * + * @param message Normally the stacktrace of the error. + * @param scriptName The name of the file. + * @param lineNumber The line number in the script that caused the error. + */ +void ApplicationWindow::onScriptExecuteError(const QString & message, const QString & scriptName, int lineNumber) +{ + g_log.fatal() << "Fatal error on line " << lineNumber << " of \"" << scriptName.toStdString() + << "\" encountered:\n" + << message.toStdString(); + this->setExitCode(1); + this->exitWithPresetCode(); +} + /** * Run Python code * @param code :: An arbitrary string of python code diff --git a/Code/Mantid/MantidPlot/src/ApplicationWindow.h b/Code/Mantid/MantidPlot/src/ApplicationWindow.h index 092468370c84..175533825b5b 100644 --- a/Code/Mantid/MantidPlot/src/ApplicationWindow.h +++ b/Code/Mantid/MantidPlot/src/ApplicationWindow.h @@ -238,6 +238,8 @@ public slots: ApplicationWindow * loadScript(const QString& fn); /// Runs a script from a file. Mainly useful for automatically running scripts void executeScriptFile(const QString & filename, const Script::ExecutionMode execMode); + /// Slot to connect the script execution errors to + void onScriptExecuteError(const QString & message, const QString & scriptName, int lineNumber); /// Runs an arbitrary lump of python code, return true/false on success/failure. bool runPythonScript(const QString & code, bool async = false, bool quiet=false, bool redirect=true); diff --git a/Code/Mantid/MantidPlot/src/ConfigDialog.cpp b/Code/Mantid/MantidPlot/src/ConfigDialog.cpp index b400b248f472..567357452db8 100644 --- a/Code/Mantid/MantidPlot/src/ConfigDialog.cpp +++ b/Code/Mantid/MantidPlot/src/ConfigDialog.cpp @@ -863,11 +863,18 @@ void ConfigDialog::addDialog() //Edit a program void ConfigDialog::editDialog() { - QList selectedItems = treePrograms->selectedItems(); - - std::map programKeysAndDetails = m_sendToSettings.find(selectedItems[0]->text(0).toStdString())->second; + auto firstSelectedItem = treePrograms->selectedItems()[0]; + auto selectedProgram = m_sendToSettings.find(firstSelectedItem->text(0).toStdString()); + // If the program name itself isn't initially selected, recurse up. + while ( selectedProgram == m_sendToSettings.end() ) + { + firstSelectedItem = treePrograms->itemAbove(firstSelectedItem); + // It shouldn't happen that we get to the top without finding anything, but should this happen just return + if ( firstSelectedItem == treePrograms->invisibleRootItem() ) return; + selectedProgram = m_sendToSettings.find(firstSelectedItem->text(0).toStdString()); + } - SendToProgramDialog* editProgram = new SendToProgramDialog(this, selectedItems[0]->text(0), programKeysAndDetails); + SendToProgramDialog* editProgram = new SendToProgramDialog(this, firstSelectedItem->text(0), selectedProgram->second); editProgram->setWindowTitle(tr("Edit a Program")); editProgram->setModal(true); diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentActor.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentActor.cpp index dc673e8ce45a..9b5af4b3eb39 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentActor.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentActor.cpp @@ -60,61 +60,25 @@ m_maskedColor(100,100,100), m_failedColor(200,200,200), m_sampleActor(NULL) { - auto shared_workspace = m_workspace.lock(); - if (!shared_workspace) - throw std::logic_error("InstrumentActor passed a workspace that isn't a MatrixWorkspace"); - - const size_t nHist = shared_workspace->getNumberHistograms(); - m_WkspBinMin = DBL_MAX; - m_WkspBinMax = -DBL_MAX; - for (size_t i = 0; i < nHist; ++i) - { - const Mantid::MantidVec & values = shared_workspace->readX(i); - double xtest = values.front(); - if( xtest != std::numeric_limits::infinity() ) - { - - if( xtest < m_WkspBinMin ) - { - m_WkspBinMin = xtest; - } - else if( xtest > m_WkspBinMax ) - { - m_WkspBinMax = xtest; - } - else {} - } - - xtest = values.back(); - if( xtest != std::numeric_limits::infinity() ) - { - if( xtest < m_WkspBinMin ) - { - m_WkspBinMin = xtest; - } - else if( xtest > m_WkspBinMax ) - { - m_WkspBinMax = xtest; - } - else {} - } - } - m_WkspDataPositiveMin = DBL_MAX; + // settings loadSettings(); - if ( !m_autoscaling ) + auto sharedWorkspace = m_workspace.lock(); + if (!sharedWorkspace) + throw std::logic_error("InstrumentActor passed a workspace that isn't a MatrixWorkspace"); + + // set up the color map + if (!m_currentColorMapFilename.isEmpty()) { - m_DataMinValue = -DBL_MAX; - m_DataMaxValue = DBL_MAX; - setMinMaxRange( scaleMin, scaleMax ); + loadColorMap(m_currentColorMapFilename,false); } + m_colorMap.changeScaleType(m_scaleType); - blockSignals(true); - setIntegrationRange(m_WkspBinMin,m_WkspBinMax); - blockSignals(false); + // set up data ranges and colours + setUpWorkspace(sharedWorkspace, scaleMin, scaleMax); /// Keep the pointer to the detid2index map - m_detid2index_map = shared_workspace->getDetectorIDToWorkspaceIndexMap(); + m_detid2index_map = sharedWorkspace->getDetectorIDToWorkspaceIndexMap(); Instrument_const_sptr instrument = getInstrument(); @@ -135,7 +99,7 @@ m_sampleActor(NULL) accept(findVisitor,GLActor::Finish); const ObjComponentActor* samplePosActor = dynamic_cast(findVisitor.getActor()); - m_sampleActor = new SampleActor(*this,shared_workspace->sample(),samplePosActor); + m_sampleActor = new SampleActor(*this,sharedWorkspace->sample(),samplePosActor); m_scene.addActor(m_sampleActor); if ( !m_showGuides ) { @@ -152,11 +116,69 @@ InstrumentActor::~InstrumentActor() saveSettings(); } +/** + * Set up the workspace: calculate the value ranges, set the colours. + * @param sharedWorkspace :: A shared pointer to the workspace. + * @param scaleMin :: Minimum limit on the color map axis. If autoscale this value is ignored. + * @param scaleMax :: Maximum limit on the color map axis. If autoscale this value is ignored. + */ +void InstrumentActor::setUpWorkspace(boost::shared_ptr sharedWorkspace, double scaleMin, double scaleMax) +{ + const size_t nHist = sharedWorkspace->getNumberHistograms(); + m_WkspBinMinValue = DBL_MAX; + m_WkspBinMaxValue = -DBL_MAX; + for (size_t i = 0; i < nHist; ++i) + { + const Mantid::MantidVec & values = sharedWorkspace->readX(i); + double xtest = values.front(); + if( xtest != std::numeric_limits::infinity() ) + { + + if( xtest < m_WkspBinMinValue ) + { + m_WkspBinMinValue = xtest; + } + else if( xtest > m_WkspBinMaxValue ) + { + m_WkspBinMaxValue = xtest; + } + else {} + } + + xtest = values.back(); + if( xtest != std::numeric_limits::infinity() ) + { + if( xtest < m_WkspBinMinValue ) + { + m_WkspBinMinValue = xtest; + } + else if( xtest > m_WkspBinMaxValue ) + { + m_WkspBinMaxValue = xtest; + } + else {} + } + } + + // set some values as the variables will be used + m_DataPositiveMinValue = DBL_MAX; + m_DataMinValue = -DBL_MAX; + m_DataMaxValue = DBL_MAX; + + if ( !m_autoscaling ) + { + setDataMinMaxRange( scaleMin, scaleMax ); + } + setDataIntegrationRange(m_WkspBinMinValue,m_WkspBinMaxValue); + resetColors(); + +} + /** Used to set visibility of an actor corresponding to a particular component * When selecting a component in the InstrumentTreeWidget * - * @param visitor - * @return + * @param visitor :: Visitor to be accepted bu this actor. + * @param rule :: A rule defining visitor acceptance by assembly actors. */ bool InstrumentActor::accept(GLActorVisitor& visitor, VisitorAcceptRule rule) { @@ -196,14 +218,14 @@ bool InstrumentActor::hasChildVisible() const */ MatrixWorkspace_const_sptr InstrumentActor::getWorkspace() const { - auto shared_workspace = m_workspace.lock(); + auto sharedWorkspace = m_workspace.lock(); - if ( !shared_workspace ) + if ( !sharedWorkspace ) { throw std::runtime_error("Instrument view: workspace doesn't exist"); } - return shared_workspace; + return sharedWorkspace; } /** Returns the mask workspace relating to this instrument view as a MatrixWorkspace @@ -287,23 +309,23 @@ Instrument_const_sptr InstrumentActor::getInstrument() const // to define our 'default' view std::string view = Mantid::Kernel::ConfigService::Instance().getString("instrument.view.geometry"); - auto shared_workspace = getWorkspace(); - Mantid::Kernel::ReadLock _lock(*shared_workspace); + auto sharedWorkspace = getWorkspace(); + Mantid::Kernel::ReadLock _lock(*sharedWorkspace); if ( boost::iequals("Default", view) || boost::iequals("Physical", view)) { // First see if there is a 'physical' instrument available. Use it if there is. - retval = shared_workspace->getInstrument()->getPhysicalInstrument(); + retval = sharedWorkspace->getInstrument()->getPhysicalInstrument(); } else if (boost::iequals("Neutronic", view)) { - retval = shared_workspace->getInstrument(); + retval = sharedWorkspace->getInstrument(); } if ( ! retval ) { // Otherwise get hold of the 'main' instrument and use that - retval = shared_workspace->getInstrument(); + retval = sharedWorkspace->getInstrument(); } return retval; @@ -354,54 +376,7 @@ size_t InstrumentActor::getWorkspaceIndex(Mantid::detid_t id) const */ void InstrumentActor::setIntegrationRange(const double& xmin,const double& xmax) { - if (!getWorkspace()) return; - - m_BinMinValue = xmin; - m_BinMaxValue = xmax; - - bool binEntireRange = m_BinMinValue == m_WkspBinMin && m_BinMaxValue == m_WkspBinMax; - - //Use the workspace function to get the integrated spectra - getWorkspace()->getIntegratedSpectra(m_specIntegrs, m_BinMinValue, m_BinMaxValue, binEntireRange); - - m_DataMinValue = DBL_MAX; - m_DataMaxValue = -DBL_MAX; - - //Now we need to convert to a vector where each entry is the sum for the detector ID at that spot (in integrated_values). - for (size_t i=0; i < m_specIntegrs.size(); i++) - { - double sum = m_specIntegrs[i]; - if( boost::math::isinf(sum) || boost::math::isnan(sum) ) - { - throw std::runtime_error("The workspace contains values that cannot be displayed (infinite or NaN).\n" - "Please run ReplaceSpecialValues algorithm for correction."); - } - //integrated_values[i] = sum; - if( sum < m_DataMinValue ) - { - m_DataMinValue = sum; - } - if( sum > m_DataMaxValue ) - { - m_DataMaxValue = sum; - } - if (sum > 0 && sum < m_WkspDataPositiveMin) - { - m_WkspDataPositiveMin = sum; - } - } - - // No preset value - if(binEntireRange) - { - m_WkspDataMin = m_DataMinValue; - m_WkspDataMax = m_DataMaxValue; - } - if (m_autoscaling) - { - m_DataMinScaleValue = m_DataMinValue; - m_DataMaxScaleValue = m_DataMaxValue; - } + setDataIntegrationRange(xmin, xmax); resetColors(); } @@ -509,7 +484,7 @@ void InstrumentActor::resetColors() QwtDoubleInterval qwtInterval(m_DataMinScaleValue,m_DataMaxScaleValue); m_colors.resize(m_specIntegrs.size()); - auto shared_workspace = getWorkspace(); + auto sharedWorkspace = getWorkspace(); Instrument_const_sptr inst = getInstrument(); IMaskWorkspace_sptr mask = getMaskWorkspaceIfExists(); @@ -522,7 +497,7 @@ void InstrumentActor::resetColors() try { // Find if the detector is masked - const std::set& dets = shared_workspace->getSpectrum(wi)->getDetectorIDs(); + const std::set& dets = sharedWorkspace->getSpectrum(wi)->getDetectorIDs(); bool masked = false; if ( mask ) @@ -558,7 +533,7 @@ void InstrumentActor::resetColors() emit colorMapChanged(); } -void InstrumentActor::update() +void InstrumentActor::updateColors() { setIntegrationRange(m_BinMinValue,m_BinMaxValue); resetColors(); @@ -597,7 +572,7 @@ void InstrumentActor::draw(bool picking)const void InstrumentActor::loadColorMap(const QString& fname,bool reset_colors) { m_colorMap.loadMap(fname); - m_currentColorMap = fname; + m_currentColorMapFilename = fname; if (reset_colors) { resetColors(); @@ -661,15 +636,10 @@ void InstrumentActor::loadSettings() { QSettings settings; settings.beginGroup("Mantid/InstrumentWindow"); - int scaleType = settings.value("ScaleType", 0 ).toInt(); + m_scaleType = static_cast( settings.value("ScaleType", 0 ).toInt() ); //Load Colormap. If the file is invalid the default stored colour map is used - m_currentColorMap = settings.value("ColormapFile", "").toString(); + m_currentColorMapFilename = settings.value("ColormapFile", "").toString(); // Set values from settings - if (!m_currentColorMap.isEmpty()) - { - loadColorMap(m_currentColorMap,false); - } - m_colorMap.changeScaleType(static_cast(scaleType)); m_showGuides = settings.value("ShowGuides", false).toBool(); settings.endGroup(); } @@ -678,7 +648,7 @@ void InstrumentActor::saveSettings() { QSettings settings; settings.beginGroup("Mantid/InstrumentWindow"); - settings.setValue("ColormapFile", m_currentColorMap); + settings.setValue("ColormapFile", m_currentColorMapFilename); settings.setValue("ScaleType", (int)m_colorMap.getScaleType() ); settings.setValue("ShowGuides", m_showGuides); settings.endGroup(); @@ -707,19 +677,13 @@ void InstrumentActor::setMaxValue(double vmax) void InstrumentActor::setMinMaxRange(double vmin, double vmax) { if (m_autoscaling) return; - if (vmin < m_DataMinValue) - { - vmin = m_DataMinValue; - } - if (vmin >= vmax) return; - m_DataMinScaleValue = vmin; - m_DataMaxScaleValue = vmax; + setDataMinMaxRange(vmin,vmax); resetColors(); } bool InstrumentActor::wholeRange()const { - return m_BinMinValue == m_WkspBinMin && m_BinMaxValue == m_WkspBinMax; + return m_BinMinValue == m_WkspBinMinValue && m_BinMaxValue == m_WkspBinMaxValue; } /** @@ -994,6 +958,64 @@ void InstrumentActor::getBinMinMaxIndex( size_t wi, size_t& imin, size_t& imax ) } } +/** + * Set the minimum and the maximum data values on the color map scale. + */ +void InstrumentActor::setDataMinMaxRange(double vmin, double vmax) +{ + if (vmin < m_DataMinValue) + { + vmin = m_DataMinValue; + } + if (vmin >= vmax) return; + m_DataMinScaleValue = vmin; + m_DataMaxScaleValue = vmax; +} + +void InstrumentActor::setDataIntegrationRange(const double& xmin,const double& xmax) +{ + if (!getWorkspace()) return; + + m_BinMinValue = xmin; + m_BinMaxValue = xmax; + + //Use the workspace function to get the integrated spectra + getWorkspace()->getIntegratedSpectra(m_specIntegrs, m_BinMinValue, m_BinMaxValue, wholeRange()); + + m_DataMinValue = DBL_MAX; + m_DataMaxValue = -DBL_MAX; + + //Now we need to convert to a vector where each entry is the sum for the detector ID at that spot (in integrated_values). + for (size_t i=0; i < m_specIntegrs.size(); i++) + { + double sum = m_specIntegrs[i]; + if( boost::math::isinf(sum) || boost::math::isnan(sum) ) + { + throw std::runtime_error("The workspace contains values that cannot be displayed (infinite or NaN).\n" + "Please run ReplaceSpecialValues algorithm for correction."); + } + //integrated_values[i] = sum; + if( sum < m_DataMinValue ) + { + m_DataMinValue = sum; + } + if( sum > m_DataMaxValue ) + { + m_DataMaxValue = sum; + } + if (sum > 0 && sum < m_DataPositiveMinValue) + { + m_DataPositiveMinValue = sum; + } + } + + if (m_autoscaling) + { + m_DataMinScaleValue = m_DataMinValue; + m_DataMaxScaleValue = m_DataMaxValue; + } +} + //-------------------------------------------------------------------------// bool SetVisibleComponentVisitor::visit(GLActor* actor) { diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentActor.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentActor.h index 8beb8dfe257c..16f560050a57 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentActor.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentActor.h @@ -84,7 +84,7 @@ class InstrumentActor: public GLActor /// Change the colormap scale type. void changeScaleType(int); /// Get the file name of the current color map. - QString getCurrentColorMap()const{return m_currentColorMap;} + QString getCurrentColorMap()const{return m_currentColorMapFilename;} /// Toggle colormap scale autoscaling. void setAutoscaling(bool); /// Get colormap scale autoscaling status. @@ -103,7 +103,7 @@ class InstrumentActor: public GLActor /// Set both the minimum and the maximum data values on the color map scale. void setMinMaxRange(double vmin, double vmax); /// Get the smallest positive data value in the data. Used by the log20 scale. - double minPositiveValue()const{return m_WkspDataPositiveMin;} + double minPositiveValue()const{return m_DataPositiveMinValue;} /// Get the lower bound of the integration range. double minBinValue()const{return m_BinMinValue;} /// Get the upper bound of the integration range. @@ -135,7 +135,7 @@ class InstrumentActor: public GLActor void getBinMinMaxIndex(size_t wi,size_t& imin, size_t& imax) const; /// Update the detector colors to match the integrated counts within the current integration range. - void update(); + void updateColors(); /// Invalidate the OpenGL display lists to force full re-drawing of the instrument and creation of new lists. void invalidateDisplayLists()const{m_scene.invalidateDisplayList();} /// Toggle display of the guide and other non-detector instrument components @@ -161,11 +161,15 @@ class InstrumentActor: public GLActor bool hasMaskWorkspace() const; signals: void colorMapChanged(); -protected: +private: + + void setUpWorkspace(boost::shared_ptr sharedWorkspace, double scaleMin, double scaleMax); void resetColors(); void loadSettings(); void saveSettings(); + void setDataMinMaxRange(double vmin, double vmax); + void setDataIntegrationRange(const double& xmin,const double& xmax); size_t push_back_detid(Mantid::detid_t)const; boost::shared_ptr getMaskWorkspaceIfExists() const; @@ -176,19 +180,21 @@ class InstrumentActor: public GLActor mutable boost::shared_ptr m_maskWorkspace; /// The colormap MantidColorMap m_colorMap; + QString m_currentColorMapFilename; /// integrated spectra std::vector m_specIntegrs; /// The workspace data and bin range limits - double m_WkspDataMin, m_WkspDataMax,m_WkspDataPositiveMin; ///< min and max over whole workspace - double m_WkspBinMin, m_WkspBinMax; ///< min and max over whole workspace + double m_WkspBinMinValue, m_WkspBinMaxValue; ///< x-values min and max over whole workspace /// The user requested data and bin ranges - double m_DataMinValue, m_DataMaxValue; ///< min and max for current bin (x integration) range - double m_DataMinScaleValue, m_DataMaxScaleValue; /// min and max of the color map scale - double m_BinMinValue, m_BinMaxValue; + double m_DataMinValue, m_DataMaxValue, m_DataPositiveMinValue; ///< y-values min and max for current bin (x integration) range + double m_DataMinScaleValue, m_DataMaxScaleValue; ///< min and max of the color map scale + double m_BinMinValue, m_BinMaxValue; ///< x integration range /// Flag to rescale the colormap axis automatically when the data or integration range change bool m_autoscaling; /// Flag to show the guide and other components. Loaded and saved in settings. bool m_showGuides; + /// Color map scale type: linear or log + GraphOptions::ScaleType m_scaleType; /// The workspace's detector ID to workspace index map Mantid::detid2index_map m_detid2index_map; @@ -201,7 +207,6 @@ class InstrumentActor: public GLActor /// Colors in order of workspace indexes mutable std::vector m_colors; - QString m_currentColorMap; /// Colour of a masked detector GLColor m_maskedColor; /// Colour of a "failed" detector diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.cpp index 18280a90c58d..83b9d206a040 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.cpp @@ -55,6 +55,9 @@ using namespace Mantid::API; using namespace Mantid::Geometry; using namespace MantidQt::API; +// Name of the QSettings group to store the InstrumentWindw settings +const char* InstrumentWindowSettingsGroup = "Mantid/InstrumentWindow"; + /** * Constructor. */ @@ -797,30 +800,26 @@ void InstrumentWindow::afterReplaceHandle(const std::string& wsName, //Replace current workspace if (wsName == m_workspaceName.toStdString()) { - bool resetGeometry = true; - bool autoscaling = true; - double scaleMin = 0.0; - double scaleMax = 0.0; if (m_instrumentActor) { // try to detect if the instrument changes with the workspace auto matrixWS = boost::dynamic_pointer_cast( workspace ); - resetGeometry = matrixWS->getInstrument()->getNumberDetectors() != m_instrumentActor->ndetectors(); + bool resetGeometry = matrixWS->getInstrument()->getNumberDetectors() != m_instrumentActor->ndetectors(); // if instrument doesn't change keep the scaling if ( !resetGeometry ) { - autoscaling = m_instrumentActor->autoscaling(); - scaleMin = m_instrumentActor->minValue(); - scaleMax = m_instrumentActor->maxValue(); + m_instrumentActor->updateColors(); + } + else + { + delete m_instrumentActor; + m_instrumentActor = NULL; + init( resetGeometry, true, 0.0, 0.0, false ); + updateInstrumentDetectors(); } - - delete m_instrumentActor; - m_instrumentActor = NULL; } - init( resetGeometry, autoscaling, scaleMin, scaleMax, false ); - updateInstrumentDetectors(); } } @@ -1344,3 +1343,20 @@ void InstrumentWindow::createTabs(QSettings& settings) m_tabs << m_renderTab << pickTab << maskTab << treeTab; } + +/** + * Return a name for a group in QSettings to store InstrumentWindow configuration. + */ +QString InstrumentWindow::getSettingsGroupName() const +{ + return QString::fromAscii( InstrumentWindowSettingsGroup ); +} + +/** + * Construct a name for a group in QSettings to store instrument-specific configuration. + */ +QString InstrumentWindow::getInstrumentSettingsGroupName() const +{ + return QString::fromAscii( InstrumentWindowSettingsGroup ) + "/" + + QString::fromStdString( getInstrumentActor()->getInstrument()->getName() ); +} diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.h index 87f8a823af98..a88d305b0031 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.h @@ -91,7 +91,7 @@ class InstrumentWindow : public MdiSubWindow, public MantidQt::API::WorkspaceObs void setViewType(const QString& type); /// for saving the instrument window to mantid project QString saveToString(const QString& geometry, bool saveAsTemplate= false); - InstrumentActor* getInstrumentActor(){return m_instrumentActor;} + InstrumentActor* getInstrumentActor() const {return m_instrumentActor;} bool blocked()const{return m_blocked;} void selectTab(int tab); void selectTab(Tab tab){selectTab(int(tab));} @@ -99,6 +99,10 @@ class InstrumentWindow : public MdiSubWindow, public MantidQt::API::WorkspaceObs InstrumentWindowTab *getTab(const Tab tab) const; /// Get a filename for saving QString getSaveFileName(const QString& title, const QString& filters, QString* selectedFilter = NULL); + /// Get a name for settings group + QString getSettingsGroupName() const; + /// Get a name for a instrument-specific settings group + QString getInstrumentSettingsGroupName() const; signals: void enableLighting(bool); diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowMaskTab.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowMaskTab.cpp index 8fb02a7e5050..c989f75a4c42 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowMaskTab.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowMaskTab.cpp @@ -1048,7 +1048,7 @@ void InstrumentWindowMaskTab::storeMask() catch(...){} } // update detector colours - m_instrWindow->getInstrumentActor()->update(); + m_instrWindow->getInstrumentActor()->updateColors(); m_instrWindow->updateInstrumentDetectors(); } } diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.cpp index 064017a6a57d..4d12d1715e28 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.cpp @@ -124,10 +124,16 @@ m_freezePlot(false) m_unitsMapper->setMapping(m_phiUnits,PHI); connect(m_phiUnits,SIGNAL(triggered()),m_unitsMapper,SLOT(map())); + m_outOfPlaneAngleUnits = new QAction("Out of plane angle",this); + m_outOfPlaneAngleUnits->setCheckable(true); + m_unitsMapper->setMapping(m_outOfPlaneAngleUnits,OUT_OF_PLANE_ANGLE); + connect(m_outOfPlaneAngleUnits,SIGNAL(triggered()),m_unitsMapper,SLOT(map())); + m_unitsGroup = new QActionGroup(this); m_unitsGroup->addAction(m_detidUnits); m_unitsGroup->addAction(m_lengthUnits); m_unitsGroup->addAction(m_phiUnits); // re #4169 disabled until fixed or removed + m_unitsGroup->addAction(m_outOfPlaneAngleUnits); connect(m_unitsMapper,SIGNAL(mapped(int)),this,SLOT(setTubeXUnits(int))); // Instrument display context menu actions @@ -351,6 +357,7 @@ void InstrumentWindowPickTab::updateSelectionInfo(int detid) case DETECTOR_ID: xUnits = "Detector ID"; break; case LENGTH: xUnits = "Length"; break; case PHI: xUnits = "Phi"; break; + case OUT_OF_PLANE_ANGLE: xUnits = "Out of plane angle"; break; default: xUnits = "Detector ID"; } } @@ -397,6 +404,7 @@ void InstrumentWindowPickTab::plotContextMenu() else { m_sumDetectors->setChecked(m_plotSum); + m_integrateTimeBins->setChecked(!m_plotSum); m_integrateTimeBins->setEnabled(true); } context.addSeparator(); @@ -445,6 +453,7 @@ void InstrumentWindowPickTab::plotContextMenu() case DETECTOR_ID: m_detidUnits->setChecked(true); break; case LENGTH: m_lengthUnits->setChecked(true); break; case PHI: m_phiUnits->setChecked(true); break; + case OUT_OF_PLANE_ANGLE: m_outOfPlaneAngleUnits->setChecked(true); break; default: m_detidUnits->setChecked(true); } } @@ -957,6 +966,7 @@ void InstrumentWindowPickTab::prepareDataForSumsPlot( * DETECTOR_ID * LENGTH * PHI + * OUT_OF_PLANE_ANGLE * The units can be set with setTubeXUnits(...) method. * @param detid :: A detector id. The miniplot will display data for a component containing the detector * with this id. @@ -990,6 +1000,8 @@ void InstrumentWindowPickTab::prepareDataForIntegralsPlot( size_t imin,imax; instrActor->getBinMinMaxIndex(wi,imin,imax); + Mantid::Kernel::V3D samplePos = instrActor->getInstrument()->getSample()->getPos(); + const int n = ass->nelements(); if (n == 0) { @@ -1000,6 +1012,8 @@ void InstrumentWindowPickTab::prepareDataForIntegralsPlot( std::map xymap,errmap; // get the first detector in the tube for lenth calculation Mantid::Geometry::IDetector_sptr idet0 = boost::dynamic_pointer_cast((*ass)[0]); + Mantid::Kernel::V3D normal = (*ass)[1]->getPos() - idet0->getPos(); + normal.normalize(); for(int i = 0; i < n; ++i) { Mantid::Geometry::IDetector_sptr idet = boost::dynamic_pointer_cast((*ass)[i]); @@ -1007,14 +1021,22 @@ void InstrumentWindowPickTab::prepareDataForIntegralsPlot( { try { const int id = idet->getID(); + // get the x-value for detector idet double xvalue = 0; switch(m_tubeXUnits) { case LENGTH: xvalue = idet->getDistance(*idet0); break; case PHI: xvalue = bOffsetPsi ? idet->getPhiOffset(M_PI) : idet->getPhi(); break; + case OUT_OF_PLANE_ANGLE: + { + Mantid::Kernel::V3D pos = idet->getPos(); + xvalue = getOutOfPlaneAngle(pos, samplePos, normal); + break; + } default: xvalue = static_cast(id); } size_t index = instrActor->getWorkspaceIndex(id); + // get the y-value for detector idet const Mantid::MantidVec& Y = ws->readY(index); double sum = std::accumulate(Y.begin() + imin,Y.begin() + imax,0); xymap[xvalue] = sum; @@ -1066,24 +1088,6 @@ void InstrumentWindowPickTab::prepareDataForIntegralsPlot( } } -/** - * Return value of TubeXUnits for its symbolic name. - * @param name :: Symbolic name of the units, caseless: Detector_ID, Length, Phi - */ -InstrumentWindowPickTab::TubeXUnits InstrumentWindowPickTab::getTubeXUnits(const QString& name) const -{ - QString caseless_name = name.toUpper(); - if (caseless_name == "LENGTH") - { - return LENGTH; - } - if (caseless_name == "PHI") - { - return PHI; - } - return DETECTOR_ID; -} - /** * Return symbolic name of a TubeXUnit. * @param unit :: One of TubeXUnits. @@ -1095,6 +1099,7 @@ QString InstrumentWindowPickTab::getTubeXUnitsName(InstrumentWindowPickTab::Tube { case LENGTH: return "Length"; case PHI: return "Phi"; + case OUT_OF_PLANE_ANGLE: return "Out of plane angle"; default: return "Detector_ID"; } return "Detector_ID"; @@ -1300,6 +1305,7 @@ void InstrumentWindowPickTab::initSurface() void InstrumentWindowPickTab::saveSettings(QSettings &settings) const { settings.setValue("TubeXUnits",getTubeXUnits()); + settings.setValue("PlotSum", m_plotSum); } /** @@ -1309,6 +1315,8 @@ void InstrumentWindowPickTab::loadSettings(const QSettings &settings) { int unitsNum = settings.value("TubeXUnits",0).toInt(); m_tubeXUnits = TubeXUnits( unitsNum ); + m_plotSum = settings.value("PlotSum",true).toBool(); + setPlotCaption(); } /** @@ -1420,3 +1428,17 @@ void InstrumentWindowPickTab::updatePlotMultipleDetectors() m_plot->replot(); } +/** + * Calculate the angle between a vector ( == pos - origin ) and a plane ( orthogonal to normal ). + * The angle is positive if the vector and the normal make an acute angle. + * @param pos :: Vector's end. + * @param origin :: Vector's origin. + * @param normal :: Normal to the plane. + * @return :: Angle between the vector and the plane in radians in [-pi/2, pi/2]. + */ +double InstrumentWindowPickTab::getOutOfPlaneAngle(const Mantid::Kernel::V3D& pos, const Mantid::Kernel::V3D& origin, const Mantid::Kernel::V3D& normal) +{ + Mantid::Kernel::V3D vec = pos - origin; + vec.normalize(); + return asin(vec.scalar_prod(normal)); +} diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.h index 14f78c7b215c..82676001f9de 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowPickTab.h @@ -44,7 +44,7 @@ class InstrumentWindowPickTab: public InstrumentWindowTab /// markers. Selected peaks can be deleted by pressing the Delete key. enum SelectionType {Single=0,AddPeak,ErasePeak,SingleDetectorSelection,Tube, Draw}; enum ToolType {Zoom,PixelSelect,TubeSelect,PeakSelect,PeakErase, DrawEllipse, DrawRectangle, EditShape}; - enum TubeXUnits {DETECTOR_ID = 0,LENGTH,PHI,NUMBER_OF_UNITS}; + enum TubeXUnits {DETECTOR_ID = 0,LENGTH,PHI,OUT_OF_PLANE_ANGLE,NUMBER_OF_UNITS}; InstrumentWindowPickTab(InstrumentWindow* instrWindow); void updatePick(int detid); @@ -98,10 +98,10 @@ private slots: std::vector&x, std::vector&y, std::vector* err = NULL); - TubeXUnits getTubeXUnits(const QString& name) const; QString getTubeXUnitsName(TubeXUnits unit) const; QString getNonDetectorInfo(); QColor getShapeBorderColor() const; + static double getOutOfPlaneAngle(const Mantid::Kernel::V3D& pos, const Mantid::Kernel::V3D& origin, const Mantid::Kernel::V3D& normal); /* Pick tab controls */ OneCurvePlot* m_plot; ///< Miniplot to display data in the detectors @@ -126,7 +126,7 @@ private slots: QAction *m_linearY; QActionGroup *m_yScale; QActionGroup* m_unitsGroup; - QAction *m_detidUnits,*m_lengthUnits,*m_phiUnits; + QAction *m_detidUnits,*m_lengthUnits,*m_phiUnits,*m_outOfPlaneAngleUnits; QSignalMapper *m_unitsMapper; // Instrument display context menu actions diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowRenderTab.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowRenderTab.cpp index 2b6fbb146fa5..abc6be7f82f6 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowRenderTab.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowRenderTab.cpp @@ -2,6 +2,8 @@ #include "ProjectionSurface.h" #include "UnwrappedSurface.h" #include "Projection3D.h" +#include "RotationSurface.h" +#include "UCorrectionDialog.h" #include #include @@ -27,6 +29,12 @@ #include "BinDialog.h" #include "ColorMapWidget.h" +#include + +// QSettings entry names +const char *EntryManualUCorrection = "ManualUCorrection"; +const char *EntryUCorrectionMin = "UCorrectionMin"; +const char *EntryUCorrectionMax = "UCorrectionMax"; InstrumentWindowRenderTab::InstrumentWindowRenderTab(InstrumentWindow* instrWindow): InstrumentWindowTab(instrWindow) @@ -128,6 +136,9 @@ InstrumentWindowTab(instrWindow) m_wireframe->setCheckable(true); m_wireframe->setChecked(false); connect(m_wireframe, SIGNAL(toggled(bool)), m_instrWindow, SLOT(setWireframe(bool))); + m_UCorrection = new QAction("U Correction",this); + m_UCorrection->setToolTip("Manually set the limits on the horizontal axis."); + connect(m_UCorrection, SIGNAL(triggered()),this,SLOT(setUCorrection())); // Create "Use OpenGL" action m_GLView = new QAction("Use OpenGL",this); @@ -147,6 +158,8 @@ InstrumentWindowTab(instrWindow) displaySettingsMenu->addAction(m_wireframe); displaySettingsMenu->addAction(m_lighting); displaySettingsMenu->addAction(m_GLView); + displaySettingsMenu->addAction(m_UCorrection); + displaySettings->setMenu(displaySettingsMenu); connect(displaySettingsMenu,SIGNAL(hovered(QAction*)),this,SLOT(showMenuToolTip(QAction*))); @@ -251,20 +264,48 @@ void InstrumentWindowRenderTab::enable3DSurface(bool on) } } +/** + * Surface-specific adjustments. + */ void InstrumentWindowRenderTab::initSurface() { setAxis(QString::fromStdString(m_instrWindow->getInstrumentActor()->getInstrument()->getDefaultAxis())); auto surface = getSurface(); + + // 3D axes switch needs to be shown for the 3D surface auto p3d = boost::dynamic_pointer_cast(surface); if ( p3d ) { p3d->set3DAxesState(areAxesOn()); } + bool detectorsOnly = !m_instrWindow->getInstrumentActor()->areGuidesShown(); m_displayDetectorsOnly->blockSignals(true); m_displayDetectorsOnly->setChecked(detectorsOnly); m_displayDetectorsOnly->blockSignals(false); setPrecisionMenuItemChecked(surface->getPeakLabelPrecision()); + + // enable u-correction for surfaces of rotation. correction applied in the last + // session is loaded and re-applied in the new session + auto rotSurface = boost::dynamic_pointer_cast(surface); + if ( rotSurface ) + { + m_UCorrection->setEnabled(true); + QString groupName = m_instrWindow->getInstrumentSettingsGroupName(); + QSettings settings; + settings.beginGroup( groupName ); + bool isManualUCorrection = settings.value(EntryManualUCorrection,false).asBool(); + if ( isManualUCorrection ) + { + double ucorrMin = settings.value(EntryUCorrectionMin,0.0).asDouble(); + double ucorrMax = settings.value(EntryUCorrectionMax,0.0).asDouble(); + rotSurface->setUCorrection( ucorrMin, ucorrMax ); + } + } + else + { + m_UCorrection->setEnabled(false); + } } /** @@ -491,7 +532,7 @@ void InstrumentWindowRenderTab::setColorMapAutoscaling(bool on) QMenu* InstrumentWindowRenderTab::createPeaksMenu() { QSettings settings; - settings.beginGroup("Mantid/InstrumentWindow"); + settings.beginGroup( m_instrWindow->getSettingsGroupName() ); QMenu* menu = new QMenu(this); // show/hide peak hkl labels @@ -573,7 +614,7 @@ void InstrumentWindowRenderTab::setSurfaceType(int index) /** * Respond to surface change from script. - * @param typeIndex :: Index selected in the surface type combo box. + * @param index :: Index selected in the surface type combo box. */ void InstrumentWindowRenderTab::surfaceTypeChanged(int index) { @@ -631,3 +672,59 @@ void InstrumentWindowRenderTab::showMenuToolTip(QAction *action) QToolTip::showText(QCursor::pos(),action->toolTip(),this); } +/** + * Set the offset in u-coordinate of a 2d (unwrapped) surface + */ +void InstrumentWindowRenderTab::setUCorrection() +{ + auto surface = getSurface(); + auto rotSurface = boost::dynamic_pointer_cast(surface); + if ( rotSurface ) + { + QPointF oldUCorr = rotSurface->getUCorrection(); + // ask the user to enter a number for the u-correction + UCorrectionDialog dlg(this, oldUCorr, rotSurface->isManualUCorrection()); + if ( dlg.exec() != QDialog::Accepted ) return; + + QSettings settings; + settings.beginGroup( m_instrWindow->getInstrumentSettingsGroupName() ); + + if ( dlg.applyCorrection() ) + { + QPointF ucorr = dlg.getValue(); + // update the surface only if the correction changes + if ( ucorr != oldUCorr ) + { + rotSurface->setUCorrection( ucorr.x(), ucorr.y() ); // manually set the correction + rotSurface->requestRedraw(); // redraw the view + settings.setValue(EntryManualUCorrection,true); + settings.setValue(EntryUCorrectionMin,ucorr.x()); + settings.setValue(EntryUCorrectionMax,ucorr.y()); + } + } + else + { + rotSurface->setAutomaticUCorrection(); // switch to automatic correction + rotSurface->requestRedraw(); // redraw the view + settings.removeEntry(EntryManualUCorrection); + settings.removeEntry(EntryUCorrectionMin); + settings.removeEntry(EntryUCorrectionMax); + } + } +} + +/** + * Get current value for the u-correction for a RotationSurface. + * Return 0 if it's not a RotationSurface. + */ +QPointF InstrumentWindowRenderTab::getUCorrection() const +{ + auto surface = getSurface(); + auto rotSurface = boost::dynamic_pointer_cast(surface); + if ( rotSurface ) + { + return rotSurface->getUCorrection(); + } + return QPointF(); +} + diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowRenderTab.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowRenderTab.h index 86a915619f10..039dfb254b5f 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowRenderTab.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowRenderTab.h @@ -16,6 +16,7 @@ class QCheckBox; class QAction; class QActionGroup; class QMenu; +class QLineEdit; /** * Implements the Render tab in InstrumentWindow. @@ -66,6 +67,7 @@ private slots: void scaleTypeChanged(int); void glOptionChanged(bool); void showMenuToolTip(QAction*); + void setUCorrection(); private: void showEvent (QShowEvent *); @@ -73,6 +75,7 @@ private slots: QFrame * setupAxisFrame(); void setPrecisionMenuItemChecked(int n); void enable3DSurface( bool on ); + QPointF getUCorrection() const; QPushButton *m_surfaceTypeButton; QPushButton *mSaveImage; @@ -100,6 +103,7 @@ private slots: QAction *m_wireframe; QAction *m_lighting; QAction *m_GLView; ///< toggle between OpenGL and simple view + QAction *m_UCorrection; QActionGroup *m_precisionActionGroup; QList m_precisionActions; diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PanelsSurface.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PanelsSurface.cpp index ef673d997365..448538a17eb9 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PanelsSurface.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/PanelsSurface.cpp @@ -278,7 +278,7 @@ void PanelsSurface::addFlatBank(ComponentID bankId, const Mantid::Kernel::V3D &n * Add a flat bank from an assembly of detectors. * @param bankId :: Component ID of the bank. * @param normal :: Normal vector to the bank's plane. - * @param objCompAssemblies :: List of component IDs. Each component must cast to Detector. + * @param detectors :: List of component IDs. Each component must cast to Detector. */ void PanelsSurface::addFlatBankOfDetectors(ComponentID bankId, const Mantid::Kernel::V3D &normal, QList detectors) { diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/ProjectionSurface.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/ProjectionSurface.cpp index 5c60098a301f..920b92baaeb7 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/ProjectionSurface.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/ProjectionSurface.cpp @@ -25,9 +25,7 @@ using Mantid::Kernel::V3D; /** * The constructor. - * @param rootActor :: The instrument actor containning all info about the instrument - * @param origin :: Defines the origin of the projection reference system (if applicable) - * @param axis :: + * @param rootActor :: The instrument actor containing all info about the instrument */ ProjectionSurface::ProjectionSurface(const InstrumentActor* rootActor): m_instrActor(rootActor), @@ -412,6 +410,7 @@ void ProjectionSurface::colorMapChanged() { this->changeColorMap(); updateView(false); + requestRedraw(); } /** diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RectangularDetectorActor.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RectangularDetectorActor.cpp index 1d408bd17c16..c786ce926686 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RectangularDetectorActor.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RectangularDetectorActor.cpp @@ -131,6 +131,7 @@ void RectangularDetectorActor::draw(bool picking)const * detector, and sets the visibility of the whole panel to true if so. * * @param visitor :: A visitor. + * @param rule :: A rule defining visitor acceptance by assembly actors. Unused. * */ bool RectangularDetectorActor::accept(GLActorVisitor& visitor, VisitorAcceptRule) diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RotationSurface.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RotationSurface.cpp index de03e72b60e9..469a67122176 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RotationSurface.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RotationSurface.cpp @@ -10,7 +10,7 @@ RotationSurface::RotationSurface(const InstrumentActor* rootActor,const Mantid:: UnwrappedSurface(rootActor), m_pos(origin), m_zaxis(axis), - m_u_correction(0) + m_manual_u_correction(false) { } @@ -22,7 +22,11 @@ void RotationSurface::init() // the actor calls this->callback for each detector m_unwrappedDetectors.clear(); m_assemblies.clear(); - m_u_correction = 0.0; + + // if u-correction is applied manually then m_u_min and m_u_max + // have valid values and have to be saved + double manual_u_min = m_u_min; + double manual_u_max = m_u_max; size_t ndet = m_instrActor->ndetectors(); m_unwrappedDetectors.resize(ndet); @@ -115,22 +119,27 @@ void RotationSurface::init() } // is a real detectord } // for each detector in pick order - // Now find the overall edges in U and V coords. - m_u_min = DBL_MAX; - m_u_max = -DBL_MAX; - m_v_min = DBL_MAX; - m_v_max = -DBL_MAX; - for(size_t i=0;i m_u_max) m_u_max = udet.u; - if (udet.v < m_v_min) m_v_min = udet.v; - if (udet.v > m_v_max) m_v_max = udet.v; + // automatic gap correction + findAndCorrectUGap(); + } + else + { + // apply manually set shift + m_u_min = manual_u_min; + m_u_max = manual_u_max; + for(size_t i=0;i m_u_max) m_u_max = udet.u; + if (udet.v < m_v_min) m_v_min = udet.v; + if (udet.v > m_v_max) m_v_max = udet.v; + } +} + void RotationSurface::findAndCorrectUGap() { double period = uPeriod(); @@ -215,24 +238,22 @@ void RotationSurface::findAndCorrectUGap() double uTo = m_u_min + iTo * bin_width; if (uTo - uFrom > period - (m_u_max - m_u_min)) { - double du = m_u_max - uTo; - m_u_max = uFrom + du; + + m_u_max = uFrom; + m_u_min = uTo; + if ( m_u_min > m_u_max ) + { + m_u_max += period; + } + std::vector::iterator ud = m_unwrappedDetectors.begin(); for(;ud != m_unwrappedDetectors.end(); ++ud) { if (! ud->detector ) continue; double& u = ud->u; - u += du; - if (u > m_u_max) - { - u -= period; - } - } - m_u_correction += du; - if (m_u_correction > m_u_max) - { - m_u_correction -= period; + u = applyUCorrection(u); } + } } @@ -246,15 +267,46 @@ double RotationSurface::applyUCorrection(double u)const { double period = uPeriod(); if (period == 0.0) return u; - u += m_u_correction; if (u < m_u_min) { - u += period; + double periods = floor( (m_u_max - u) / period ) * period; + u += periods; } if (u > m_u_max) { - u -= period; + double periods = floor( (u - m_u_min) / period ) * period; + u -= periods; } return u; } +/** + * Set new value for the u-correction. + * Correct all uv corrdinates of detectors. + */ +void RotationSurface::setUCorrection(double umin, double umax) +{ + m_u_min = umin; + m_u_max = umax; + double period = uPeriod(); + double du = m_u_max - m_u_min; + if ( du > period * 1.1 ) + { + m_u_max -= floor( du / period ) * period; + } + while ( m_u_min >= m_u_max ) + { + m_u_max += period; + } + m_manual_u_correction = true; + updateDetectors(); +} + +/** + * Set automatic u-correction + */ +void RotationSurface::setAutomaticUCorrection() +{ + m_manual_u_correction = false; + updateDetectors(); +} diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RotationSurface.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RotationSurface.h index b77748be4b32..30ab9ecd064d 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RotationSurface.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/RotationSurface.h @@ -12,12 +12,24 @@ class RotationSurface: public UnwrappedSurface public: RotationSurface(const InstrumentActor* rootActor,const Mantid::Kernel::V3D& origin,const Mantid::Kernel::V3D& axis); void init(); + // Get the value of the u-correction - a shift in the u-coord added to automatically determined uv coordinates + QPointF getUCorrection() const {return QPointF(m_u_min,m_u_max);} + // Set new value for the u-correction + void setUCorrection(double umin, double umax); + // Set automatic u-correction + void setAutomaticUCorrection(); + // Is u-correction applied manually? + bool isManualUCorrection() const {return m_manual_u_correction;} protected: /// Period in the u coordinate. 2pi by default. virtual double uPeriod() const {return 2 * M_PI;} + /// Given the u and v coords for all detectors find their min and max values + /// and set m_u_min, m_u_max, m_v_min, m_v_max + void findUVBounds(); + /// Automatic generation of the projection coordinates may leave a gap /// in u when the surface is unwrapped. This method tries to minimize /// this gap by shifting the origin of the u axis. @@ -31,8 +43,7 @@ class RotationSurface: public UnwrappedSurface const Mantid::Kernel::V3D m_zaxis; ///< The z axis of the surface specific coord system Mantid::Kernel::V3D m_xaxis; ///< The x axis Mantid::Kernel::V3D m_yaxis; ///< The y axis - double m_u_correction; ///< Correction to u calculated by project() after findAndCorrectUGap() - + bool m_manual_u_correction; ///< Flag set to prevent automatic FindAndCorrectUGap() }; #endif // ROTATIONSURFACE_H diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UCorrectionDialog.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UCorrectionDialog.cpp new file mode 100644 index 000000000000..3291dd2b7adb --- /dev/null +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UCorrectionDialog.cpp @@ -0,0 +1,39 @@ +#include "UCorrectionDialog.h" +#include "ui_UCorrectionDialog.h" + +UCorrectionDialog::UCorrectionDialog(QWidget *parent, QPointF oldValue, bool isManual) : + QDialog(parent), + ui(new Ui::UCorrectionDialog) +{ + ui->setupUi(this); + connect(ui->cbApply,SIGNAL(toggled(bool)),ui->dsbUMin,SLOT(setEnabled(bool))); + connect(ui->cbApply,SIGNAL(toggled(bool)),ui->dsbUMax,SLOT(setEnabled(bool))); + ui->cbApply->setChecked(isManual); + ui->dsbUMin->setValue(oldValue.x()); + ui->dsbUMax->setValue(oldValue.y()); + ui->dsbUMin->setEnabled(isManual); + ui->dsbUMax->setEnabled(isManual); +} + +UCorrectionDialog::~UCorrectionDialog() +{ + delete ui; +} + +/** + * If true the manual correction returned by getValue() method should + * be applied to the surface or use automatic correction if false. + */ +bool UCorrectionDialog::applyCorrection() const +{ + return ui->cbApply->isChecked(); +} + +/** + * Get the value of the manual u-correction. + */ +QPointF UCorrectionDialog::getValue() const +{ + return QPointF( ui->dsbUMin->value(), ui->dsbUMax->value() ); +} + diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UCorrectionDialog.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UCorrectionDialog.h new file mode 100644 index 000000000000..82634260f45b --- /dev/null +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UCorrectionDialog.h @@ -0,0 +1,24 @@ +#ifndef UCorrectionDIALOG_H +#define REMOVEERRORSDIALOG_H + +#include +#include + +namespace Ui { + class UCorrectionDialog; +} + +class UCorrectionDialog : public QDialog { + Q_OBJECT +public: + UCorrectionDialog(QWidget *parent, QPointF oldValue, bool isManual); + ~UCorrectionDialog(); + + bool applyCorrection() const; + QPointF getValue() const; + +private: + Ui::UCorrectionDialog *ui; +}; + +#endif // REMOVEERRORSDIALOG_H diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UCorrectionDialog.ui b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UCorrectionDialog.ui new file mode 100644 index 000000000000..6cf480733e19 --- /dev/null +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UCorrectionDialog.ui @@ -0,0 +1,154 @@ + + + UCorrectionDialog + + + + 0 + 0 + 360 + 133 + + + + MantidPlot - Instrument + + + + + + + + + 1 + 0 + + + + Minimum value on the horizontal axis. + + + 6 + + + -1000000000.000000000000000 + + + 1000000000.000000000000000 + + + 0.100000000000000 + + + + + + + Minimum value on the horizontal axis. + + + U Min + + + + + + + Set to enable manual correction of the horizontal axis limits. + + + Apply horizontal shift + + + + + + + + 1 + 0 + + + + Maximum value on the horizontal axis. + + + 6 + + + -1000000000.000000000000000 + + + 1000000000.000000000000000 + + + 0.100000000000000 + + + + + + + Maximum value on the horizontal axis. + + + U Max + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + cbApply + dsbUMin + dsbUMax + buttonBox + + + + + buttonBox + accepted() + UCorrectionDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + UCorrectionDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.cpp b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.cpp index c5a7b27f0c43..e3b692e969da 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/UnwrappedSurface.cpp @@ -729,9 +729,8 @@ void UnwrappedSurface::calcUV(UnwrappedDetector& udet, Mantid::Kernel::V3D & pos //------------------------------------------------------------------------------ /** Calculate the size of the detector in U/V * - * @param udet - * @param X - * @param Y + * @param udet :: UwrappedDetector struct to calculate the size for. udet's size fields + * are updated by this method. */ void UnwrappedSurface::calcSize(UnwrappedDetector& udet) { diff --git a/Code/Mantid/MantidPlot/src/Mantid/MantidDock.cpp b/Code/Mantid/MantidPlot/src/Mantid/MantidDock.cpp index c07505b05f31..d67b60c73506 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/MantidDock.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/MantidDock.cpp @@ -408,7 +408,7 @@ void MantidDockWidget::setTreeUpdating(const bool state) /** * Clears the tree and re-populates it with the given top level items * @param topLevelItems The map of names to workspaces - * @param expandedItems Names of items who should expanded after being populated + * @param expanded Names of items who should expanded after being populated */ void MantidDockWidget::populateTopLevel(const std::map & topLevelItems, const QStringList & expanded) diff --git a/Code/Mantid/MantidPlot/src/Mantid/MantidUI.cpp b/Code/Mantid/MantidPlot/src/Mantid/MantidUI.cpp index 8e2d85154a9f..ac25b7d57154 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/MantidUI.cpp +++ b/Code/Mantid/MantidPlot/src/Mantid/MantidUI.cpp @@ -2365,7 +2365,7 @@ void MantidUI::importString(const QString &logName, const QString &data) * @param logName :: the title of the table is based on this * @param data :: the string to display * @param sep :: the seperator character -* @param caption :: the caption to appear on the table window title bar, defualts to logname if left blank +* @param wsName :: add workspace name to the table window title bar, defaults to logname if left blank */ void MantidUI::importString(const QString &logName, const QString &data, const QString &sep, const QString &wsName) { @@ -2400,7 +2400,7 @@ void MantidUI::importString(const QString &logName, const QString &data, const Q /** Displays a string in a Qtiplot table * @param logName :: the title of the table is based on this * @param data :: a formated string with the time series data to display -* @param caption :: the caption to appear on the table window title bar, defualts to logname if left blank +* @param wsName :: add workspace name to the table window title bar, defaults to logname if left blank */ void MantidUI::importStrSeriesLog(const QString &logName, const QString &data, const QString &wsName) { @@ -2451,7 +2451,6 @@ void MantidUI::importStrSeriesLog(const QString &logName, const QString &data, c * - 1 filter by running status * - 2 filter by period * - 3 filter by status & period -* @param caption :: the caption to appear on the table window title bar, defualts to logname if left blank */ void MantidUI::importNumSeriesLog(const QString &wsName, const QString &logName, int filter) { @@ -3003,7 +3002,7 @@ void MantidUI::setUpBinGraph(MultiLayer* ml, const QString& Name, Mantid::API::M /** Plots the spectra from the given workspaces -@param ws_name :: List of ws names to plot +@param ws_names :: List of ws names to plot @param spec_list :: List of spectra indices to plot for each workspace @param errs :: If true include the errors on the graph @param style :: Curve style for plot diff --git a/Code/Mantid/MantidPlot/src/PythonScript.cpp b/Code/Mantid/MantidPlot/src/PythonScript.cpp index fefc79fa03a1..8badbfd034a9 100644 --- a/Code/Mantid/MantidPlot/src/PythonScript.cpp +++ b/Code/Mantid/MantidPlot/src/PythonScript.cpp @@ -63,6 +63,10 @@ namespace return retcode; } + /// Message to emit when everything worked out fine + static const QString MSG_FINISHED = "Script execution finished."; + /// Message to emit when starting + static const QString MSG_STARTED = "Script execution started."; } @@ -223,27 +227,69 @@ void PythonScript::generateAutoCompleteList() } /** - * Convert a python error state into a human-readable string - * - * @return A string containing the error message + * This emits the error signal and resets the error state + * of the python interpreter. */ -QString PythonScript::constructErrorMsg() +void PythonScript::emit_error() { + // gil is necessary so other things don't continue GlobalInterpreterLock gil; - QString message; - if (!PyErr_Occurred()) - { - return message; - } + + // return early if nothing happened + if (!PyErr_Occurred()) + return; + + // get the error information out PyObject *exception(NULL), *value(NULL), *traceback(NULL); PyErr_Fetch(&exception, &value, &traceback); + + // special check for system exceptions + if (bool(exception) + && PyErr_GivenExceptionMatches(exception, PyExc_SystemExit) + && PyObject_HasAttrString(exception, "code")) + { + // value is the return code handed to sys.exit + long code = 0; + if (bool(value) && PyInt_Check(value)) + { + code = PyInt_AsLong(value); + } + + // if we are returning 0 then cleanup and return + if (code == 0) + { + // maybe shouldn't clear the error, but for now this + // is the agreed upon behavior + PyErr_Clear(); + Py_XDECREF(traceback); + Py_XDECREF(exception); + Py_XDECREF(value); + emit finished(MSG_FINISHED); + return; + } + } + + // prework on the exception handling PyErr_NormalizeException(&exception, &value, &traceback); PyErr_Clear(); + + // convert the traceback into something useful + int lineNumber = 0; + QString filename; + if (traceback) + { + PyTracebackObject* tb = (PyTracebackObject*)traceback; + lineNumber = tb->tb_lineno; + filename = PyString_AsString(tb->tb_frame->f_code->co_filename); + } + + // the error message is the full (formated) traceback PyObject *str_repr = PyObject_Str(value); + QString message; QTextStream msgStream(&message); if( value && str_repr ) { - if(exception == PyExc_SyntaxError) + if(exception == PyExc_SyntaxError) { msgStream << constructSyntaxErrorStr(value); } @@ -253,7 +299,7 @@ QString PythonScript::constructErrorMsg() excTypeName = excTypeName.section(".", -1); msgStream << excTypeName << ": " << PyString_AsString(str_repr); } - + } else { @@ -265,7 +311,8 @@ QString PythonScript::constructErrorMsg() Py_XDECREF(traceback); Py_XDECREF(exception); Py_XDECREF(value); - return msgStream.readAll(); + + emit error(msgStream.readAll(), filename, lineNumber); } /** @@ -468,7 +515,7 @@ QVariant PythonScript::evaluateImpl() } else { - emit error(constructErrorMsg(), "", 0); + emit_error(); return QVariant(); } } @@ -538,7 +585,7 @@ QVariant PythonScript::evaluateImpl() } else { - emit error(constructErrorMsg(), "", 0); + emit_error(); } return QVariant(); } @@ -554,7 +601,7 @@ bool PythonScript::executeImpl() /// Performs the call to Python bool PythonScript::executeString() { - emit started("Script execution started."); + emit started(MSG_STARTED); bool success(false); GlobalInterpreterLock gil; @@ -567,13 +614,13 @@ bool PythonScript::executeString() // If an error has occurred we need to construct the error message // before any other python code is run QString msg; - if(!result) + if(!result) { - msg = constructErrorMsg(); // Also clears error indicator + emit_error(); } else { - msg = "Script execution finished."; + emit finished(MSG_FINISHED); success = true; } if(isInteractive()) @@ -583,8 +630,7 @@ bool PythonScript::executeString() Py_XDECREF(compiledCode); Py_XDECREF(result); - if(success) emit finished(msg); - else emit error(msg, "", 0); + return success; } diff --git a/Code/Mantid/MantidPlot/src/PythonScript.h b/Code/Mantid/MantidPlot/src/PythonScript.h index 5b427fa1084d..86d13235632c 100644 --- a/Code/Mantid/MantidPlot/src/PythonScript.h +++ b/Code/Mantid/MantidPlot/src/PythonScript.h @@ -79,8 +79,6 @@ class PythonScript : public Script, MantidQt::API::WorkspaceObserver /// Create a list of keywords for the code completion API void generateAutoCompleteList(); - /// Construct the error message from the stack trace (if one exists) - QString constructErrorMsg(); /// Special handle for syntax errors as they have no traceback QString constructSyntaxErrorStr(PyObject *syntaxError); /// Convert a traceback to a string @@ -185,6 +183,9 @@ class PythonScript : public Script, MantidQt::API::WorkspaceObserver /// Delete a Python reference to the given workspace name void deletePythonReference(const std::string& wsName); + /// Send out an error and clear it from python. + void emit_error(); + PythonScripting * m_pythonEnv; PyObject *localDict, *stdoutSave, *stderrSave; PyObject *m_CodeFileObject; diff --git a/Code/Mantid/MantidPlot/src/SendToProgramDialog.cpp b/Code/Mantid/MantidPlot/src/SendToProgramDialog.cpp index 5752e7869a3d..e6214c369f68 100644 --- a/Code/Mantid/MantidPlot/src/SendToProgramDialog.cpp +++ b/Code/Mantid/MantidPlot/src/SendToProgramDialog.cpp @@ -157,21 +157,9 @@ void SendToProgramDialog::validateTarget() */ void SendToProgramDialog::validateSaveUsing() { - validSaveUsing = true; - try - { - Mantid::API::AlgorithmManager::Instance().create(m_uiform.saveUsingText->text().toStdString() ); - } - catch(std::exception&) - { - m_uiform.validateSaveUsing->setVisible(true); - validSaveUsing = false; - } - - if (validSaveUsing == true) - { - m_uiform.validateSaveUsing->setVisible(false); - } + validSaveUsing = Mantid::API::AlgorithmFactory::Instance().exists( m_uiform.saveUsingText->text().toStdString() ); + m_uiform.validateSaveUsing->setVisible( ! validSaveUsing ); + validateAll(); } diff --git a/Code/Mantid/MantidQt/API/src/HelpWindow.cpp b/Code/Mantid/MantidQt/API/src/HelpWindow.cpp index c9f880a8fe55..6b4c336f3bde 100644 --- a/Code/Mantid/MantidQt/API/src/HelpWindow.cpp +++ b/Code/Mantid/MantidQt/API/src/HelpWindow.cpp @@ -27,7 +27,7 @@ const string WIKI_BASE_URL("http://mantidproject.org/"); const string WIKI_DEFAULT_URL(WIKI_BASE_URL + "MantidPlot"); /** - * Default constructor shows the \link MantidQt::API::DEFAULT_URL. + * Default constructor shows the @link MantidQt::API::DEFAULT_URL @endlink. */ HelpWindowImpl::HelpWindowImpl() : m_collectionFile(""), @@ -69,7 +69,7 @@ void HelpWindowImpl::openWebpage(const string &url) * Have the help window show a specific url. If the url doesn't exist * this just pops up the default view for the help. * - * \param url The url to open. This should start with \link MantidQt::API::BASE_URL. + * @param url The url to open. This should start with @link MantidQt::API::BASE_URL @endlink. * If it is empty show the default page. */ void HelpWindowImpl::showURL(const string &url) @@ -139,7 +139,7 @@ void HelpWindowImpl::showAlgorithm(const string &name, const int version) } /** - * Convenience method for \link HelpWindowImpl::showAlgorithm(string, int). + * Convenience method for HelpWindowImpl::showAlgorithm(const string &, const int). * * @param name The name of the algorithm to show. If this is empty show * the algorithm index. diff --git a/Code/Mantid/MantidQt/API/src/InterfaceFactory.cpp b/Code/Mantid/MantidQt/API/src/InterfaceFactory.cpp index e6034dad4d7f..c8c2747c97be 100644 --- a/Code/Mantid/MantidQt/API/src/InterfaceFactory.cpp +++ b/Code/Mantid/MantidQt/API/src/InterfaceFactory.cpp @@ -46,7 +46,7 @@ UserSubWindow * UserSubWindowFactoryImpl::createUnwrapped(const std::string & na /** * Return the set of categories that the interface with the given name belongs to. * - * @param name :: The name of the interface. + * @param interfaceName :: The name of the interface. * @returns the set of category names if an interface with the given name has been registered, * else an empty set. */ diff --git a/Code/Mantid/MantidQt/API/src/RepoModel.cpp b/Code/Mantid/MantidQt/API/src/RepoModel.cpp index c42cdc406faa..e6f73ef7e81c 100644 --- a/Code/Mantid/MantidQt/API/src/RepoModel.cpp +++ b/Code/Mantid/MantidQt/API/src/RepoModel.cpp @@ -5,6 +5,7 @@ #include #include "MantidKernel/ConfigService.h" +#include "MantidKernel/Logger.h" #include #include using namespace MantidQt::API; diff --git a/Code/Mantid/MantidQt/API/src/ScriptRepositoryView.cpp b/Code/Mantid/MantidQt/API/src/ScriptRepositoryView.cpp index a68dc0527435..d7a0e02caa1d 100644 --- a/Code/Mantid/MantidQt/API/src/ScriptRepositoryView.cpp +++ b/Code/Mantid/MantidQt/API/src/ScriptRepositoryView.cpp @@ -4,6 +4,7 @@ #include "MantidAPI/ScriptRepository.h" #include "MantidAPI/ScriptRepositoryFactory.h" #include "MantidKernel/ConfigService.h" +#include "MantidKernel/Logger.h" #include #include #include diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/MuonAnalysis.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/MuonAnalysis.h index 28d0c68c699a..eaeebf397563 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/MuonAnalysis.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/MuonAnalysis.h @@ -5,12 +5,16 @@ // Includes //---------------------- #include "ui_MuonAnalysis.h" -#include "MantidQtAPI/UserSubWindow.h" -#include "MantidQtMantidWidgets/pythonCalc.h" -#include "MantidQtMantidWidgets/MWDiag.h" #include "MantidAPI/AnalysisDataService.h" #include "MantidAPI/MatrixWorkspace.h" +#include "MantidAPI/ITableWorkspace.h" + +#include "MantidGeometry/Instrument.h" + +#include "MantidQtAPI/UserSubWindow.h" +#include "MantidQtMantidWidgets/pythonCalc.h" +#include "MantidQtMantidWidgets/MWDiag.h" #include @@ -28,6 +32,7 @@ namespace Muon using namespace Mantid::Kernel; using namespace Mantid::API; +using namespace Mantid::Geometry; /** This is the main class for the MuonAnalysis interface @@ -209,6 +214,8 @@ private slots: /// Saves the value of the widget which called the slot void saveWidgetValue(); + /// Opens a sequential fit dialog + void openSequentialFitDialog(); private: @@ -271,9 +278,6 @@ private slots: /// Selects a workspace from the group according to what is selected on the interface for the period MatrixWorkspace_sptr getPeriodWorkspace(PeriodType periodType, WorkspaceGroup_sptr group); - /// Apply whatever grouping is specified in GUI tables to workspace - bool applyGroupingToWS( const std::string& inputWS, const std::string& outputWS); - /// Update front void updateFront(); @@ -286,9 +290,6 @@ private slots: /// Calculate number of detectors from string of type 1-3, 5, 10-15 int numOfDetectors(const std::string& str) const; - /// Return a vector of IDs for row number from string of type 1-3, 5, 10-15 - std::vector spectrumIDs(const std::string& str) const; - void changeCurrentRun(std::string& workspaceGroupName); /// is string a number? @@ -321,6 +322,15 @@ private slots: /// Return number of groups defined (not including pairs) int numGroups(); + /// Returns custom dead time table file name as set on the interface + std::string deadTimeFilename(); + + /// Loads dead time table (group of tables) from the file. + Workspace_sptr loadDeadTimes(const std::string& filename); + + /// Creates and algorithm with all the properties set according to widget values on the interface + Algorithm_sptr createLoadAlgorithm(); + // TODO: wsIndex can be removed from functions below if we put only one group to the workspace // (as we are doing with pairs) @@ -349,9 +359,12 @@ private slots: /// The last directory that was viewed QString m_last_dir; - /// name of workspace + /// Name of the loaded workspace std::string m_workspace_name; + /// Name of the loaded AND grouped workspace + std::string m_grouped_name; + /// name of the loaded data QString m_currentDataName; @@ -408,15 +421,6 @@ private slots: /// time zero returned in ms double timeZero(); - /// set grouping in table from information from nexus raw file - void setGroupingFromNexus(const QString& nexusFile); - - /// - void setDummyGrouping(const int numDetectors); - - /// - void setGroupingFromIDF(const std::string& mainFieldDirection, MatrixWorkspace_sptr matrix_workspace); - /// title of run std::string m_title; @@ -465,6 +469,21 @@ private slots: /// Saves the value of the widget which called the slot void loadWidgetValue(QWidget* target, const QVariant& defaultValue); + // Groups loaded workspace (m_workspace_name) + void groupLoadedWorkspace(ITableWorkspace_sptr detGroupingTable = ITableWorkspace_sptr()); + + /// Parses grouping information from the UI table. + ITableWorkspace_sptr parseGrouping(); + + /// Updated UI table using the grouping information provided. + void setGrouping(ITableWorkspace_sptr detGroupingTable); + + /// Updates UI grouping table - creates dummy grouping + void setDummyGrouping(Instrument_const_sptr instrument); + + /// Updates UI grouping table using default grouping of the instrument + void setGroupingFromIDF(Instrument_const_sptr instrument, const std::string& mainFieldDirection); + /// handles option tab work MantidQt::CustomInterfaces::Muon::MuonAnalysisOptionTab* m_optionTab; /// handles fit data work diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ConvFit.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ConvFit.cpp index 120c7bfd87ec..89106c7fb6c2 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ConvFit.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ConvFit.cpp @@ -353,7 +353,7 @@ namespace IDA * +-- Lorentzian 1 (yes/no) * +-- Lorentzian 2 (yes/no) * - * @param tie :: whether to tie centres of the two lorentzians. + * @param tieCentres :: whether to tie centres of the two lorentzians. * * @returns the composite fitting function. */ diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/IDATab.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/IDATab.cpp index 709e56dbf80f..c98fc01c3668 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/IDATab.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/IDATab.cpp @@ -136,7 +136,7 @@ namespace IDA * @param plot :: the QwtPlot object * @param curve :: the QwtPlotCurve object * @param workspace :: A pointer to the workspace to use - * @param wsIndex :: the workspace index + * @param index :: the workspace index * * @returns the resulting QwtPlotCurve object */ diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect.cpp index fdce047de588..a0f224ed15cb 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect.cpp @@ -1667,6 +1667,13 @@ void Indirect::calPlotEnergy() "outWS = resolution(files, iconOpt, '', '', instrument, analyser, reflection, Res=False)\n" "print outWS\n"; QString pyOutput = runPythonCode(pyInput).trimmed(); + + //something went wrong in the python + if(pyOutput == "None") + { + showInformationBox("Failed to convert to energy. See log for details."); + return; + } Mantid::API::MatrixWorkspace_sptr input = boost::dynamic_pointer_cast(Mantid::API::AnalysisDataService::Instance().retrieve(pyOutput.toStdString())); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/IndirectBayesTab.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/IndirectBayesTab.cpp index 5252f62ad29b..73ae6711acd8 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/IndirectBayesTab.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/IndirectBayesTab.cpp @@ -78,7 +78,7 @@ namespace MantidQt * a specturm index. * * @param workspace :: Pointer to the workspace - * @param index :: The spectrum index of the workspace + * @param wsIndex :: The spectrum index of the workspace */ void IndirectBayesTab::plotMiniPlot(const Mantid::API::MatrixWorkspace_const_sptr & workspace, size_t wsIndex) { @@ -131,7 +131,7 @@ namespace MantidQt * Checks the workspace's intrument for a resolution parameter to use as * a default for the energy range on the mini plot * - * @param workspace :: Pointer to the workspace to use + * @param ws :: Pointer to the workspace to use * @param res :: The retrieved values for the resolution parameter (if one was found) */ bool IndirectBayesTab::getInstrumentResolution(Mantid::API::MatrixWorkspace_const_sptr ws, std::pair& res) @@ -158,7 +158,7 @@ namespace MantidQt /** * Gets the range of the curve plotted in the mini plot * - * @param A pair containing the maximum and minimum points of the curve + * @return A pair containing the maximum and minimum points of the curve */ std::pair IndirectBayesTab::getCurveRange() { @@ -245,7 +245,7 @@ namespace MantidQt * Checks if a file is present in the ADS and if not attempts to load it. * * @param filename :: name of the file that should be loaded - * @param errorMsg :: error message to display if the file couldn't be found. + * @param filepath :: path to file */ bool IndirectBayesTab::checkFileLoaded(const QString& filename, const QString& filepath) { diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/IndirectNeutron.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/IndirectNeutron.cpp index 2cc801381516..a47e86529357 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/IndirectNeutron.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/IndirectNeutron.cpp @@ -237,7 +237,7 @@ namespace MantidQt * Set the instrument selected in the combobox based on * the file name of the run is possible. * - * Assumes that names have the form _. + * Assumes that names have the form \_\.\ */ void IndirectNeutron::handleFilesFound() { diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/JumpFit.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/JumpFit.cpp index 6feee8811f08..0de9650ba867 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/JumpFit.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/JumpFit.cpp @@ -99,7 +99,7 @@ namespace MantidQt * Set the data selectors to use the default save directory * when browsing for input files. * - * @param filename :: The name of the workspace to plot + * @param settings :: The current settings */ void JumpFit::loadSettings(const QSettings& settings) { diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/MantidEVWorker.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/MantidEVWorker.cpp index 8737344d9384..da7407d181e1 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/MantidEVWorker.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/MantidEVWorker.cpp @@ -138,12 +138,15 @@ bool MantidEVWorker::isEventWorkspace( const std::string & event_ws_name ) * @param file_name Name of the NeXus file to load * @param ev_ws_name Name of the event workspace to create * @param md_ws_name Name of the MD workspace to create + * @param minQ The smallest absolute value of any component + * of Q to include. * @param maxQ The largest absolute value of any component * of Q to include. When ConvertToMD is called, * MinValues = -maxQ,-maxQ,-maxQ and * MaxValues = maxQ, maxQ, maxQ * @param do_lorentz_corr Set true to do the Lorentz correction when * converting to reciprocal space. + * @param load_data Set true to load original data. * @param load_det_cal Set true to call LoadIsawDetCal after loading * the event file. * @param det_cal_file Fully qualified name of the .DetCal file. @@ -377,6 +380,7 @@ bool MantidEVWorker::findUBUsingFFT( const std::string & peaks_ws_name, * peaks workspace. * * @param peaks_ws_name The name of the peaks workspace. + * @param tolerance The tolerance for peak finding. * * @return true if FindUBusingIndexedPeaks completed successfully. */ @@ -661,6 +665,12 @@ bool MantidEVWorker::changeHKL( const std::string & peaks_ws_name, * region. * @param integrate_edge If true, integrate peaks for which the sphere * goes off the edge of the detector. + * @param use_cylinder_integration Set true to use cylinder integration. + * @param cylinder_length The length of the cylinder to integrate. + * @param cylinder_percent_bkg The percentage of the cylinder length + * that is background. + * @param cylinder_profile_fit The fitting function for cylinder + * integration. * * @return true if the unweighted workspace was successfully created and * integrated using IntegratePeaksMD. diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysis.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysis.cpp index f90963c23e63..c187824512f4 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysis.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysis.cpp @@ -2,9 +2,9 @@ // Includes //---------------------- #include "MantidAPI/AlgorithmManager.h" -#include "MantidAPI/AnalysisDataService.h" #include "MantidAPI/FrameworkManager.h" #include "MantidAPI/IAlgorithm.h" +#include "MantidAPI/AnalysisDataService.h" #include "MantidAPI/Run.h" #include "MantidAPI/ScopedWorkspace.h" #include "MantidAPI/TableRow.h" @@ -28,6 +28,7 @@ #include "MantidQtCustomInterfaces/MuonAnalysisResultTableTab.h" #include "MantidQtMantidWidgets/FitPropertyBrowser.h" #include "MantidQtMantidWidgets/MuonFitPropertyBrowser.h" +#include "MantidQtMantidWidgets/MuonSequentialFitDialog.h" #include #include @@ -80,11 +81,17 @@ const QString MuonAnalysis::NOT_AVAILABLE("N/A"); //---------------------- ///Constructor MuonAnalysis::MuonAnalysis(QWidget *parent) : - UserSubWindow(parent), m_last_dir(), m_workspace_name("MuonAnalysis"), m_currentDataName(), - m_groupTableRowInFocus(0), m_pairTableRowInFocus(0), m_currentTab(NULL), - m_groupNames(), m_settingsGroup("CustomInterfaces/MuonAnalysis/"), - m_updating(false), m_loaded(false), m_deadTimesChanged(false), m_textToDisplay(""), m_dataTimeZero(0.0), - m_dataFirstGoodData(0.0) + UserSubWindow(parent), + m_last_dir(), + m_workspace_name("MuonAnalysis"), m_grouped_name(m_workspace_name + "Grouped"), + m_currentDataName(), + m_groupTableRowInFocus(0), m_pairTableRowInFocus(0), + m_currentTab(NULL), + m_groupNames(), + m_settingsGroup("CustomInterfaces/MuonAnalysis/"), + m_updating(false), m_loaded(false), m_deadTimesChanged(false), + m_textToDisplay(""), + m_dataTimeZero(0.0), m_dataFirstGoodData(0.0) {} /** @@ -142,6 +149,8 @@ void MuonAnalysis::initLayout() } m_uiForm.fitBrowser->init(); + connect( m_uiForm.fitBrowser, SIGNAL(sequentialFitRequested()), + this, SLOT(openSequentialFitDialog()) ); // alow appending files m_uiForm.mwRunFiles->allowMultipleFiles(true); @@ -361,7 +370,7 @@ void MuonAnalysis::plotItem(ItemType itemType, int tableRow, PlotType plotType) // Find names for new workspaces const std::string wsName = getNewAnalysisWSName(groupName, itemType, tableRow, plotType); - const std::string wsRawName = wsName + "; Raw"; + const std::string wsRawName = wsName + "_Raw"; // Make sure they end up in the ADS ads.addOrReplace(wsName, ws); @@ -389,7 +398,7 @@ void MuonAnalysis::plotItem(ItemType itemType, int tableRow, PlotType plotType) setCurrentDataName( wsNameQ ); } - catch(std::exception& e) + catch(...) { QMessageBox::critical( this, "MuonAnalysis - Error", "Unable to plot the item. Check log for details." ); } @@ -499,10 +508,7 @@ MatrixWorkspace_sptr MuonAnalysis::createAnalysisWorkspace(ItemType itemType, in alg->initialize(); - // TODO: should really be global - const std::string loadedWSName = m_workspace_name + "Grouped"; - - auto loadedWS = AnalysisDataService::Instance().retrieveWS(loadedWSName); + auto loadedWS = AnalysisDataService::Instance().retrieveWS(m_grouped_name); if ( auto group = boost::dynamic_pointer_cast(loadedWS) ) { @@ -567,7 +573,7 @@ MatrixWorkspace_sptr MuonAnalysis::createAnalysisWorkspace(ItemType itemType, in QTableWidget* t = m_uiForm.pairTable; - double alpha = t->item(m_pairTableRowInFocus,3)->text().toDouble(); + double alpha = t->item(tableRow,3)->text().toDouble(); int index1 = static_cast( t->cellWidget(tableRow,1) )->currentIndex(); int index2 = static_cast( t->cellWidget(tableRow,2) )->currentIndex(); @@ -788,6 +794,7 @@ void MuonAnalysis::runSaveGroupButton() void MuonAnalysis::runLoadGroupButton() { m_updating = true; + // Get grouping file QSettings prevValues; prevValues.beginGroup(m_settingsGroup + "LoadGroupFile"); @@ -823,30 +830,23 @@ void MuonAnalysis::runLoadGroupButton() clearTablesAndCombo(); fillGroupingTable(loadedGrouping, m_uiForm); + updateFront(); - // add number of detectors column to group table - int numRows = m_uiForm.groupTable->rowCount(); - for (int i = 0; i < numRows; i++) - { - QTableWidgetItem *item = m_uiForm.groupTable->item(i,1); - if (!item) - break; - if ( item->text().isEmpty() ) - break; + m_updating = false; - std::stringstream detNumRead; + if ( m_loaded ) + { try { - detNumRead << numOfDetectors(item->text().toStdString()); - m_uiForm.groupTable->setItem(i, 2, new QTableWidgetItem(detNumRead.str().c_str())); + groupLoadedWorkspace(); } - catch (...) + catch(std::exception& e) { - m_uiForm.groupTable->setItem(i, 2, new QTableWidgetItem("Invalid")); + g_log.error( e.what() ); + QMessageBox::critical(this, "MantidPlot - MuonAnalysis", + "Unable to group the workspace. See log for details."); } } - updateFront(); - m_updating = false; } /** @@ -1007,16 +1007,9 @@ void MuonAnalysis::runLoadCurrent() } if ( !isGroupingSet() ) - { - std::stringstream idstr; - idstr << "1-" << matrix_workspace->getNumberHistograms(); - m_uiForm.groupTable->setItem(0, 0, new QTableWidgetItem("NoGroupingDetected")); - m_uiForm.groupTable->setItem(0, 1, new QTableWidgetItem(idstr.str().c_str())); - updateFrontAndCombo(); - } + setDummyGrouping( matrix_workspace->getInstrument() ); - if ( !applyGroupingToWS(m_workspace_name, m_workspace_name+"Grouped") ) - return; + groupLoadedWorkspace(); // Populate instrument fields std::stringstream str; @@ -1198,9 +1191,23 @@ void MuonAnalysis::groupTableChanged(int row, int column) } } whichGroupToWhichRow(m_uiForm, m_groupToRow); - applyGroupingToWS(m_workspace_name, m_workspace_name+"Grouped"); updatePairTable(); updateFrontAndCombo(); + + if ( m_loaded && ! m_updating ) + { + try + { + groupLoadedWorkspace(); + } + catch(std::exception& e) + { + g_log.error( e.what() ); + + QMessageBox::critical(this, "MantidPlot - MuonAnalysis", + "Unable to group the workspace. See log for details"); + } + } } @@ -1428,7 +1435,9 @@ void MuonAnalysis::inputFileChanged(const QStringList& files) std::string mainFieldDirection(""); double timeZero(0.0); double firstGoodData(0.0); + ScopedWorkspace loadedDeadTimes; + ScopedWorkspace loadedDetGrouping; for (int i=0; iinitialize(); + loadMuonAlg->setLogging(false); loadMuonAlg->setPropertyValue("Filename", filename.toStdString() ); - loadMuonAlg->setPropertyValue("DeadTimeTable", loadedDeadTimes.name()); + loadMuonAlg->setProperty("AutoGroup", false); - if (i > 0) + if ( i == 0 ) { - QString tempRangeNum; - tempRangeNum.setNum(i); - loadMuonAlg->setPropertyValue("OutputWorkspace", m_workspace_name + tempRangeNum.toStdString() ); + // Get dead times/grouping from first file only + loadMuonAlg->setPropertyValue( "DeadTimeTable", loadedDeadTimes.name() ); + loadMuonAlg->setPropertyValue( "DetectorGroupingTable", loadedDetGrouping.name() ); + + loadMuonAlg->setPropertyValue("OutputWorkspace", m_workspace_name); } else { - loadMuonAlg->setPropertyValue("OutputWorkspace", m_workspace_name); + QString tempRangeNum; + tempRangeNum.setNum(i); + loadMuonAlg->setPropertyValue("OutputWorkspace", m_workspace_name + tempRangeNum.toStdString() ); } - loadMuonAlg->setProperty("AutoGroup", false); + if (loadMuonAlg->execute() ) { @@ -1517,38 +1532,28 @@ void MuonAnalysis::inputFileChanged(const QStringList& files) if (m_uiForm.instrSelector->currentText().toUpper() == "ARGUS") throw std::runtime_error("Dead times are currently not implemented in ARGUS files."); - IAlgorithm_sptr applyCorrAlg = AlgorithmManager::Instance().create("ApplyDeadTimeCorr"); - - applyCorrAlg->setPropertyValue("InputWorkspace", m_workspace_name); - applyCorrAlg->setPropertyValue("OutputWorkspace", m_workspace_name); - - ScopedWorkspace customDeadTimes; + ScopedWorkspace deadTimes; if (m_uiForm.deadTimeType->currentIndex() == 1) // From Run Data { if( ! loadedDeadTimes ) throw std::runtime_error("Data file doesn't appear to contain dead time values"); - - applyCorrAlg->setPropertyValue("DeadTimeTable", loadedDeadTimes.name()); + + Workspace_sptr ws = loadedDeadTimes.retrieve(); + loadedDeadTimes.remove(); + + deadTimes.set(ws); } else if (m_uiForm.deadTimeType->currentIndex() == 2) // From Specified File { - if(!m_uiForm.mwRunDeadTimeFile->isValid()) - throw std::runtime_error("Specified Dead Time file is not valid."); - - std::string deadTimeFile = m_uiForm.mwRunDeadTimeFile->getFirstFilename().toStdString(); - - IAlgorithm_sptr loadDeadTimes = AlgorithmManager::Instance().create("LoadNexusProcessed"); - loadDeadTimes->setPropertyValue("Filename", deadTimeFile); - loadDeadTimes->setPropertyValue("OutputWorkspace", customDeadTimes.name()); - loadDeadTimes->execute(); - - if ( ! customDeadTimes ) - throw std::runtime_error("Unable to load dead times from the spefied file"); - - applyCorrAlg->setPropertyValue("DeadTimeTable", customDeadTimes.name()); + Workspace_sptr ws = loadDeadTimes( deadTimeFilename() ); + deadTimes.set(ws); } + IAlgorithm_sptr applyCorrAlg = AlgorithmManager::Instance().create("ApplyDeadTimeCorr"); + applyCorrAlg->setPropertyValue("InputWorkspace", m_workspace_name); + applyCorrAlg->setPropertyValue("OutputWorkspace", m_workspace_name); + applyCorrAlg->setPropertyValue("DeadTimeTable", deadTimes.name()); applyCorrAlg->execute(); } catch(std::exception& e) @@ -1563,39 +1568,70 @@ void MuonAnalysis::inputFileChanged(const QStringList& files) } } - // Make the options available - m_optionTab->nowDataAvailable(); - - // Get hold of a pointer to a matrix workspace and apply grouping if applicatable - Workspace_sptr workspace_ptr = AnalysisDataService::Instance().retrieve(m_workspace_name); - WorkspaceGroup_sptr wsPeriods = boost::dynamic_pointer_cast(workspace_ptr); + // Get hold of a pointer to a matrix workspace MatrixWorkspace_sptr matrix_workspace; - int numPeriods = 1; // 1 may mean either a group with one period or simply just 1 normal matrix workspace - if (wsPeriods) + int numPeriods; + + Workspace_sptr loadedWS = AnalysisDataService::Instance().retrieve(m_workspace_name); + + if ( auto group = boost::dynamic_pointer_cast(loadedWS) ) { - numPeriods = wsPeriods->getNumberOfEntries(); + numPeriods = static_cast( group->size() ); + matrix_workspace = boost::dynamic_pointer_cast( group->getItem(0) ); + } + else + { + numPeriods = 1; + matrix_workspace = boost::dynamic_pointer_cast(loadedWS); + } - Workspace_sptr workspace_ptr1 = AnalysisDataService::Instance().retrieve(m_workspace_name + "_1"); - matrix_workspace = boost::dynamic_pointer_cast(workspace_ptr1); + if ( isGroupingSet() ) + { + // If grouping set already - it means it wasn't reset and we can use it + g_log.information("Using custom grouping"); + groupLoadedWorkspace(); } else { - matrix_workspace = boost::dynamic_pointer_cast(workspace_ptr); - } + setGroupingFromIDF( matrix_workspace->getInstrument(), mainFieldDirection ); + + if ( isGroupingSet() ) + { + g_log.information("Using grouping loaded from IDF"); + groupLoadedWorkspace(); + } + else if ( loadedDetGrouping ) + { + g_log.information("Using grouping loaded from Nexus file"); - // if grouping not set, first see if grouping defined in Nexus - if ( !isGroupingSet() ) - setGroupingFromNexus(files[0]); - // if grouping still not set, then take grouping from IDF - if ( !isGroupingSet() ) - setGroupingFromIDF(mainFieldDirection, matrix_workspace); - // finally if nothing else works set dummy grouping and display - // message to user - if ( !isGroupingSet() ) - setDummyGrouping(static_cast(matrix_workspace->getInstrument()->getDetectorIDs().size())); + Workspace_sptr groupingWS = loadedDetGrouping.retrieve(); + loadedDetGrouping.remove(); // Don't need it in the ADS any more - if ( !applyGroupingToWS(m_workspace_name, m_workspace_name+"Grouped") ) - throw std::runtime_error("Couldn't apply grouping"); + ITableWorkspace_sptr groupingTable; + + if ( auto table = boost::dynamic_pointer_cast(groupingWS) ) + { + groupingTable = table; + } + else if ( auto group = boost::dynamic_pointer_cast(groupingWS) ) + { + g_log.information("Multi-period grouping loaded from the Nexus file. Using the first one."); + groupingTable = boost::dynamic_pointer_cast( group->getItem(0) ); + } + + setGrouping(groupingTable); + groupLoadedWorkspace(groupingTable); + } + else + { + g_log.information("Using dummy grouping"); + setDummyGrouping( matrix_workspace->getInstrument() ); + groupLoadedWorkspace(); + } + } + + // Make the options available + m_optionTab->nowDataAvailable(); // Populate instrument fields std::stringstream str; @@ -1722,10 +1758,10 @@ void MuonAnalysis::inputFileChanged(const QStringList& files) infoStr += ss.str(); } else // Show appropriate error message. - infoStr += "Errror - Not set in data file."; + infoStr += "Error - Not set in data file."; } else // Show appropriate error message. - infoStr += "Errror - Not found in data file."; + infoStr += "Error - Not found in data file."; // Include all the run information. m_uiForm.infoBrowser->setText(infoStr.c_str()); @@ -2110,6 +2146,8 @@ void MuonAnalysis::clearTablesAndCombo() m_uiForm.pairTable->setCellWidget(i,1, new QComboBox); m_uiForm.pairTable->setCellWidget(i,2, new QComboBox); } + + m_uiForm.groupDescription->clear(); } /** @@ -2392,89 +2430,6 @@ bool MuonAnalysis::isGroupingSet() return true; } -/** - * Apply whatever grouping is specified in GUI tables to workspace. - */ -bool MuonAnalysis::applyGroupingToWS( const std::string& inputWsName, const std::string& outputWsName) -{ - if (!isGroupingSet() || !AnalysisDataService::Instance().doesExist(inputWsName)) - return false; - - std::string complaint = isGroupingAndDataConsistent(); - if (!( complaint.empty() ) ) - { - if (m_uiForm.frontPlotButton->isEnabled() ) - QMessageBox::warning(this, "MantidPlot - MuonAnalysis", complaint.c_str()); - m_optionTab->noDataAvailable(); - return false; - } - else - { - if (!m_uiForm.frontPlotButton->isEnabled() ) - m_optionTab->nowDataAvailable(); - } - - // If output workspace exists, remove explicitly, so even if something goes wrong - old data - // is not used - if(AnalysisDataService::Instance().doesExist(outputWsName)) - { - // Using DeleteWorkspace algorithm so if outputWs is a group - it is fully removed - Mantid::API::IAlgorithm_sptr rmWs = AlgorithmManager::Instance().create("DeleteWorkspace"); - rmWs->initialize(); - rmWs->setPropertyValue("Workspace", outputWsName); - rmWs->execute(); - } - - Grouping tableGrouping; - parseGroupingTable(m_uiForm, tableGrouping); - - // Retrieve input workspace - Workspace_sptr inputWs = AnalysisDataService::Instance().retrieve(inputWsName); - - Workspace_sptr outputWs; - - try // ... to group - { - // Single workspace - if(MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast(inputWs)) - { - outputWs = groupWorkspace(ws, tableGrouping); - } - // Workspace group - else if(WorkspaceGroup_sptr group = boost::dynamic_pointer_cast(inputWs)) - { - // Create output group - WorkspaceGroup_sptr outputGroup = boost::make_shared(); - - for(size_t i = 0; i < group->size(); i++) - { - if(MatrixWorkspace_sptr member = boost::dynamic_pointer_cast(group->getItem(i))) - { - MatrixWorkspace_sptr groupedMember = groupWorkspace(member, tableGrouping); - - outputGroup->addWorkspace(groupedMember); - } - else - throw std::invalid_argument("Group contains unsupported workspace type"); - } - - outputWs = outputGroup; - } - else - throw std::invalid_argument("Unsupported workspace type"); - } - catch(std::exception& e) - { - m_optionTab->noDataAvailable(); - g_log.error(e.what()); - return false; - } - - AnalysisDataService::Instance().add(outputWsName, outputWs); - - return true; -} - /** * Calculate number of detectors from string of type 1-3, 5, 10-15 * @@ -2483,78 +2438,9 @@ bool MuonAnalysis::applyGroupingToWS( const std::string& inputWsName, const std */ int MuonAnalysis::numOfDetectors(const std::string& str) const { - return static_cast(spectrumIDs(str).size()); + return static_cast(Strings::parseRange(str).size()); } - -/** - * Return a vector of IDs for row number from string of type 1-3, 5, 10-15 - * - * @param str :: String of type "1-3, 5, 10-15" - * @return Vector of IDs - */ -std::vector MuonAnalysis::spectrumIDs(const std::string& str) const -{ - std::vector retVal; - - if (str.empty()) - return retVal; - - typedef Poco::StringTokenizer tokenizer; - tokenizer values(str, ",", tokenizer::TOK_TRIM); - - for (int i = 0; i < static_cast(values.count()); i++) - { - std::size_t found= values[i].find("-"); - if (found!=std::string::npos) - { - tokenizer aPart(values[i], "-", tokenizer::TOK_TRIM); - - if ( aPart.count() != 2 ) - { - retVal.clear(); - return retVal; - } - else - { - if ( !(isNumber(aPart[0]) && isNumber(aPart[1])) ) - { - retVal.clear(); - return retVal; - } - } - - int leftInt; - std::stringstream leftRead(aPart[0]); - leftRead >> leftInt; - int rightInt; - std::stringstream rightRead(aPart[1]); - rightRead >> rightInt; - - if (leftInt > rightInt) - { - retVal.clear(); - return retVal; - } - for (int step = leftInt; step <= rightInt; step++) - retVal.push_back(step); - } - else - { - - if (isNumber(values[i])) - retVal.push_back(boost::lexical_cast(values[i].c_str())); - else - { - retVal.clear(); - return retVal; - } - } - } - return retVal; -} - - /** * Change the workspace group name to the instrument and run number if load current run was pressed. * @@ -2684,226 +2570,53 @@ void MuonAnalysis::startUpLook() } } -/** -* set grouping in table from information from nexus raw file -*/ -void MuonAnalysis::setGroupingFromNexus(const QString& nexusFile) -{ - if ( isGroupingSet() ) - return; - - std::string groupedWS = m_workspace_name+"Grouped"; - - // Setup Load Nexus Algorithm - Mantid::API::IAlgorithm_sptr loadMuonAlg = Mantid::API::AlgorithmManager::Instance().create("LoadMuonNexus"); - loadMuonAlg->setPropertyValue("Filename", nexusFile.toStdString()); - loadMuonAlg->setPropertyValue("OutputWorkspace", groupedWS); - loadMuonAlg->setProperty("AutoGroup", true); - if (! (loadMuonAlg->execute() ) ) - { - QMessageBox::warning(this,"Mantid - MuonAnalysis", "Problem when executing LoadMuonNexus algorithm."); - } - - // get hold of a matrix-workspace. If period data assume each period has - // the same grouping - Workspace_sptr ws_ptr = AnalysisDataService::Instance().retrieve(groupedWS); - WorkspaceGroup_sptr wsPeriods = boost::dynamic_pointer_cast(ws_ptr); - MatrixWorkspace_sptr matrix_workspace; - if (wsPeriods) - { - Workspace_sptr ws_ptr1 = AnalysisDataService::Instance().retrieve(groupedWS + "_1"); - matrix_workspace = boost::dynamic_pointer_cast(ws_ptr1); - } - else - { - matrix_workspace = boost::dynamic_pointer_cast(ws_ptr); - } - - // check if there is any grouping in file - bool thereIsGrouping = false; - int numOfHist = static_cast(matrix_workspace->getNumberHistograms()); //Qt has no size_t understanding - for (int wsIndex = 0; wsIndex < numOfHist; wsIndex++) - { - IDetector_const_sptr det; - try // for some bizarry reason when reading EMUautorun_A.tmp this - // underlying nexus file think there are more histogram than there is - // hence the reason for this try/catch here - { - det = matrix_workspace->getDetector(wsIndex); - } - catch (...) - { - break; - } - - if( boost::dynamic_pointer_cast(det) ) - { - // prepare IDs string - - boost::shared_ptr detG = boost::dynamic_pointer_cast(det); - std::vector detIDs = detG->getDetectorIDs(); - if (detIDs.size() > 1) - { - thereIsGrouping = true; - break; - } - } - } - - // if no grouping in nexus then return - if ( thereIsGrouping == false ) - { - return; - } - - // Add info about grouping from Nexus file to group table - for (int wsIndex = 0; wsIndex < numOfHist; wsIndex++) - { - IDetector_const_sptr det = matrix_workspace->getDetector(wsIndex); - - if( boost::dynamic_pointer_cast(det) ) - { - // prepare IDs string - - boost::shared_ptr detG = boost::dynamic_pointer_cast(det); - std::vector detIDs = detG->getDetectorIDs(); - std::stringstream idstr; - int leftInt = detIDs[0]; // meaning left as in the left number of the range 8-18 for instance - int numIDs = static_cast(detIDs.size()); - idstr << detIDs[0]; - for (int i = 1; i < numIDs; i++) - { - if (detIDs[i] != detIDs[i-1]+1 ) - { - if (detIDs[i-1] == leftInt) - { - idstr << ", " << detIDs[i]; - leftInt = detIDs[i]; - } - else - { - idstr << "-" << detIDs[i-1] << ", " << detIDs[i]; - leftInt = detIDs[i]; - } - } - else if ( i == numIDs-1 ) - { - idstr << "-" << detIDs[i]; - } - } - - // prepare group name string - - std::stringstream gName; - gName << wsIndex; - - // create table row - QTableWidgetItem* it = m_uiForm.groupTable->item(wsIndex, 0); - if (it) - it->setText(gName.str().c_str()); - else - { - m_uiForm.groupTable->setItem(wsIndex, 0, new QTableWidgetItem(gName.str().c_str())); - } - - it = m_uiForm.groupTable->item(wsIndex, 1); - if (it) - it->setText(idstr.str().c_str()); - else - m_uiForm.groupTable->setItem(wsIndex, 1, new QTableWidgetItem(idstr.str().c_str())); - } - } // end loop over wsIndex - - // check if exactly two groups added in which case assume these are forward/backward groups - // and automatically then create a pair from which, where the first group is assumed to be - // the forward group - - updatePairTable(); - if ( numGroups() == 2 && numPairs() <= 0 ) - { - QTableWidgetItem* it = m_uiForm.pairTable->item(0, 0); - if (it) - it->setText("pair"); - else - { - m_uiForm.pairTable->setItem(0, 0, new QTableWidgetItem("long")); - } - it = m_uiForm.pairTable->item(0, 3); - if (it) - it->setText("1.0"); - else - { - m_uiForm.pairTable->setItem(0, 3, new QTableWidgetItem("1.0")); - } - updatePairTable(); - updateFrontAndCombo(); - m_uiForm.frontGroupGroupPairComboBox->setCurrentIndex(2); - runFrontGroupGroupPairComboBox(2); - } - updatePairTable(); - updateFrontAndCombo(); -} - - /** * If nothing else work set dummy grouping and display comment to user */ -void MuonAnalysis::setDummyGrouping(const int numDetectors) +void MuonAnalysis::setDummyGrouping(Instrument_const_sptr instrument) { // if no grouping in nexus then set dummy grouping and display warning to user std::stringstream idstr; - idstr << "1-" << numDetectors; - m_uiForm.groupTable->setItem(0, 0, new QTableWidgetItem("NoGroupingDetected")); - m_uiForm.groupTable->setItem(0, 1, new QTableWidgetItem(idstr.str().c_str())); + idstr << "1-" << instrument->getNumberDetectors(); + m_uiForm.groupTable->setItem( 0, 0, new QTableWidgetItem("NoGroupingDetected") ); + m_uiForm.groupTable->setItem( 0, 1, new QTableWidgetItem( QString::fromStdString(idstr.str()) ) ); updateFrontAndCombo(); - - QMessageBox::warning(this, "MantidPlot - MuonAnalysis", QString("No grouping detected in Nexus file.\n") - + "and no default grouping file specified in IDF\n" - + "therefore dummy grouping created."); } /** * Try to load default grouping file specified in IDF */ -void MuonAnalysis::setGroupingFromIDF(const std::string& mainFieldDirection, MatrixWorkspace_sptr matrix_workspace) +void MuonAnalysis::setGroupingFromIDF(Instrument_const_sptr instrument, const std::string& mainFieldDirection) { - Instrument_const_sptr inst = matrix_workspace->getInstrument(); + std::string parameterName = "Default grouping file"; - QString instname = m_uiForm.instrSelector->currentText().toUpper(); - - QString groupParameter = "Default grouping file"; - // for now hard coded in the special case of MUSR - if (instname == "MUSR") + // Special case for MUSR, because it has two possible groupings + if (instrument->getName() == "MUSR") { - if ( mainFieldDirection == "Transverse" ) - groupParameter += " - Transverse"; - else - groupParameter += " - Longitudinal"; + parameterName.append(" - " + mainFieldDirection); } - std::vector groupFile = inst->getStringParameter(groupParameter.toStdString()); + std::vector groupingFiles = instrument->getStringParameter(parameterName); - // get search directory for XML instrument definition files (IDFs) + // Get search directory for XML instrument definition files (IDFs) std::string directoryName = ConfigService::Instance().getInstrumentDirectory(); - if ( groupFile.size() == 1 ) + if ( groupingFiles.size() == 1 ) { - Grouping loadedGrouping; + const std::string groupingFile = groupingFiles[0]; try { - loadGroupingFromXML(directoryName+groupFile[0], loadedGrouping); + Grouping loadedGrouping; + loadGroupingFromXML(directoryName + groupingFile, loadedGrouping); + fillGroupingTable(loadedGrouping, m_uiForm); } catch (...) { - QMessageBox::warning(this, "MantidPlot - MuonAnalysis", - QString("Can't load default grouping file in IDF. \n With name: ") + groupFile[0].c_str()); - return; + g_log.error("Can't load default grouping file: " + groupingFile); } - - fillGroupingTable(loadedGrouping, m_uiForm); } } @@ -3075,7 +2788,7 @@ void MuonAnalysis::checkIf_ID_dublicatesInTable(const int row) QTableWidgetItem *item = m_uiForm.groupTable->item(row,1); // row of IDs to compare against - std::vector idsNew = spectrumIDs(item->text().toStdString()); + std::vector idsNew = Strings::parseRange(item->text().toStdString()); int numG = numGroups(); int rowInFocus = getGroupNumberFromRow(row); @@ -3083,7 +2796,7 @@ void MuonAnalysis::checkIf_ID_dublicatesInTable(const int row) { if (iG != rowInFocus) { - std::vector ids = spectrumIDs(m_uiForm.groupTable->item(m_groupToRow[iG],1)->text().toStdString()); + std::vector ids = Strings::parseRange(m_uiForm.groupTable->item(m_groupToRow[iG],1)->text().toStdString()); for (unsigned int i = 0; i < ids.size(); i++) { @@ -3858,5 +3571,283 @@ void MuonAnalysis::setFirstGoodDataState(int checkBoxState) } } +/** + * Groups loaded workspace (m_workspace_name). Grouped workspace is stored under m_grouped_name. + * @param detGroupingTable :: Grouping information to use. If null - info from table widget is used + */ +void MuonAnalysis::groupLoadedWorkspace(ITableWorkspace_sptr detGroupingTable) +{ + if ( ! detGroupingTable ) + { + auto groupingFromUI = parseGrouping(); + + if ( ! groupingFromUI ) + throw std::invalid_argument("Unable to parse grouping information from the table, or it is empty."); + + detGroupingTable = groupingFromUI; + } + + // Make sure grouping table is in the ADS + ScopedWorkspace table(detGroupingTable); + + try + { + IAlgorithm_sptr groupAlg = AlgorithmManager::Instance().createUnmanaged("MuonGroupDetectors"); + groupAlg->initialize(); + groupAlg->setLogging(false); // Don't want to clutter the log + groupAlg->setRethrows(true); + groupAlg->setPropertyValue("InputWorkspace", m_workspace_name); + groupAlg->setPropertyValue("OutputWorkspace", m_grouped_name); + groupAlg->setPropertyValue("DetectorGroupingTable", table.name()); + groupAlg->execute(); + } + catch(std::exception& e) + { + throw std::runtime_error( "Unable to group loaded workspace:\n\n" + std::string(e.what()) ); + } +} + +/** + * Parses grouping information from the UI table. + * @return ITableWorkspace of the format returned by LoadMuonNexus + */ +ITableWorkspace_sptr MuonAnalysis::parseGrouping() +{ + std::vector groupRows; + whichGroupToWhichRow(m_uiForm, groupRows); + + if ( groupRows.size() == 0 ) + return ITableWorkspace_sptr(); + + auto newTable = boost::dynamic_pointer_cast( + WorkspaceFactory::Instance().createTable("TableWorkspace") ); + + newTable->addColumn("vector_int", "Detectors"); + + for ( auto it = groupRows.begin(); it != groupRows.end(); ++it ) + { + const std::string detectorsString = m_uiForm.groupTable->item(*it,1)->text().toStdString(); + + TableRow newRow = newTable->appendRow(); + newRow << Strings::parseRange(detectorsString); + } + + return newTable; +} + +/** + * Updated UI table using the grouping information provided. + * @param detGroupingTable :: Grouping information in the format as returned by LoadMuonNexus + */ +void MuonAnalysis::setGrouping(ITableWorkspace_sptr detGroupingTable) +{ + for ( size_t row = 0; row < detGroupingTable->rowCount(); ++row ) + { + std::vector detectors = detGroupingTable->cell< std::vector >(row,0); + + // toString() expects the sequence to be sorted + std::sort( detectors.begin(), detectors.end() ); + + // Convert to a range string, i.e. 1-5,6-8,9 + const std::string& detectorRange = Strings::toString(detectors); + + m_uiForm.groupTable->setItem( static_cast(row), 0, + new QTableWidgetItem( QString::number(row + 1) ) ); + + m_uiForm.groupTable->setItem( static_cast(row), 1, + new QTableWidgetItem( QString::fromStdString(detectorRange) ) ); + } + + if ( numGroups() == 2 && numPairs() <= 0 ) + { + m_uiForm.pairTable->setItem( 0, 0, new QTableWidgetItem("long") ); + m_uiForm.pairTable->setItem( 0, 3, new QTableWidgetItem("1.0") ); + } + + updatePairTable(); + updateFrontAndCombo(); +} + +/** + * Opens a sequential fit dialog. + */ +void MuonAnalysis::openSequentialFitDialog() +{ + Algorithm_sptr loadAlg; + + try + { + loadAlg = createLoadAlgorithm(); + } + catch(...) + { + QMessageBox::critical(this, "Unable to open dialog", "Error while setting load properties"); + return; + } + + m_uiForm.fitBrowser->blockSignals(true); + + MuonSequentialFitDialog* dialog = new MuonSequentialFitDialog(m_uiForm.fitBrowser, loadAlg); + dialog->exec(); + + m_uiForm.fitBrowser->blockSignals(false); +} + +/** + * Returns custom dead time table file name as set on the interface. + * @return The filename + */ +std::string MuonAnalysis::deadTimeFilename() +{ + if(!m_uiForm.mwRunDeadTimeFile->isValid()) + throw std::runtime_error("Specified Dead Time file is not valid."); + + return m_uiForm.mwRunDeadTimeFile->getFirstFilename().toStdString(); +} + +/** + * Loads dead time table (group of tables) from the file. + * @param filename :: File to load dead times from + * @return Table (group of tables) with dead times + */ +Workspace_sptr MuonAnalysis::loadDeadTimes(const std::string& filename) +{ + try + { + IAlgorithm_sptr loadDeadTimes = AlgorithmManager::Instance().create("LoadNexusProcessed"); + loadDeadTimes->setChild(true); + loadDeadTimes->setPropertyValue("Filename", filename); + loadDeadTimes->setPropertyValue("OutputWorkspace", "__NotUsed"); + loadDeadTimes->execute(); + + return loadDeadTimes->getProperty("OutputWorkspace"); + } + catch(...) + { + throw std::runtime_error("Unable to load dead times from the spefied file"); + } +} + +/** + * Creates and algorithm with all the properties set according to widget values on the interface. + * @return The algorithm with properties set + */ +Algorithm_sptr MuonAnalysis::createLoadAlgorithm() +{ + Algorithm_sptr loadAlg = AlgorithmManager::Instance().createUnmanaged("MuonLoad"); + loadAlg->initialize(); + + // -- Dead Time Correction -------------------------------------------------- + + if (m_uiForm.deadTimeType->currentIndex() != 0) + { + loadAlg->setProperty("ApplyDeadTimeCorrection", true); + + if (m_uiForm.deadTimeType->currentIndex() == 2) // From Specified File + { + + Workspace_sptr deadTimes = loadDeadTimes( deadTimeFilename() ); + + loadAlg->setProperty("CustomDeadTimeTable", deadTimes); + } + } + + // -- Grouping -------------------------------------------------------------- + + ITableWorkspace_sptr grouping = parseGrouping(); + loadAlg->setProperty("DetectorGroupingTable", grouping); + + // -- X axis options -------------------------------------------------------- + + double Xmin = m_uiForm.timeAxisStartAtInput->text().toDouble(); + loadAlg->setProperty("Xmin", Xmin); + + double Xmax = m_uiForm.timeAxisFinishAtInput->text().toDouble(); + loadAlg->setProperty("Xmax", Xmax); + + double timeZero = m_uiForm.timeZeroFront->text().toDouble(); + loadAlg->setProperty("TimeZero", timeZero); + + // -- Rebin options --------------------------------------------------------- + + if ( m_uiForm.rebinComboBox->currentIndex() != 0) + { + std::string rebinParams; + + if(m_uiForm.rebinComboBox->currentIndex() == 1) // Fixed + { + auto loadedWS = AnalysisDataService::Instance().retrieveWS(m_grouped_name); + MatrixWorkspace_sptr ws; + + if ( ! ( ws = boost::dynamic_pointer_cast(loadedWS) ) ) + { + auto group = boost::dynamic_pointer_cast(loadedWS); + ws = boost::dynamic_pointer_cast(group->getItem(0)); + } + + double binSize = ws->dataX(0)[1] - ws->dataX(0)[0]; + + double bunchedBinSize = binSize * m_uiForm.optionStepSizeText->text().toDouble(); + + rebinParams = boost::lexical_cast(bunchedBinSize); + } + else // Variable + { + rebinParams = m_uiForm.binBoundaries->text().toStdString(); + } + + loadAlg->setPropertyValue("RebinParams", rebinParams); + } + + // -- Group/pair properties ------------------------------------------------- + + int index = m_uiForm.frontGroupGroupPairComboBox->currentIndex(); + + if (index >= numGroups()) + { + loadAlg->setProperty("OutputType", "PairAsymmetry"); + int tableRow = m_pairToRow[index - numGroups()]; + + QTableWidget* t = m_uiForm.pairTable; + + double alpha = t->item(tableRow,3)->text().toDouble(); + int index1 = static_cast( t->cellWidget(tableRow,1) )->currentIndex(); + int index2 = static_cast( t->cellWidget(tableRow,2) )->currentIndex(); + + loadAlg->setProperty("PairFirstIndex", index1); + loadAlg->setProperty("PairSecondIndex", index2); + loadAlg->setProperty("Alpha", alpha); + } + else + { + if ( parsePlotType(m_uiForm.frontPlotFuncs) == Asymmetry ) + loadAlg->setProperty("OutputType", "GroupAsymmetry"); + else + loadAlg->setProperty("OutputType", "GroupCounts"); + + int groupIndex = getGroupNumberFromRow(m_groupToRow[index]); + loadAlg->setProperty("GroupIndex", groupIndex); + } + + // -- Period options -------------------------------------------------------- + + QString periodLabel1 = m_uiForm.homePeriodBox1->currentText(); + + int periodIndex1 = periodLabel1.toInt() - 1; + loadAlg->setProperty("FirstPeriod", periodIndex1); + + QString periodLabel2 = m_uiForm.homePeriodBox2->currentText(); + if ( periodLabel2 != "None" ) + { + int periodIndex2 = periodLabel2.toInt() - 1; + loadAlg->setProperty("SecondPeriod", periodIndex2); + + std::string op = m_uiForm.homePeriodBoxMath->currentText().toStdString(); + loadAlg->setProperty("PeriodOperation", op); + } + + return loadAlg; +} + }//namespace MantidQT }//namespace CustomInterfaces diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysisOptionTab.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysisOptionTab.cpp index 59031c813ef3..73aac8086f96 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysisOptionTab.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysisOptionTab.cpp @@ -496,8 +496,6 @@ void MuonAnalysisOptionTab::storeCustomTimeValue() * - ShowErrors: True of False * - YAxisAuto: True or False * - YAxisMin/YAxisMax: Double values - * - * @param workspace :: The workspace name of the plot to be created. */ QMap MuonAnalysisOptionTab::parsePlotStyleParams() const { diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysisResultTableTab.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysisResultTableTab.cpp index 46744811542f..3bd15f1c8f8c 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysisResultTableTab.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysisResultTableTab.cpp @@ -9,6 +9,7 @@ #include "MantidQtAPI/UserSubWindow.h" #include +#include #include #include @@ -27,8 +28,10 @@ namespace CustomInterfaces { namespace Muon { - using namespace MantidQt::API; using namespace Mantid::Kernel; + using namespace Mantid::API; + + using namespace MantidQt::API; using namespace MantidQt::MantidWidgets; const std::string MuonAnalysisResultTableTab::RUN_NO_LOG = "run_number"; @@ -207,6 +210,7 @@ void MuonAnalysisResultTableTab::populateTables(const QStringList& wsList) // Clear the previous table values m_logValues.clear(); QVector fittedWsList; + // Get all the workspaces from the fitPropertyBrowser and find out whether they have had fitting done to them. for (int i(0); i items = + Mantid::API::AnalysisDataService::Instance().topLevelItems(); + + for ( auto it = items.begin(); it != items.end(); ++it ) + { + if ( ! boost::starts_with(it->first, "MuonSeqFit_") ) + continue; + + auto group = boost::dynamic_pointer_cast(it->second); + + if ( ! group ) + continue; + + for ( size_t i = 0; i < group->size(); ++i ) + { + auto ws = boost::dynamic_pointer_cast( group->getItem(i) ); + + if ( ! ws ) + continue; + + std::string name = ws->name(); + size_t pos = name.find("_Workspace"); + + if ( pos == std::string::npos) + continue; + + name.erase(pos); + + fittedWsList << QString::fromStdString(name); + } + } + if(fittedWsList.size() > 0) { // Make sure all params match. diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/Quasi.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/Quasi.cpp index 9f4e381d5efa..8e922f0a4edb 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/Quasi.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/Quasi.cpp @@ -48,7 +48,7 @@ namespace MantidQt * Set the data selectors to use the default save directory * when browsing for input files. * - * @param filename :: The name of the workspace to plot + * @param settings :: The current settings */ void Quasi::loadSettings(const QSettings& settings) { diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ResNorm.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ResNorm.cpp index 96e775bbd984..81fd95db93f1 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ResNorm.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ResNorm.cpp @@ -94,7 +94,7 @@ namespace MantidQt * Set the data selectors to use the default save directory * when browsing for input files. * - * @param filename :: The name of the workspace to plot + * @param settings :: The current settings */ void ResNorm::loadSettings(const QSettings& settings) { diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/Stretch.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/Stretch.cpp index a93294dd37a2..c6e912f9784a 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/Stretch.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/Stretch.cpp @@ -121,7 +121,7 @@ namespace MantidQt * Set the data selectors to use the default save directory * when browsing for input files. * - * @param filename :: The name of the workspace to plot + * @param settings :: The current settings */ void Stretch::loadSettings(const QSettings& settings) { diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/EventNexusFileMementoTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/EventNexusFileMementoTest.h index f30a5d07af6a..ddb6991962d8 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/EventNexusFileMementoTest.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/EventNexusFileMementoTest.h @@ -86,7 +86,7 @@ class EventNexusFileMementoTest : public CxxTest::TestSuite memento.setUB(0,0,2,0,4,0,-8,0,0); memento.setLogValue("A", "12", "Number"); memento.setLogValue("angle1", "1.234", "Number Series"); - memento.setLogValue("angle2", "2", "Number Series"); + memento.setLogValue("angle2", "2.0", "Number Series"); memento.setGoniometer("angle1, 1.0,2.0,3.0,1","angle2, 1.1,2.1,3.1,-1","","","",""); IEventWorkspace_sptr ws = boost::dynamic_pointer_cast(memento.applyActions()); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/RawFileMementoTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/RawFileMementoTest.h index d2bb15281074..e3e6585aa9c7 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/RawFileMementoTest.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/RawFileMementoTest.h @@ -77,7 +77,7 @@ class RawFileMementoTest : public CxxTest::TestSuite memento.setUB(0,0,2,0,4,0,-8,0,0); memento.setLogValue("A", "12", "Number"); memento.setLogValue("angle1", "1.234", "Number Series"); - memento.setLogValue("angle2", "2", "Number Series"); + memento.setLogValue("angle2", "2.0", "Number Series"); memento.setGoniometer("angle1, 1.0,2.0,3.0,1","angle2, 1.1,2.1,3.1,-1","","","",""); MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast(memento.applyActions()); diff --git a/Code/Mantid/MantidQt/MantidWidgets/CMakeLists.txt b/Code/Mantid/MantidQt/MantidWidgets/CMakeLists.txt index 3b33b025556a..08cb74567e17 100644 --- a/Code/Mantid/MantidQt/MantidWidgets/CMakeLists.txt +++ b/Code/Mantid/MantidQt/MantidWidgets/CMakeLists.txt @@ -1,22 +1,23 @@ set ( SRC_FILES src/AlgorithmSelectorWidget.cpp + src/CatalogHelper.cpp + src/CatalogSearch.cpp src/CheckboxHeader.cpp src/DataSelector.cpp src/DiagResults.cpp - src/FindReplaceDialog.cpp src/FindDialog.cpp + src/FindReplaceDialog.cpp src/FitPropertyBrowser.cpp src/FunctionBrowser.cpp - src/CatalogSearch.cpp - src/CatalogHelper.cpp src/InstrumentSelector.cpp - src/MessageDisplay.cpp src/MWDiag.cpp src/MWRunFiles.cpp + src/MessageDisplay.cpp src/MultifitSetupDialog.cpp src/MuonFitPropertyBrowser.cpp + src/MuonSequentialFitDialog.cpp + src/ProcessingAlgoWidget.cpp src/PropertyHandler.cpp - src/ProcessingAlgoWidget.cpp src/RangeSelector.cpp src/RenameParDialog.cpp src/SafeQwtPlot.cpp @@ -32,9 +33,9 @@ set ( SRC_FILES # Header files with Q_OBJECT that qmake will "moc" set ( MOC_FILES - inc/MantidQtMantidWidgets/AlgorithmSelectorWidget.h - inc/MantidQtMantidWidgets/CheckboxHeader.h - inc/MantidQtMantidWidgets/DataSelector.h + inc/MantidQtMantidWidgets/AlgorithmSelectorWidget.h + inc/MantidQtMantidWidgets/CheckboxHeader.h + inc/MantidQtMantidWidgets/DataSelector.h inc/MantidQtMantidWidgets/DiagResults.h inc/MantidQtMantidWidgets/FindReplaceDialog.h inc/MantidQtMantidWidgets/FindDialog.h @@ -45,6 +46,7 @@ set ( MOC_FILES inc/MantidQtMantidWidgets/MessageDisplay.h inc/MantidQtMantidWidgets/MultifitSetupDialog.h inc/MantidQtMantidWidgets/MuonFitPropertyBrowser.h + inc/MantidQtMantidWidgets/MuonSequentialFitDialog.h inc/MantidQtMantidWidgets/MWDiag.h inc/MantidQtMantidWidgets/MWRunFiles.h inc/MantidQtMantidWidgets/PropertyHandler.h @@ -52,7 +54,7 @@ set ( MOC_FILES inc/MantidQtMantidWidgets/pythonCalc.h inc/MantidQtMantidWidgets/RangeSelector.h inc/MantidQtMantidWidgets/RenameParDialog.h - inc/MantidQtMantidWidgets/SafeQwtPlot.h + inc/MantidQtMantidWidgets/SafeQwtPlot.h inc/MantidQtMantidWidgets/SaveWorkspaces.h inc/MantidQtMantidWidgets/ScriptEditor.h inc/MantidQtMantidWidgets/SelectFunctionDialog.h @@ -71,11 +73,12 @@ set ( INC_FILES # QtDesigner UI files to process set ( UI_FILES - inc/MantidQtMantidWidgets/DataSelector.ui + inc/MantidQtMantidWidgets/DataSelector.ui inc/MantidQtMantidWidgets/CatalogSearch.ui - inc/MantidQtMantidWidgets/MultifitSetupDialog.ui inc/MantidQtMantidWidgets/MWDiag.ui inc/MantidQtMantidWidgets/MWRunFiles.ui + inc/MantidQtMantidWidgets/MultifitSetupDialog.ui + inc/MantidQtMantidWidgets/MuonSequentialFitDialog.ui inc/MantidQtMantidWidgets/ProcessingAlgoWidget.ui inc/MantidQtMantidWidgets/RenameParDialog.ui inc/MantidQtMantidWidgets/SelectFunctionDialog.ui diff --git a/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/FitPropertyBrowser.h b/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/FitPropertyBrowser.h index ac67db1dbc87..ab21560ff6de 100644 --- a/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/FitPropertyBrowser.h +++ b/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/FitPropertyBrowser.h @@ -96,8 +96,13 @@ class EXPORT_OPT_MANTIDQT_MANTIDWIDGETS FitPropertyBrowser: public QDockWidget, /// Create a new function PropertyHandler* addFunction(const std::string& fnName); + /// Get Composite Function boost::shared_ptr compositeFunction()const{return m_compositeFunction;} + + /// Return the fitting function + Mantid::API::IFunction_sptr getFittingFunction() const; + /// Get the default function type std::string defaultFunctionType()const; /// Set the default function type @@ -209,9 +214,10 @@ class EXPORT_OPT_MANTIDQT_MANTIDWIDGETS FitPropertyBrowser: public QDockWidget, /// Create a MatrixWorkspace from a TableWorkspace Mantid::API::Workspace_sptr createMatrixFromTableWorkspace()const; + public slots: virtual void fit(){ doFit(500); } - void sequentialFit(); + virtual void sequentialFit(); void undoFit(); void clear(); void clearBrowser(); @@ -337,8 +343,6 @@ private slots: void minimizerChanged(); /// Do the fitting void doFit(int maxIterations); - /// Return the fitting function - Mantid::API::IFunction_sptr getFittingFunction() const; /// Property managers: QtGroupPropertyManager *m_groupManager; diff --git a/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonFitPropertyBrowser.h b/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonFitPropertyBrowser.h index 1745a05c6460..1d05b1422cd2 100644 --- a/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonFitPropertyBrowser.h +++ b/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonFitPropertyBrowser.h @@ -47,7 +47,12 @@ class EXPORT_OPT_MANTIDQT_MANTIDWIDGETS MuonFitPropertyBrowser: public MantidQt: public slots: /// Perform the fit algorithm virtual void fit(); + /// Open sequential fit dialog + virtual void sequentialFit(); +signals: + /// Emitted when sequential fit is requested by user + void sequentialFitRequested(); protected: virtual void showEvent(QShowEvent* e); @@ -60,8 +65,6 @@ private slots: private: /// Get the registered function names virtual void populateFunctionNames(); - /// Enable/disable the Fit button; - virtual void setFitEnabled(bool yes); /// Check if the workspace can be used in the fit virtual bool isWorkspaceValid(Mantid::API::Workspace_sptr)const; @@ -71,4 +74,4 @@ private slots: } // API -#endif /*MUONFITPROPERTYBROWSER_H_*/ \ No newline at end of file +#endif /*MUONFITPROPERTYBROWSER_H_*/ diff --git a/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonSequentialFitDialog.h b/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonSequentialFitDialog.h new file mode 100644 index 000000000000..1a3e76a190d0 --- /dev/null +++ b/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonSequentialFitDialog.h @@ -0,0 +1,132 @@ +#ifndef MANTID_MANTIDWIDGETS_MUONSEQUENTIALFITDIALOG_H_ +#define MANTID_MANTIDWIDGETS_MUONSEQUENTIALFITDIALOG_H_ + +#include "MantidKernel/System.h" +#include "MantidKernel/Logger.h" + +#include "ui_MuonSequentialFitDialog.h" + +#include "MantidQtMantidWidgets/MuonFitPropertyBrowser.h" + +#include + +namespace MantidQt +{ +namespace MantidWidgets +{ + using namespace Mantid::Kernel; + using namespace Mantid::API; + + /** MuonSequentialFitDialog : TODO: DESCRIPTION + + Copyright © 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + File change history is stored at: + Code Documentation is available at: + */ + class EXPORT_OPT_MANTIDQT_MANTIDWIDGETS MuonSequentialFitDialog : public QDialog + { + + Q_OBJECT + + public: + MuonSequentialFitDialog(MuonFitPropertyBrowser* fitPropBrowser, Algorithm_sptr loadAlg); + virtual ~MuonSequentialFitDialog(); + + enum DialogState + { + Running, + Stopped + }; + + signals: + void stateChanged(DialogState newState); + + private: + + // -- FUNCTIONS ----------------------------------------------------------- + + /// Check if all the input field are valid + bool isInputValid(); + + /// Set current dialog state + void setState(DialogState newState); + + /// Initialize diagnosis table + void initDiagnosisTable(); + + /// Add a new entry to the diagnosis table + void addDiagnosisEntry(const std::string& runTitle, double fitQuality, + IFunction_sptr fittedFunction); + + /// Helper function to create new item for Diagnosis table + QTableWidgetItem* createTableWidgetItem(const QString& text); + + // -- VARIABLES ----------------------------------------------------------- + + /// UI form + Ui::MuonSequentialFitDialog m_ui; + + /// Fit properties browser used to start the dialog + MuonFitPropertyBrowser* m_fitPropBrowser; + + /// Current state of the dialog + DialogState m_state; + + /// Whether user requested fitting to be stopped + bool m_stopRequested; + + /// Algorithm the dialog should use for loading + Algorithm_sptr m_loadAlg; + + // -- STATIC MEMBERS ------------------------------------------------------ + + /// Checks if specified name is valid as a name for label. + static std::string isValidLabel(const std::string& label); + + /// Returns displayable title for the given workspace + static std::string getRunTitle(Workspace_const_sptr ws); + + /// Instance used to print log messages + static Mantid::Kernel::Logger& g_log; + + private slots: + /// Updates visibility/tooltip of label error asterisk + void updateLabelError(const QString& label); + + /// Enables/disables start button depending on wether we are allowed to start + void updateControlButtonState(); + + /// Sets control button to be start/stop depending on new dialog state + void updateControlButtonType(DialogState newState); + + /// Update enabled state off all the input widgets depending on new dialog state + void updateInputEnabled(DialogState newState); + + /// Start fitting process + void startFit(); + + /// Stop fitting process + void stopFit(); + }; + + +} // namespace MantidWidgets +} // namespace Mantid + +#endif /* MANTID_MANTIDWIDGETS_MUONSEQUENTIALFITDIALOG_H_ */ diff --git a/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonSequentialFitDialog.ui b/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonSequentialFitDialog.ui new file mode 100644 index 000000000000..ce0433dfafbd --- /dev/null +++ b/Code/Mantid/MantidQt/MantidWidgets/inc/MantidQtMantidWidgets/MuonSequentialFitDialog.ui @@ -0,0 +1,270 @@ + + + MuonSequentialFitDialog + + + + 0 + 0 + 550 + 500 + + + + + 0 + 0 + + + + + 550 + 500 + + + + Muon Analysis - Sequential Fitting + + + + + + 0 + + + 6 + + + + + + + + true + + + true + + + + + + + + 0 + 0 + + + + Runs: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Label: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 6 + + + + + + + + + 0 + 0 + + + + + + + + + 170 + 0 + 0 + + + + + + + + + 170 + 0 + 0 + + + + + + + + + 161 + 160 + 159 + + + + + + + + * + + + + + + + + + + 0 + 0 + + + + Parameters: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + 0 + + + + Values from previous fit + + + true + + + paramTypeGroup + + + + + + + + 0 + 0 + + + + Initial values for every fit + + + paramTypeGroup + + + + + + + + + + + + + false + + + 0 + + + true + + + false + + + + + + + Start + + + true + + + false + + + + + + + + + Diagnosis + + + false + + + + 6 + + + 0 + + + 0 + + + + + + + + + + + + MantidQt::MantidWidgets::MWRunFiles + QWidget +
MantidQtMantidWidgets/MWRunFiles.h
+
+
+ + + + + +
diff --git a/Code/Mantid/MantidQt/MantidWidgets/src/FitPropertyBrowser.cpp b/Code/Mantid/MantidQt/MantidWidgets/src/FitPropertyBrowser.cpp index b4d4d4892cc9..76061ac0f484 100644 --- a/Code/Mantid/MantidQt/MantidWidgets/src/FitPropertyBrowser.cpp +++ b/Code/Mantid/MantidQt/MantidWidgets/src/FitPropertyBrowser.cpp @@ -2253,6 +2253,7 @@ void FitPropertyBrowser::clearAllPlots() /** Create a double property and set some settings * @param name :: The name of the new property + * @param manager :: The current property manager * @return Pointer to the created property */ QtProperty* FitPropertyBrowser::addDoubleProperty(const QString& name, QtDoublePropertyManager *manager)const diff --git a/Code/Mantid/MantidQt/MantidWidgets/src/FunctionBrowser.cpp b/Code/Mantid/MantidQt/MantidWidgets/src/FunctionBrowser.cpp index 96720714f4ad..a741f24bde5c 100644 --- a/Code/Mantid/MantidQt/MantidWidgets/src/FunctionBrowser.cpp +++ b/Code/Mantid/MantidQt/MantidWidgets/src/FunctionBrowser.cpp @@ -1264,6 +1264,7 @@ void FunctionBrowser::addFunction() /** * Return the function * @param prop :: Function property + * @param attributesOnly :: Only set attributes */ Mantid::API::IFunction_sptr FunctionBrowser::getFunction(QtProperty* prop, bool attributesOnly) { diff --git a/Code/Mantid/MantidQt/MantidWidgets/src/MWRunFiles.cpp b/Code/Mantid/MantidQt/MantidWidgets/src/MWRunFiles.cpp index 87157889de71..c9992b1262e4 100644 --- a/Code/Mantid/MantidQt/MantidWidgets/src/MWRunFiles.cpp +++ b/Code/Mantid/MantidQt/MantidWidgets/src/MWRunFiles.cpp @@ -276,6 +276,7 @@ QString MWRunFiles::getLabelText() const void MWRunFiles::setLabelText(const QString & text) { m_uiForm.textLabel->setText(text); + m_uiForm.textLabel->setVisible( ! text.isEmpty() ); } /** Set the minimum width on the label widget diff --git a/Code/Mantid/MantidQt/MantidWidgets/src/MuonFitPropertyBrowser.cpp b/Code/Mantid/MantidQt/MantidWidgets/src/MuonFitPropertyBrowser.cpp index b827130e6d43..dc853b2978ae 100644 --- a/Code/Mantid/MantidQt/MantidWidgets/src/MuonFitPropertyBrowser.cpp +++ b/Code/Mantid/MantidQt/MantidWidgets/src/MuonFitPropertyBrowser.cpp @@ -1,6 +1,5 @@ #include "MantidQtMantidWidgets/MuonFitPropertyBrowser.h" #include "MantidQtMantidWidgets/PropertyHandler.h" -#include "MantidQtMantidWidgets/SequentialFitDialog.h" #include "MantidAPI/FunctionFactory.h" // Suppress a warning coming out of code that isn't ours @@ -35,6 +34,7 @@ #include #include +#include namespace MantidQt @@ -42,7 +42,6 @@ namespace MantidQt namespace MantidWidgets { - /** * Constructor * @param parent :: The parent widget - must be an ApplicationWindow @@ -311,6 +310,13 @@ void MuonFitPropertyBrowser::fit() } } +/** + * Show sequential fit dialog. + */ +void MuonFitPropertyBrowser::sequentialFit() +{ + emit sequentialFitRequested(); +} /** * Connect to the AnalysisDataServis when shown @@ -322,17 +328,6 @@ void MuonFitPropertyBrowser::showEvent(QShowEvent* e) populateWorkspaceNames(); } - -/** -* Enable/disable the Fit button. -*/ -void MuonFitPropertyBrowser::setFitEnabled(bool yes) -{ - m_fitActionFit->setEnabled(yes); - m_fitActionSeqFit->setEnabled(false); -} - - /** Check if the workspace can be used in the fit. The accepted types are * MatrixWorkspaces same size and that it isn't the generated raw file. * @param ws :: The workspace @@ -349,6 +344,5 @@ bool MuonFitPropertyBrowser::isWorkspaceValid(Mantid::API::Workspace_sptr ws)con return false; } - } // MantidQt } // API diff --git a/Code/Mantid/MantidQt/MantidWidgets/src/MuonSequentialFitDialog.cpp b/Code/Mantid/MantidQt/MantidWidgets/src/MuonSequentialFitDialog.cpp new file mode 100644 index 000000000000..96a56e001b8c --- /dev/null +++ b/Code/Mantid/MantidQt/MantidWidgets/src/MuonSequentialFitDialog.cpp @@ -0,0 +1,411 @@ +#include "MantidQtMantidWidgets/MuonSequentialFitDialog.h" +#include "MantidQtMantidWidgets/MuonFitPropertyBrowser.h" + +#include "MantidAPI/AnalysisDataService.h" +#include "MantidAPI/AlgorithmProxy.h" + +namespace MantidQt +{ +namespace MantidWidgets +{ + using namespace Mantid::Kernel; + using namespace Mantid::API; + + Logger& MuonSequentialFitDialog::g_log(Logger::get("MuonSequentialFitDialog")); + /** + * Constructor + */ + MuonSequentialFitDialog::MuonSequentialFitDialog(MuonFitPropertyBrowser* fitPropBrowser, + Algorithm_sptr loadAlg) : + QDialog(fitPropBrowser), m_fitPropBrowser(fitPropBrowser), m_loadAlg(loadAlg) + { + m_ui.setupUi(this); + + setState(Stopped); + + // Set initial runs text + Workspace_const_sptr fitWS = m_fitPropBrowser->getWorkspace(); + m_ui.runs->setText( QString::fromStdString( getRunTitle(fitWS) ) + "-" ); + + // TODO: find a better initial one, e.g. previously used + m_ui.labelInput->setText("Label"); + + initDiagnosisTable(); + + // After initial values are set, update depending elements accordingly. We don't rely on + // slot/signal update, as element might be left with default values which means these will + // never be called on initialication. + updateLabelError( m_ui.labelInput->text() ); + updateControlButtonState(); + updateControlButtonType(m_state); + updateInputEnabled(m_state); + + connect( m_ui.labelInput, SIGNAL( textChanged(const QString&) ), + this, SLOT( updateLabelError(const QString&) ) ); + + connect( m_ui.labelInput, SIGNAL( textChanged(const QString&) ), + this, SLOT( updateControlButtonState() ) ); + connect( m_ui.runs, SIGNAL( fileFindingFinished() ), + this, SLOT( updateControlButtonState() ) ); + + connect( this, SIGNAL( stateChanged(DialogState) ), + this, SLOT( updateControlButtonType(DialogState) ) ); + connect( this, SIGNAL( stateChanged(DialogState) ), + this, SLOT( updateInputEnabled(DialogState) ) ); + } + + /** + * Destructor + */ + MuonSequentialFitDialog::~MuonSequentialFitDialog() + {} + + /** + * Checks if specified name is valid as a name for label. + * @param label :: The name to check + * @return Empty string if valid, otherwise a string describing why is invalid + */ + std::string MuonSequentialFitDialog::isValidLabel(const std::string& label) + { + if ( label.empty() ) + return "Can not be empty"; + else + return AnalysisDataService::Instance().isValid(label); + } + + /** + * Returns displayable title for the given workspace; + * @param ws :: Workpspace to get title from + * @return The title, or empty string if unable to get one + */ + std::string MuonSequentialFitDialog::getRunTitle(Workspace_const_sptr ws) + { + auto matrixWS = boost::dynamic_pointer_cast(ws); + + if ( ! matrixWS ) + return ""; + + const std::string& instrName = matrixWS->getInstrument()->getName(); + const int runNumber = matrixWS->getRunNumber(); + + if ( instrName.empty() || runNumber == 0 ) + return ""; + + std::ostringstream runTitle; + runTitle << instrName << runNumber; + return runTitle.str(); + } + + /** + * Initialize diagnosis table. + */ + void MuonSequentialFitDialog::initDiagnosisTable() + { + QStringList headerLabels; + + // Add two static columns + headerLabels << "Run" << "Fit quality"; + + // Add remaining columns - one for every fit function parameter + IFunction_sptr fitFunc = m_fitPropBrowser->getFittingFunction(); + + for(size_t i = 0; i < fitFunc->nParams(); i++) + { + QString paramName = QString::fromStdString( fitFunc->parameterName(i) ); + headerLabels << paramName; + headerLabels << paramName + "_Err"; + } + + m_ui.diagnosisTable->setColumnCount( headerLabels.size() ); + m_ui.diagnosisTable->setHorizontalHeaderLabels(headerLabels); + + // Make the table fill all the available space and columns be resized to fit contents + m_ui.diagnosisTable->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); + + // Make rows alternate bg colors for better user experience + m_ui.diagnosisTable->setAlternatingRowColors(true); + } + + /** + * Add a new entry to the diagnosis table. + * @param runTitle :: Title of the run fitted + * @param fitQuality :: Number representing a goodness of the fit + * @param fittedFunction :: Function containing fitted parameters + */ + void MuonSequentialFitDialog::addDiagnosisEntry(const std::string& runTitle, double fitQuality, + IFunction_sptr fittedFunction) + { + int newRow = m_ui.diagnosisTable->rowCount(); + + m_ui.diagnosisTable->insertRow(newRow); + + QString runTitleDisplay = QString::fromStdString(runTitle); + m_ui.diagnosisTable->setItem( newRow, 0, createTableWidgetItem(runTitleDisplay) ); + + QString fitQualityDisplay = QString::number(fitQuality); + m_ui.diagnosisTable->setItem( newRow, 1, createTableWidgetItem(fitQualityDisplay) ); + + for(int i = 2; i < m_ui.diagnosisTable->columnCount(); i += 2) + { + std::string paramName = m_ui.diagnosisTable->horizontalHeaderItem(i)->text().toStdString(); + size_t paramIndex = fittedFunction->parameterIndex(paramName); + + QString value = QString::number( fittedFunction->getParameter(paramIndex) ); + QString error = QString::number( fittedFunction->getError(paramIndex) ); + + m_ui.diagnosisTable->setItem(newRow, i, createTableWidgetItem(value) ); + m_ui.diagnosisTable->setItem(newRow, i + 1, createTableWidgetItem(error) ); + } + + m_ui.diagnosisTable->scrollToBottom(); + } + + /** + * Helper function to create new item for Diagnosis table. + * @return Created and initialized item with text + */ + QTableWidgetItem* MuonSequentialFitDialog::createTableWidgetItem(const QString& text) + { + auto newItem = new QTableWidgetItem(text); + newItem->setFlags(newItem->flags() ^ Qt::ItemIsEditable); + return newItem; + } + + /** + * Updates visibility/tooltip of label error asterisk. + * @param label :: New label as specified by user + */ + void MuonSequentialFitDialog::updateLabelError(const QString& label) + { + std::string error = isValidLabel( label.toStdString() ); + + m_ui.labelError->setVisible( ! error.empty() ); + m_ui.labelError->setToolTip( QString::fromStdString(error) ); + } + + /** + * Check if all the input field are valid. + * @return True if everything valid, false otherwise + */ + bool MuonSequentialFitDialog::isInputValid() + { + if ( ! m_ui.runs->isValid() ) + return false; + + std::string label = m_ui.labelInput->text().toStdString(); + if ( ! isValidLabel(label).empty() ) + return false; + + return true; + } + + /** + * Enables/disables start button depending on wether we are allowed to start. + */ + void MuonSequentialFitDialog::updateControlButtonState() + { + m_ui.controlButton->setEnabled( isInputValid() ); + } + + /** + * Sets control button to be start/stop depending on new dialog state. + * @param newState :: New state of the dialog + */ + void MuonSequentialFitDialog::updateControlButtonType(DialogState newState) + { + // Disconnect everything connected to pressed() signal of the button + disconnect( m_ui.controlButton, SIGNAL( pressed() ), 0, 0); + + // Connect to appropriate slot + auto buttonSlot = (newState == Stopped) ? SLOT( startFit() ) : SLOT( stopFit() ); + connect( m_ui.controlButton, SIGNAL( pressed() ), this, buttonSlot ); + + // Set appropriate text + QString buttonText = (newState == Stopped) ? "Start" : "Stop"; + m_ui.controlButton->setText(buttonText); + } + + /** + * Updates current state of the dialog. + */ + void MuonSequentialFitDialog::setState(DialogState newState) + { + m_state = newState; + emit stateChanged(newState); + } + + /** + * Update enabled state off all the input widgets depending on new dialog state. + * @param newState :: New state of the dialog + */ + void MuonSequentialFitDialog::updateInputEnabled(DialogState newState) + { + bool enabled = (newState == Stopped); + + m_ui.runs->setEnabled(enabled); + m_ui.labelInput->setEnabled(enabled); + + foreach(QAbstractButton* button, m_ui.paramTypeGroup->buttons()) + button->setEnabled(enabled); + } + + /** + * Start fitting process. + */ + void MuonSequentialFitDialog::startFit() + { + if ( m_state == Running ) + throw std::runtime_error("Couln't start: already running"); + + const std::string label = m_ui.labelInput->text().toStdString(); + const std::string labelGroupName = "MuonSeqFit_" + label; + + AnalysisDataServiceImpl& ads = AnalysisDataService::Instance(); + + if ( ads.doesExist(labelGroupName) ) + { + QMessageBox::StandardButton answer = QMessageBox::question(this, "Label already exists", + "Label you specified was used for one of the previous fits. Do you want to overwrite it?", + QMessageBox::Yes | QMessageBox::Cancel); + + if ( answer != QMessageBox::Yes ) + return; + + ads.deepRemoveGroup(labelGroupName); + } + + // Create a group for label + ads.add(labelGroupName, boost::make_shared()); + + QStringList runFilenames = m_ui.runs->getFilenames(); + + // Tell progress bar how many iterations we will need to make and reset it + m_ui.progress->setRange( 0, runFilenames.size() ); + m_ui.progress->setFormat("%p%"); + m_ui.progress->setValue(0); + + // Clear diagnosis table for new fit + m_ui.diagnosisTable->setRowCount(0); + + // Get fit function as specified by user in the fit browser + IFunction_sptr fitFunction = FunctionFactory::Instance().createInitialized( + m_fitPropBrowser->getFittingFunction()->asString() ); + + // Whether we should use initial function for every fit + bool useInitFitFunction = (m_ui.paramTypeGroup->checkedButton() == m_ui.paramTypeInitial); + + setState(Running); + m_stopRequested = false; + + for ( auto fileIt = runFilenames.constBegin(); fileIt != runFilenames.constEnd(); ++fileIt ) + { + // Process events (so that Stop button press is processed) + QApplication::processEvents(); + + // Stop if requested by user + if ( m_stopRequested ) + break; + + MatrixWorkspace_sptr ws; + + auto load = boost::dynamic_pointer_cast( AlgorithmManager::Instance().create("MuonLoad") ); + load->setChild(true); + load->setRethrows(true); + load->copyPropertiesFrom(*m_loadAlg); + + try + { + load->initialize(); + + load->setPropertyValue( "Filename", fileIt->toStdString() ); + load->setPropertyValue( "OutputWorkspace", "__YouDontSeeMeIAmNinja" ); // Is not used + + if ( m_fitPropBrowser->rawData() ) // TODO: or vice verca? + load->setPropertyValue( "RebinParams", "" ); + + load->execute(); + + ws = load->getProperty("OutputWorkspace"); + } + catch(...) + { + QMessageBox::critical(this, "Loading failed", + "Unable to load one of the files.\n\nCheck log for details"); + break; + } + + const std::string runTitle = getRunTitle(ws); + const std::string wsBaseName = labelGroupName + "_" + runTitle; + + ads.add(wsBaseName, ws); + ads.addToGroup(labelGroupName, wsBaseName); + + IFunction_sptr functionToFit; + + if ( useInitFitFunction ) + // Create a copy so that the original function is not changed + functionToFit = FunctionFactory::Instance().createInitialized( fitFunction->asString() ); + else + // Use the same function over and over, so that previous fitted params are used for the next fit + functionToFit = fitFunction; + + IAlgorithm_sptr fit = AlgorithmManager::Instance().create("Fit"); + fit->setRethrows(true); + + try + { + + // Set function. Gets updated when fit is done. + fit->setProperty("Function", functionToFit); + + fit->setProperty("InputWorkspace", ws); + fit->setProperty("Output", wsBaseName); + + // We should have one spectra only in the workspace, so use the first one. + fit->setProperty("WorkspaceIndex", 0); + + // Various properties from the fit prop. browser + fit->setProperty("StartX", m_fitPropBrowser->startX()); + fit->setProperty("EndX", m_fitPropBrowser->endX()); + fit->setProperty("Minimizer", m_fitPropBrowser->minimizer()); + fit->setProperty("CostFunction", m_fitPropBrowser->costFunction()); + + fit->execute(); + } + catch(...) + { + QMessageBox::critical(this, "Fitting failed", + "Unable to fit one of the files.\n\nCheck log for details"); + break; + } + + // Make sure created fit workspaces end-up in the group + // TODO: this really should use loop + ads.addToGroup(labelGroupName, wsBaseName + "_NormalisedCovarianceMatrix"); + ads.addToGroup(labelGroupName, wsBaseName + "_Parameters"); + ads.addToGroup(labelGroupName, wsBaseName + "_Workspace"); + + // Add information about the fit to the diagnosis table + addDiagnosisEntry(runTitle, fit->getProperty("OutputChi2OverDof"), functionToFit); + + // Update progress + m_ui.progress->setFormat("%p% - " + QString::fromStdString(runTitle) ); + m_ui.progress->setValue( m_ui.progress->value() + 1 ); + } + + setState(Stopped); + } + + /** + * Stop fitting process. + */ + void MuonSequentialFitDialog::stopFit() + { + if ( m_state != Running ) + throw std::runtime_error("Couldn't stop: is not running"); + + m_stopRequested = true; + } + +} // namespace MantidWidgets +} // namespace Mantid diff --git a/Code/Mantid/MantidQt/SliceViewer/src/LineViewer.cpp b/Code/Mantid/MantidQt/SliceViewer/src/LineViewer.cpp index 3ab77e1ac0a8..c72ea7df7b82 100644 --- a/Code/Mantid/MantidQt/SliceViewer/src/LineViewer.cpp +++ b/Code/Mantid/MantidQt/SliceViewer/src/LineViewer.cpp @@ -947,8 +947,8 @@ int LineViewer::getPlotAxis() const /** * Helper method to get the positive min value. - * @param curveDat : CurveData to look through the data of. - * @param to : Start value + * @param curveData : CurveData to look through the data of. + * @param from : Start value * @return : Positive min value. */ double getPositiveMin(const MantidQwtWorkspaceData& curveData, const double from) diff --git a/Code/Mantid/MantidQt/SliceViewer/src/PeaksWorkspaceWidget.cpp b/Code/Mantid/MantidQt/SliceViewer/src/PeaksWorkspaceWidget.cpp index e4fd5c4de86a..4ae39afc1aec 100644 --- a/Code/Mantid/MantidQt/SliceViewer/src/PeaksWorkspaceWidget.cpp +++ b/Code/Mantid/MantidQt/SliceViewer/src/PeaksWorkspaceWidget.cpp @@ -167,7 +167,6 @@ namespace MantidQt /** Handler to hide/show the widget on request. - @param hidden: flag indicating what to do. */ void PeaksWorkspaceWidget::onToggleHideInPlot() { diff --git a/Code/Mantid/MantidQt/SpectrumViewer/src/SpectrumView.cpp b/Code/Mantid/MantidQt/SpectrumViewer/src/SpectrumView.cpp index 4057829afe05..12297305815a 100644 --- a/Code/Mantid/MantidQt/SpectrumViewer/src/SpectrumView.cpp +++ b/Code/Mantid/MantidQt/SpectrumViewer/src/SpectrumView.cpp @@ -25,7 +25,7 @@ namespace SpectrumView * parts of the SpectrumView are constructed here and are deleted when the * SpectrumView destructor is called. * - * @param data_source The source of the data that will be displayed. + * @param parent Top-level widget for object. */ SpectrumView::SpectrumView(QWidget *parent) : QMainWindow(parent, 0), diff --git a/Code/Mantid/instrument/Facilities.xml b/Code/Mantid/instrument/Facilities.xml index fc6af65a4a50..c052b6a9f946 100644 --- a/Code/Mantid/instrument/Facilities.xml +++ b/Code/Mantid/instrument/Facilities.xml @@ -7,7 +7,7 @@ - + diff --git a/Code/Mantid/instrument/LET_Parameters.xml b/Code/Mantid/instrument/LET_Parameters.xml index 805c0dfdc219..02f28044bd03 100644 --- a/Code/Mantid/instrument/LET_Parameters.xml +++ b/Code/Mantid/instrument/LET_Parameters.xml @@ -28,6 +28,11 @@ + + + + + diff --git a/Code/Mantid/instrument/MAPS_Parameters.xml b/Code/Mantid/instrument/MAPS_Parameters.xml index e651324cc82a..332e71d7c7b3 100644 --- a/Code/Mantid/instrument/MAPS_Parameters.xml +++ b/Code/Mantid/instrument/MAPS_Parameters.xml @@ -28,6 +28,11 @@ + + + + + diff --git a/Code/Mantid/instrument/MARI_Parameters.xml b/Code/Mantid/instrument/MARI_Parameters.xml index b75425f2d0c2..042911c94b22 100644 --- a/Code/Mantid/instrument/MARI_Parameters.xml +++ b/Code/Mantid/instrument/MARI_Parameters.xml @@ -29,6 +29,10 @@ + + + + diff --git a/Code/Mantid/instrument/MERLIN_Parameters.xml b/Code/Mantid/instrument/MERLIN_Parameters.xml index ddced9473df2..c98eca1fd56e 100644 --- a/Code/Mantid/instrument/MERLIN_Parameters.xml +++ b/Code/Mantid/instrument/MERLIN_Parameters.xml @@ -28,6 +28,11 @@ + + + + + diff --git a/Code/Mantid/instrument/VISION_Definition.xml b/Code/Mantid/instrument/VISION_Definition.xml deleted file mode 100644 index 2305ea4e9664..000000000000 --- a/Code/Mantid/instrument/VISION_Definition.xml +++ /dev/null @@ -1,4477 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Code/Mantid/instrument/VISION_Definition_20131021-.xml b/Code/Mantid/instrument/VISION_Definition_20131021-.xml index 84b40c2281ae..21fb59cdf00b 100644 --- a/Code/Mantid/instrument/VISION_Definition_20131021-.xml +++ b/Code/Mantid/instrument/VISION_Definition_20131021-.xml @@ -4390,7 +4390,7 @@
- + diff --git a/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMaps_Simple.py b/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMaps_Simple.py deleted file mode 100644 index b975b9d991d8..000000000000 --- a/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMaps_Simple.py +++ /dev/null @@ -1,72 +0,0 @@ -# -# TUBE CALIBRATION DEMONSTRATION PROGRAM FOR MAPS -# -# This is a simple example for running calibration for and calibration run of MAPS. -# It uses the CalibrateMaps function - -# -import tube -from tube_calib_fit_params import TubeCalibFitParams - -def CalibrateMaps( RunNumber ): - ''' - RunNumber is the run number for the calibration. - ''' - - # == Set parameters for calibration == - previousDefaultInstrument = config['default.instrument'] - config['default.instrument']="MAPS" - filename = str(RunNumber) # Name of calibration run - print "Filename",filename - rangeLower = 2000 # Integrate counts in each spectra from rangeLower to rangeUpper - rangeUpper = 10000 # - - # Set initial parameters for peak finding - ExpectedHeight = -1000.0 # Expected Height of Gaussian Peaks (initial value of fit parameter) - ExpectedWidth = 8.0 # Expected width of Gaussian peaks in pixels (initial value of fit parameter) - ExpectedPositions = [4.0, 85.0, 128.0, 165.0, 252.0] #Expected positions of the edges and Gaussian peaks in pixels (initial values of fit parameters) - - # Set what we want to calibrate (e.g whole intrument or one door ) - CalibratedComponent = 'MAPS' # Calibrate all - - - # Get calibration raw file and integrate it - rawCalibInstWS = Load(filename) #'raw' in 'rawCalibInstWS' means unintegrated. - print "Integrating Workspace" - CalibInstWS = Integration( rawCalibInstWS, RangeLower=rangeLower, RangeUpper=rangeUpper ) - DeleteWorkspace(rawCalibInstWS) - print "Created workspace (CalibInstWS) with integrated data from run and instrument to calibrate" - - # == Create Objects needed for calibration == - - # First array gives positions in Metres and second array gives type 1=Gaussian peak 2=edge. - # An intelligent guess is used here that is not correct for all tubes. - knownPos, funcForm = [-0.50,-0.16,-0.00, 0.16, 0.50 ],[2,1,1,1,2] - - # Get fitting parameters - fitPar = TubeCalibFitParams( ExpectedPositions, ExpectedHeight, ExpectedWidth) - fitPar.setAutomatic(True) - - print "Created objects needed for calibration." - - # == Get the calibration and put results into calibration table == - calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, - knownPos, funcForm, fitPar = fitPar, - outputPeak=True) - print "Got calibration (new positions of detectors) " - - # == Apply the Calibation == - ApplyCalibration( Workspace=CalibInstWS, PositionTable=calibrationTable) - print "Applied calibration" - - - # == Save workspace == - SaveNexusProcessed( CalibInstWS, 'TubeCalibDemoMapsResult.nxs',"Result of Running MAPS Calibration") - print "saved calibrated workspace (CalibInstWS) into Nexus file TubeCalibDemoMapsResult.nxs" - - # == Reset dafault instrument == - config['default.instrument'] = previousDefaultInstrument - - # ==== End of CalibrateMaps() ==== - -CalibrateMaps( 14919 ) #found at \\isis\inst$\NDXMAPS\Instrument\data\cycle_09_5 diff --git a/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMerlin_Adjustable.py b/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMerlin_Adjustable.py deleted file mode 100644 index f0cd75cc81b0..000000000000 --- a/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMerlin_Adjustable.py +++ /dev/null @@ -1,100 +0,0 @@ -# -# TUBE CALIBRATION DEMONSTRATION PROGRAM FOR MERLIN -# -# This is example that allows some calibration slit peaks to be adjusted. -# First execute the part from thwe beginning to the end of the CalibrateMerlin function -# then the later parts. -# -# Here we run the calibration of MERLIN or selected part of MERLIN -# (excluding short tubes of door 3) -# This calibration is organised by the function CalibrateMerlin, -# which takes a string consisting of the run number of a calibration run number as argument. -# The workspace with calibrated instrument is saved to a Nexus file -# -import tube -from tube_calib_fit_params import * # To handle fit parameters -import os - -def CalibrateMerlin( RunNumber, UsePeakFile=False ): -# Run number must include any leading zeros that appear in the file name of the run. - - # == Set parameters for calibration == - - # File details - filename = 'MER'+str(RunNumber) # Name of calibration run - rangeLower = 3000 # Integrate counts in each spectra from rangeLower to rangeUpper - rangeUpper = 20000 # - - # Set parameters for ideal tube. - Left = 2.0 # Where the left end of tube should be in pixels (target for AP) - Centre = 512.5 # Where the centre of the tube should be in pixels (target for CP) - Right = 1023.0 # Where the right of the tube should be in pxels (target for BP) - ActiveLength = 2.9 # Active length of tube in Metres - - # Set initial parameters for peak finding - ExpectedHeight = 1000.0 # Expected Height of Peaks (initial value of fit parameter) - ExpectedWidth = 32.0 # Expected width of centre peak (initial value of fit parameter) - ExpectedPositions = [35.0, 512.0, 989.0] # Expected positions of the edges and peak (initial values of fit parameters) - - # Set what we want to calibrate (e.g whole intrument or one door ) - CalibratedComponent = 'MERLIN/door8' # Calibrate whole instrument - - # Get calibration raw file and integrate it - rawCalibInstWS = LoadRaw(filename) #'raw' in 'rawCalibInstWS' means unintegrated. - print "Integrating Workspace" - CalibInstWS = Integration( rawCalibInstWS, RangeLower=rangeLower, RangeUpper=rangeUpper ) - DeleteWorkspace(rawCalibInstWS) - print "Created workspace (CalibInstWS) with integrated data from run and instrument to calibrate" - - # == Create Objects needed for calibration == - - # Get ideal tube - # the known positions are given in pixels inside the tubes and transformed to provide the positions - # with the center of the tube as the origin - knownPositions = ActiveLength*(numpy.array([ Left, Right, Centre])/1024 - 0.5) - funcForm = [1,1,1] - - print "Created objects needed for calibration." - - # == Get the calibration and put results into calibration table == - # also put peaks into PeakFile - saveDirectory = config['defaultsave.directory'] - peakFileName = "TubeCalibDemoMerlin_Peaks.txt" - - # pass more parameters to tube.calibrate by name - extra_options = dict() - extra_options['excludeShortTubes']=ActiveLength - extra_options['outputPeak']=True - if(not UsePeakFile): - # Get fitting parameters - fitPar = TubeCalibFitParams( ExpectedPositions, ExpectedHeight, ExpectedWidth ) - fitPar.setAutomatic(True) - extra_options['fitPar'] = fitPar - - - calibrationTable, peakTable = tube.calibrate(CalibInstWS, CalibratedComponent, knownPositions, funcForm, - **extra_options) - - peakFileName = "TubeCalibDemoMerlin_Peaks.txt" - if UsePeakFile: - tube.savePeak(peakTable, peakFileName) - print " Put slit peaks into file",peakFileName, "in save directory",saveDirectory,"." - - print "Got calibration (new positions of detectors)" - - # == Apply the Calibation == - ApplyCalibration( Workspace=CalibInstWS, PositionTable=calibrationTable) - print "Applied calibration" - - # == Save workspace == - SaveNexusProcessed( CalibInstWS, 'TubeCalibDemoMerlinResult.nxs',"Result of Running TubeCalibDemoMerlin_Adjustable.py") - print "saved calibrated workspace (CalibInstWS) into Nexus file TubeCalibDemoMerlinResult.nxs in save directory",saveDirectory,"." - - # ==== End of CalibrateMerlin() ==== - # INITIALLY EXECUTE THE CODE FROM THE BEGINNING TO HERE, THEN EACH OF THE TWO CALLS BELOW IN ORDER SEPARATELY - -# Run this one first -CalibrateMerlin( 12024 ) - -#Then edit the'TubeCalibDemoMerlin_Peaks.txt' file, then run -CalibrateMerlin( 12024, True) diff --git a/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMerlin_Simple.py b/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMerlin_Simple.py index 0a0bcd00ab75..d4b6727f29c9 100644 --- a/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMerlin_Simple.py +++ b/Code/Mantid/scripts/Calibration/Examples/TubeCalibDemoMerlin_Simple.py @@ -13,6 +13,7 @@ import tube from tube_calib_fit_params import TubeCalibFitParams +import numpy RunNumber = 12024 def CalibrateMerlin(RunNumber): diff --git a/Code/Mantid/scripts/Inelastic/IndirectEnergyConversion.py b/Code/Mantid/scripts/Inelastic/IndirectEnergyConversion.py index 2404293d431c..c40c31fe9943 100644 --- a/Code/Mantid/scripts/Inelastic/IndirectEnergyConversion.py +++ b/Code/Mantid/scripts/Inelastic/IndirectEnergyConversion.py @@ -63,7 +63,13 @@ def resolution(files, iconOpt, rebinParam, bground, reducer.set_parameter_file(parfile) reducer.set_grouping_policy('All') reducer.set_sum_files(True) - reducer.reduce() + + try: + reducer.reduce() + except Exception, e: + logger.error(str(e)) + return + iconWS = reducer.get_result_workspaces()[0] if factor != None: diff --git a/Code/Mantid/scripts/Inelastic/dgreduce.py b/Code/Mantid/scripts/Inelastic/dgreduce.py index 05b8302fb01c..752b6cd4bee0 100644 --- a/Code/Mantid/scripts/Inelastic/dgreduce.py +++ b/Code/Mantid/scripts/Inelastic/dgreduce.py @@ -257,8 +257,11 @@ def arb_units(wb_run,sample_run,ei_guess,rebin,map_file='default',monovan_run=No if Reducer.mask_run == None : mask_run=sample_run - + masking = None; masks_done=False + if not Reducer.run_diagnostics: + header="Diagnostics skipped " + masks_done = True; if Reducer.save_and_reuse_masks : raise NotImplementedError("Save and reuse masks option is not yet implemented") mask_file_name = common.create_resultname(str(mask_run),Reducer.instr_name,'_masks.xml') @@ -268,7 +271,7 @@ def arb_units(wb_run,sample_run,ei_guess,rebin,map_file='default',monovan_run=No #Reducer.hard_mask_file = mask_full_file; #Reducer.use_hard_mask_only = True masks_done=True - header="Masking loaded " + header="Masking fully skipped and processed {0} spectra and {1} bad spectra " else: pass #------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -278,7 +281,7 @@ def arb_units(wb_run,sample_run,ei_guess,rebin,map_file='default',monovan_run=No if not masks_done: masking = Reducer.diagnose(wb_run,sample = mask_run, second_white = None,print_results=True) - header = "Diag Processed " + header = "Diag Processed workspace with {0:d} spectra and masked {1:d} bad spectra" # Calculate absolute units: @@ -308,7 +311,7 @@ def arb_units(wb_run,sample_run,ei_guess,rebin,map_file='default',monovan_run=No failed_sp_list,nSpectra = get_failed_spectra_list_from_masks(masking) nMaskedSpectra = len(failed_sp_list) # this tells turkey in case of hard mask only but everythin else semems work fine - print '{0} workspace with {1:d} spectra and masked {2:d} bad spectra'.format(header,nSpectra,nMaskedSpectra) + print header.format(nSpectra,nMaskedSpectra) #Run the conversion first on the sample deltaE_wkspace_sample = Reducer.convert_to_energy(sample_run, ei_guess, wb_run) @@ -701,6 +704,9 @@ def get_failed_spectra_list_from_masks(masking_wksp): masking_wksp = mtd[masking_wksp] failed_spectra = [] + if masking_wksp is None: + return (failed_spectra,0); + n_spectra = masking_wksp.getNumberHistograms() for i in xrange(n_spectra): if masking_wksp.readY(i)[0] >0.99 : # spectrum is masked diff --git a/Code/Mantid/scripts/Inelastic/inelastic_indirect_reduction_steps.py b/Code/Mantid/scripts/Inelastic/inelastic_indirect_reduction_steps.py index 0dcc9499f790..fe6983a63f08 100644 --- a/Code/Mantid/scripts/Inelastic/inelastic_indirect_reduction_steps.py +++ b/Code/Mantid/scripts/Inelastic/inelastic_indirect_reduction_steps.py @@ -521,7 +521,10 @@ def _unwrap_monitor(self, ws): unwrapped_ws, join = UnwrapMonitor(InputWorkspace=monitor, OutputWorkspace=monitor, LRef=l_ref) RemoveBins(InputWorkspace=monitor,OutputWorkspace= monitor,XMin= join-0.001,XMax= join+0.001, Interpolation='Linear') - FFTSmooth(InputWorkspace=monitor,OutputWorkspace=monitor,WorkspaceIndex=0) + try: + FFTSmooth(InputWorkspace=monitor,OutputWorkspace=monitor,WorkspaceIndex=0) + except ValueError: + raise ValueError("Indirect Energy Conversion does not support uneven bin widths.") def _get_reference_length(self, ws, index): workspace = mtd[ws] diff --git a/Code/Mantid/scripts/Interface/ui/reflectometer/refl_gui.py b/Code/Mantid/scripts/Interface/ui/reflectometer/refl_gui.py index 7e3b805a52c5..5f62f87e7afc 100644 --- a/Code/Mantid/scripts/Interface/ui/reflectometer/refl_gui.py +++ b/Code/Mantid/scripts/Interface/ui/reflectometer/refl_gui.py @@ -2,6 +2,7 @@ #so this file provides any extra GUI tweaks not easily doable in the designer #for the time being this also includes non-GUI behaviour import refl_window +import refl_save import csv from PyQt4 import QtCore, QtGui from mantid.simpleapi import * @@ -496,12 +497,15 @@ def reloadTable(self): self.loading = False self.windowRefl.modFlag = False def saveWorkspaces(self): - #Dialog = QtGui.QDialog() - #u = Ui_SaveWindow() - #u.setupUi(Dialog) - #Dialog.exec_() - #print "Disabled until decision about old dialog has been reached" - print "Disabled - Run desired save algorithm from main MantidPlot window instead" + try: + Dialog = QtGui.QDialog() + u = refl_save.Ui_SaveWindow() + u.setupUi(Dialog) + Dialog.exec_() + except Exception as ex: + logger.notice("Could not open save workspace dialog") + logger.notice(str(ex)) + #print "Disabled - Run desired save algorithm from main MantidPlot window instead" def showHelp(self): import webbrowser webbrowser.open('http://www.mantidproject.org/ISIS_Reflectometry_GUI') diff --git a/Code/Mantid/scripts/Interface/ui/reflectometer/refl_save.py b/Code/Mantid/scripts/Interface/ui/reflectometer/refl_save.py new file mode 100644 index 000000000000..b038e7b6d3f8 --- /dev/null +++ b/Code/Mantid/scripts/Interface/ui/reflectometer/refl_save.py @@ -0,0 +1,478 @@ +from PyQt4 import QtCore, QtGui +from mantid.simpleapi import * +from mantid.api import WorkspaceGroup +import xml.etree.ElementTree as xml +from isis_reflectometry.quick import * +from isis_reflectometry.procedures import * +from isis_reflectometry.combineMulti import * +from isis_reflgui.settings import * + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + _fromUtf8 = lambda s: s + +class Ui_SaveWindow(object): + def __init__(self): + + self.__instrument = config['default.instrument'].strip().upper() + + usersettings = Settings() # This will throw a missing config exception if no config file is available. + try: + self.__mountpoint = usersettings.get_named_setting("DataMountPoint") + except KeyError: + print "DataMountPoint is missing from the config.xml file." + raise + + def setupUi(self, SaveWindow): + self.SavePath="" + SaveWindow.setObjectName(_fromUtf8("SaveWindow")) + SaveWindow.resize(700, 450) + SaveWindow.setAcceptDrops(True) + + self.centralWidget = QtGui.QWidget(SaveWindow) + self.centralWidget.setObjectName(_fromUtf8("centralWidget")) + self.gridLayout_2 = QtGui.QGridLayout(self.centralWidget) + self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) + self.gridLayout = QtGui.QGridLayout() + self.gridLayout.setSizeConstraint(QtGui.QLayout.SetNoConstraint) + self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + +# Path label and edit field + self.PathLabel = QtGui.QLabel("Save path: ",self.centralWidget) + self.gridLayout.addWidget(self.PathLabel,0,2,1,1) + self.lineEdit = QtGui.QLineEdit(self.centralWidget) + font = QtGui.QFont() + font.setWeight(75) + font.setBold(False) + self.lineEdit.setFont(font) + self.lineEdit.setObjectName(_fromUtf8("lineEdit")) + self.gridLayout.addWidget(self.lineEdit, 0, 3, 1, 3) + #print QtGui.QMainWindow.findChild(QtGui.QMainWindow.QLabel,'RBEdit') + +# Prefix label and edit field + self.PrefixLabel = QtGui.QLabel("Prefix: ",self.centralWidget) + self.gridLayout.addWidget(self.PrefixLabel,0,6,1,1) + self.lineEdit2 = QtGui.QLineEdit(self.centralWidget) + self.lineEdit2.setFont(font) + self.lineEdit2.setObjectName(_fromUtf8("lineEdit2")) + self.gridLayout.addWidget(self.lineEdit2, 0, 7, 1, 2) + + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth()) + self.lineEdit.setSizePolicy(sizePolicy) + self.lineEdit2.setSizePolicy(sizePolicy) + + self.ListLabel = QtGui.QLabel("List of workspaces: ",self.centralWidget) + self.gridLayout.addWidget(self.ListLabel,1,2,1,3) + + self.LogsLabel = QtGui.QLabel("List of logged parameters: ",self.centralWidget) + self.gridLayout.addWidget(self.LogsLabel,1,6,1,3) + +# List of workspaces + self.listWidget = QtGui.QListWidget(self.centralWidget) + self.listWidget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth()) + self.gridLayout.addWidget(self.listWidget, 2, 2, 1, 3) + +# List of Logged Parameters + self.listWidget2 = QtGui.QListWidget(self.centralWidget) + self.listWidget2.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.listWidget.sizePolicy().hasHeightForWidth()) + self.gridLayout.addWidget(self.listWidget2, 2, 6, 1, 3) + + spacerItem = QtGui.QSpacerItem(20, 20, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) + self.gridLayout.addItem(spacerItem, 2, 5, 1, 1) + spacerItem1 = QtGui.QSpacerItem(20, 20, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + self.gridLayout.addItem(spacerItem1, 4, 2, 1, 1) + spacerItem2 = QtGui.QSpacerItem(20, 20, QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum) + self.gridLayout.addItem(spacerItem2, 2, 0, 1, 1) + self.pushButton = QtGui.QPushButton(self.centralWidget) + +# Save Title + self.titleCheckBox = QtGui.QCheckBox("Title", self.centralWidget) + #self.gridLayout.addWidget(self.titleCheckBox, 3, 6, 1, 1) + +# Tab check box + #self.tabCheckBox = QtGui.QCheckBox("tab", self.centralWidget) + #self.gridLayout.addWidget(self.titleCheckBox, 3, 6, 1, 1) + +# Comma check box + #self.commaCheckBox = QtGui.QCheckBox("comma", self.centralWidget) + #self.gridLayout.addWidget(self.commaCheckBox, 3, 6, 1, 1) + + +# Space check box + #self.spaceCheckBox = QtGui.QCheckBox("space", self.centralWidget) + #self.gridLayout.addWidget(self.commaCheckBox, 3, 6, 1, 1) + +# Save XError + self.xErrorCheckBox = QtGui.QCheckBox("Q resolution", self.centralWidget) + #self.gridLayout.addWidget(self.xErrorCheckBox, 3, 7, 1, 1) + +# separator + #self.separatorLabel = QtGui.QLabel("Separator: ", self.centralWidget) + #self.gridLayout.addWidget(self.separatorLabel,4,6,1,1) + #self.separatorEdit = QtGui.QLineEdit(self.centralWidget) + #self.separatorEdit.setObjectName(_fromUtf8("separatorEdit")) + #self.gridLayout.addWidget(self.separatorEdit, 4, 7, 1, 1) + + self.groupBox = QtGui.QGroupBox("Custom format options") + self.vbox = QtGui.QVBoxLayout() + self.hbox = QtGui.QHBoxLayout() + self.vbox.addWidget(self.titleCheckBox) + self.vbox.addWidget(self.xErrorCheckBox) + + self.groupBox2 = QtGui.QGroupBox("Separator") + #self.buttonGroup=QtGui.QButtonGroup("Separator:", self.groupBox) + self.radio1=QtGui.QRadioButton("Comma", self.centralWidget) + self.radio2=QtGui.QRadioButton("Space", self.centralWidget) + self.radio3=QtGui.QRadioButton("Tab", self.centralWidget) + + self.radio1.setChecked(1) + self.hbox.addWidget(self.radio1) + self.hbox.addWidget(self.radio2) + self.hbox.addWidget(self.radio3) + self.groupBox2.setLayout(self.hbox) + # self.hbox.addWidget(self.separatorLabel) + # self.hbox.addWidget(self.commaCheckBox) + # self.hbox.addWidget(self.spaceCheckBox) + # self.hbox.addWidget(self.tabCheckBox) + + self.vbox.addWidget(self.groupBox2) + self.vbox.addStretch(1) + #self.groupBox.setCheckable(1) + self.groupBox.setLayout(self.vbox) + self.gridLayout.addWidget(self.groupBox, 3, 6, 3, 3) + +# spectralist + self.spectraLabel = QtGui.QLabel("Spectra list: ", self.centralWidget) + self.gridLayout.addWidget(self.spectraLabel,3,2,1,1) + self.spectraEdit = QtGui.QLineEdit(self.centralWidget) + self.spectraEdit.setObjectName(_fromUtf8("spectraEdit")) + self.gridLayout.addWidget(self.spectraEdit, 3, 3, 1, 1) + +# file format selector + self.fileFormatLabel = QtGui.QLabel("File format: ", self.centralWidget) + self.gridLayout.addWidget(self.fileFormatLabel,5,2,1,1) + self.comboBox = QtGui.QComboBox(self.centralWidget) + self.comboBox.setToolTip("Please select the file format") + self.comboBox.setStatusTip("Please select the file format") + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.comboBox.sizePolicy().hasHeightForWidth()) + self.comboBox.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setWeight(75) + font.setBold(True) + self.comboBox.setFont(font) + self.comboBox.setObjectName(_fromUtf8("comboBox")) + self.comboBox.addItem(_fromUtf8("Custom format (*.dat)")) + self.comboBox.addItem(_fromUtf8("3 column (*.dat)")) + self.comboBox.addItem(_fromUtf8("ANSTO, MotoFit, 4 column (*.txt)")) + self.comboBox.addItem(_fromUtf8("ILL Cosmos (*.mft)")) + self.gridLayout.addWidget(self.comboBox, 5, 3, 1, 1) + + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.pushButton.sizePolicy().hasHeightForWidth()) + self.pushButton.setSizePolicy(sizePolicy) + self.pushButton.setObjectName(_fromUtf8("pushButton")) + self.gridLayout.addWidget(self.pushButton, 8, 2, 1, 1) + spacerItem3 = QtGui.QSpacerItem(20, 28, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + self.gridLayout.addItem(spacerItem3, 8, 3, 1, 1) + self.pushButton_2 = QtGui.QPushButton(self.centralWidget) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.pushButton_2.sizePolicy().hasHeightForWidth()) + self.pushButton_2.setSizePolicy(sizePolicy) + self.pushButton_2.setObjectName(_fromUtf8("pushButton_2")) + self.gridLayout.addWidget(self.pushButton_2, 8, 4, 1, 1) + + self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1) + + self.retranslateUi(SaveWindow) + self.populateList() + #self.workspaceSelected() + QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL(_fromUtf8("clicked()")), self.buttonClickHandler1) + QtCore.QObject.connect(self.pushButton_2, QtCore.SIGNAL(_fromUtf8("clicked()")), self.populateList) + QtCore.QObject.connect(self.lineEdit, QtCore.SIGNAL(_fromUtf8("textChanged()")), self.setPath) + QtCore.QObject.connect(self.comboBox, QtCore.SIGNAL(_fromUtf8("activated(QString)")), self.on_comboBox_Activated) + QtCore.QObject.connect(self.listWidget, QtCore.SIGNAL(_fromUtf8("itemActivated(QListWidgetItem*)")), self.workspaceSelected) + # QtCore.QObject.connect(self.actionSave_table, QtCore.SIGNAL(_fromUtf8("triggered()")), self.saveDialog) + # QtCore.QObject.connect(self.actionLoad_table, QtCore.SIGNAL(_fromUtf8("triggered()")), self.loadDialog) + QtCore.QMetaObject.connectSlotsByName(SaveWindow) + + def retranslateUi(self, SaveWindow): + SaveWindow.setWindowTitle(QtGui.QApplication.translate("SaveWindow", "SaveWindow", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton.setText(QtGui.QApplication.translate("SaveWindow", "SAVE", None, QtGui.QApplication.UnicodeUTF8)) + self.pushButton_2.setText(QtGui.QApplication.translate("SaveWindow", "Refresh", None, QtGui.QApplication.UnicodeUTF8)) + + def setPath(): + self.SavePath=self.lineEdit.text() + + def workspaceSelected(self): + self.listWidget2.clear() + #self.listWidget.setCurrentRow(0) + print str(self.listWidget.currentItem().text()) + logs = mtd[str(self.listWidget.currentItem().text())].getRun().getLogData() + for i in range(0,len(logs)): + self.listWidget2.addItem(logs[i].name) + + def on_comboBox_Activated(self): + print "" + + def populateList(self): + self.listWidget.clear() + names = mtd.getObjectNames() + if len(names): + RB_Number=groupGet(names[0],'samp','rb_proposal') + for ws in names: + self.listWidget.addItem(ws) + + self.listWidget.setCurrentItem(self.listWidget.item(0)) + # try to get correct user directory + currentInstrument=config['default.instrument'] + if (self.SavePath!=''): + self.lineEdit.setText(self.SavePath) + else: + base_path = os.path.join(self.__mountpoint, 'NDX'+ self.__instrument, 'Instrument','logs','journal') + print base_path + main_journal_path = os.path.join(base_path, 'journal_main.xml') + tree1=xml.parse(main_journal_path) + root1=tree1.getroot() + currentJournal=root1[len(root1)-1].attrib.get('name') + cycle_journal_path = os.path.join(base_path, currentJournal) + tree=xml.parse(cycle_journal_path) + root=tree.getroot() + + i=0 + try: + while root[i][4].text!=str(RB_Number): + i+=1 + if root[i][1].text.find(',')>0: + user=root[i][1].text[0:root[i][1].text.find(',')] + else: + user=root[i][1].text[0:root[i][1].text.find(' ')] + SavePath = os.path.join('U:', user) + self.lineEdit.setText(SavePath) + except LookupError: + print "Not found!" + +#--------- If "Save" button pressed, selcted workspaces are saved ------------- + def buttonClickHandler1(self): + names = mtd.getObjectNames() + dataToSave=[] + prefix = str(self.lineEdit2.text()) + if not (self.lineEdit.text() and os.path.exists(self.lineEdit.text())): + logger.notice("Directory specified doesn't exist or was invalid for your operating system") + QtGui.QMessageBox.critical(self.lineEdit, 'Could not save',"Directory specified doesn't exist or was invalid for your operating system") + return + print self.listWidget.selectedItems() + for idx in self.listWidget.selectedItems(): + runlist=parseRunList(str(self.spectraEdit.text())) + print runlist + fname=os.path.join(self.lineEdit.text(),prefix + idx.text()) + if (self.comboBox.currentIndex() == 0): + fname+='.dat' + print "FILENAME: ", fname + a1=mtd[str(idx.text())] + titl='#'+a1.getTitle()+'\n' + x1=a1.readX(0) + X1=n.zeros((len(x1)-1)) + for i in range(0,len(x1)-1): + X1[i]=(x1[i]+x1[i+1])/2.0 + y1=a1.readY(0) + e1=a1.readE(0) + if (self.radio1.isChecked()): + sep=',' + elif (self.radio2.isChecked()): + sep=' ' + elif (self.radio3.isChecked()): + sep='\t' + print fname + f=open(fname,'w') + if self.titleCheckBox.isChecked(): + f.write(titl) + samp = a1.getRun() + for log in self.listWidget2.selectedItems(): + + prop = samp.getLogData(str(log.text())) + headerLine='#'+log.text() + ': ' + str(prop.value) + '\n' + print headerLine + f.write(headerLine) + qres=(X1[1]-X1[0])/X1[1] + print "Constant dq/q from file: ",qres + for i in range(len(X1)): + if self.xErrorCheckBox.isChecked(): + dq=X1[i]*qres + s="%e" % X1[i] +sep+"%e" % y1[i] +sep + "%e" % e1[i] + sep + "%e" % dq +"\n" + else: + s="%e" % X1[i] +sep+"%e" % y1[i] +sep + "%e" % e1[i]+ "\n" + f.write(s) + f.close() + elif (self.comboBox.currentIndex() == 1): + print "Not yet implemented!" + elif (self.comboBox.currentIndex() == 2): + print "ANSTO format" + self.saveANSTO(idx,fname) + elif (self.comboBox.currentIndex() == 3): + print "ILL MFT format" + self.saveMFT(idx,fname) + # for idx in self.listWidget.selectedItems(): + # fname=str(path+prefix+idx.text()+'.dat') + # print "FILENAME: ", fname + # wksp=str(idx.text()) + # SaveAscii(InputWorkspace=wksp,Filename=fname) + + self.SavePath=self.lineEdit.text() + + def saveANSTO(self,idx,fname): + fname+='.txt' + print "FILENAME: ", fname + a1=mtd[str(idx.text())] + titl='#'+a1.getTitle()+'\n' + x1=a1.readX(0) + X1=n.zeros((len(x1)-1)) + for i in range(0,len(x1)-1): + X1[i]=(x1[i]+x1[i+1])/2.0 + y1=a1.readY(0) + e1=a1.readE(0) + sep='\t' + f=open(fname,'w') + qres=(X1[1]-X1[0])/X1[1] + print "Constant dq/q from file: ",qres + for i in range(len(X1)): + dq=X1[i]*qres + s="%e" % X1[i] +sep+"%e" % y1[i] +sep + "%e" % e1[i] + sep + "%e" % dq +"\n" + f.write(s) + f.close() + + def saveMFT(self,idx,fname): + fname+='.mft' + print "FILENAME: ", fname + a1=mtd[str(idx.text())] + titl=a1.getTitle()+'\n' + x1=a1.readX(0) + X1=n.zeros((len(x1)-1)) + for i in range(0,len(x1)-1): + X1[i]=(x1[i]+x1[i+1])/2.0 + y1=a1.readY(0) + e1=a1.readE(0) + sep='\t' + f=open(fname,'w') + f.write('MFT\n') + f.write('Instrument: '+a1.getInstrument().getName()+'\n') + f.write('User-local contact: \n') + f.write('Title: \n') + samp = a1.getRun() + s = 'Subtitle: '+samp.getLogData('run_title').value+'\n' + f.write(s) + s = 'Start date + time: '+samp.getLogData('run_start').value+'\n' + f.write(s) + s = 'End date + time: '+samp.getLogData('run_end').value+'\n' + f.write(s) + + for log in self.listWidget2.selectedItems(): + + prop = samp.getLogData(str(log.text())) + headerLine=log.text() + ': ' + str(prop.value) + '\n' + print headerLine + f.write(headerLine) + f.write('Number of file format: 2\n') + s = 'Number of data points:\t' + str(len(X1))+'\n' + f.write(s) + f.write('\n') + f.write('\tq\trefl\trefl_err\tq_res\n') + qres=(X1[1]-X1[0])/X1[1] + print "Constant dq/q from file: ",qres + for i in range(len(X1)): + dq=X1[i]*qres + s="\t%e" % X1[i] +sep+"%e" % y1[i] +sep + "%e" % e1[i] + sep + "%e" % dq +"\n" + f.write(s) + f.close() + +def calcRes(run): + runno = '_' + str(run) + 'temp' + if type(run) == type(int()): + Load(Filename=run, OutputWorkspace=runno) + else: + Load(Filename=run.replace("raw", "nxs", 1), OutputWorkspace=runno) + # Get slits and detector angle theta from NeXuS + theta = groupGet(runno, 'samp', 'THETA') + inst = groupGet(runno, 'inst') + s1z = inst.getComponentByName('slit1').getPos().getZ() * 1000.0 # distance in mm + s2z = inst.getComponentByName('slit2').getPos().getZ() * 1000.0 # distance in mm + s1vg = inst.getComponentByName('slit1') + s1vg = s1vg.getNumberParameter('vertical gap')[0] + s2vg = inst.getComponentByName('slit2') + s2vg = s2vg.getNumberParameter('vertical gap')[0] + + if type(theta) != float: + th = theta[len(theta) - 1] + else: + th = theta + + print "s1vg=", s1vg, "s2vg=", s2vg, "theta=", theta + #1500.0 is the S1-S2 distance in mm for SURF!!! + resolution = math.atan((s1vg + s2vg) / (2 * (s2z - s1z))) * 180 / math.pi / th + print "dq/q=", resolution + DeleteWorkspace(runno) + return resolution + +def groupGet(wksp, whattoget, field=''): + ''' + returns information about instrument or sample details for a given workspace wksp, + also if the workspace is a group (info from first group element) + ''' + if (whattoget == 'inst'): + if isinstance(mtd[wksp], WorkspaceGroup): + return mtd[wksp + '_1'].getInstrument() + else: + return mtd[wksp].getInstrument() + elif (whattoget == 'samp' and field != ''): + if isinstance(mtd[wksp], WorkspaceGroup): + try: + log = mtd[wksp + '_1'].getRun().getLogData(field).value + if (type(log) is int or type(log) is str): + res = log + else: + res = log[len(log) - 1] + except RuntimeError: + res = 0 + print "Block " + field + " not found." + else: + try: + log = mtd[wksp].getRun().getLogData(field).value + if (type(log) is int or type(log) is str): + res = log + else: + res = log[len(log) - 1] + except RuntimeError: + res = 0 + print "Block " + field + " not found." + return res + elif (whattoget == 'wksp'): + if isinstance(mtd[wksp], WorkspaceGroup): + return mtd[wksp + '_1'].getNumberHistograms() + else: + return mtd[wksp].getNumberHistograms() + +def getWorkspace(wksp): + + if isinstance(mtd[wksp], WorkspaceGroup): + wout = mtd[wksp + '_1'] + else: + wout = mtd[wksp] + return wout diff --git a/Code/Mantid/scripts/Reflectometry/isis_reflectometry/convert_to_wavelength.py b/Code/Mantid/scripts/Reflectometry/isis_reflectometry/convert_to_wavelength.py index 7f5d4c9bb79f..ea0401b73c17 100644 --- a/Code/Mantid/scripts/Reflectometry/isis_reflectometry/convert_to_wavelength.py +++ b/Code/Mantid/scripts/Reflectometry/isis_reflectometry/convert_to_wavelength.py @@ -16,10 +16,18 @@ def sum_workspaces(cls, workspaces): """ return sum(workspaces) + @classmethod + def to_single_workspace(cls, candidate): + ws = ConvertToWavelength.to_workspace(candidate) + if isinstance(ws, mantid.api.WorkspaceGroup): + return ws[0] + else: + return ws + @classmethod def to_workspace(cls, candidate): workspace = None - if isinstance(candidate, mantid.api.MatrixWorkspace): + if isinstance(candidate, mantid.api.Workspace): workspace = candidate elif isinstance(candidate, str): if mantid.api.AnalysisDataService.doesExist(candidate): diff --git a/Code/Mantid/scripts/Reflectometry/isis_reflectometry/l2q.py b/Code/Mantid/scripts/Reflectometry/isis_reflectometry/l2q.py index 5f0a7056bf7d..41bf0b0f1670 100644 --- a/Code/Mantid/scripts/Reflectometry/isis_reflectometry/l2q.py +++ b/Code/Mantid/scripts/Reflectometry/isis_reflectometry/l2q.py @@ -3,7 +3,7 @@ from mantid.simpleapi import * # New API from mantid.geometry import ReferenceFrame -def l2q(ws,whichDet,theta): +def l2q(ws,whichDet,theta, sample_component_name): ''' call signature::call signature:: @@ -15,6 +15,7 @@ def l2q(ws,whichDet,theta): ws : run to be converted into Q. whichDet : Name of the component detector theta : final theta angle to use. + sample_component_name: Name of the component that corresponds to the sample TODO: 1) This is currently a point detector only function. This will either need @@ -30,7 +31,7 @@ def l2q(ws,whichDet,theta): else: inst = ws.getInstrument() - sampleLocation=inst.getComponentByName('some-surface-holder').getPos() + sampleLocation=inst.getComponentByName(sample_component_name).getPos() detLocation=inst.getComponentByName(whichDet).getPos() sample2detector=detLocation-sampleLocation # meters diff --git a/Code/Mantid/scripts/Reflectometry/isis_reflectometry/procedures.py b/Code/Mantid/scripts/Reflectometry/isis_reflectometry/procedures.py new file mode 100644 index 000000000000..db14d0ef7eee --- /dev/null +++ b/Code/Mantid/scripts/Reflectometry/isis_reflectometry/procedures.py @@ -0,0 +1,1336 @@ +from math import * + +try: + from mantid.simpleapi import * # New API +except ImportError: + pass +#import qti as qti +import numpy as n + +def addRuns(runlist,wname): + mtd.deleteWorkspace(str(wname)) + output=str(wname) + if runlist[0] != "0": + #nzeros=8-len(str(runlist[0])) + #fpad="" + #for i in range(nzeros): + # fpad+="0" + #filename="offspec"+fpad+str(runlist[0])+".nxs" + #fname=str(FileFinder.findRuns(filename)) + #fname=str.replace(fname,'\\','/') + #fname=str.replace(fname,'[','') + #fname=str.replace(fname,']','') + #fname=str.replace(fname,'\'','') + #fname=fname.lower() + ##fname=str.replace(fname,'.nxs','.raw') + #Load(fname,output) + Load(str(runlist[0]),output) + else: + #dae="ndx"+mtd.settings['default.instrument'].lower() + dae="ndxoffspec" + LoadDAE(DAEname=dae,OutputWorkspace=output,SpectrumMin="1") + if(mtd[output].isGroup()): + for k in mtd[output].getNames(): + mtd[k].setYUnit('Counts') + else: + mtd[output].setYUnit('Counts') + + if len(runlist) > 1: + for i in range(1,len(runlist)): + if runlist[i] != "0": + #nzeros=8-len(str(runlist[i])) + #fpad="" + #for j in range(nzeros): + # fpad+="0" + #filename="offspec"+fpad+str(runlist[i])+".nxs" + #fname=str(FileFinder.findRuns(filename)) + #fname=str.replace(fname,'\\','/') + #fname=str.replace(fname,'[','') + #fname=str.replace(fname,']','') + #fname=str.replace(fname,'\'','') + #fname=fname.lower() + ##fname=str.replace(fname,'.nxs','.raw') + #Load(fname,"wtemp") + Load(str(runlist[i]),"wtemp") + else: + #dae="ndx"+mtd.settings['default.instrument'].lower() + dae="ndxoffspec" + LoadDAE(DAEname=dae,OutputWorkspace="wtemp",SpectrumMin="1") + if(mtd['wtemp'].isGroup()): + for k in mtd['wtemp'].getNames(): + mtd[k].setYUnit('Counts') + else: + mtd[output].setYUnit('Counts') + Plus(output,"wtemp",output) + mtd.deleteWorkspace("wtemp") + + mtd.sendLogMessage("addRuns Completed") + +''' +parse a text string of the format "1-6:2+8+9,10+11+12+13-19:3,20-24" +to return a structure containing the separated lists [1, 3, 5, 8, 9], +[10, 11, 12, 13, 16, 19] and [20, 21, 22, 23, 24] +as integer lists that addRuns can handle. +''' +def parseRunList(istring): + rlist=[] + if len(istring) >0: + s1=istring.split(',') + rlist1=[] + for i in range(len(s1)): + tstr=s1[i].strip() + if len(tstr) > 0: + rlist1.append(tstr) + for i in range(len(rlist1)): + rlist2=[] + if rlist1[i].find('+') >= 0: + tstr=rlist1[i].split('+') + for j in range(len(tstr)): + if tstr[j].find(':') >=0 and tstr[j].find('-') >=0: + tstr[j].strip() + tstr2=tstr[j].split('-') + tstr3=tstr2[1].split(':') + r1=range(int(tstr2[0]),int(tstr3[0])+1,int(tstr3[1])) + for k in r1: + rlist2.append(str(k)) + elif tstr[j].find('-') >=0: + tstr[j].strip() + tstr2=tstr[j].split('-') + r1=range(int(tstr2[0]),int(tstr2[1])+1) + for k in r1: + rlist2.append(str(k)) + else: + rlist2.append(tstr[j]) + else: + if rlist1[i].find(':') >=0 and rlist1[i].find('-')>=0: + rlist1[i].strip() + tstr2=rlist1[i].split('-') + tstr3=tstr2[1].split(':') + r1=range(int(tstr2[0]),int(tstr3[0])+1,int(tstr3[1])) + for k in r1: + rlist2.append(str(k)) + elif rlist1[i].find('-')>=0: + rlist1[i].strip() + tstr2=rlist1[i].split('-') + r1=range(int(tstr2[0]),int(tstr2[1])+1) + for k in r1: + rlist2.append(str(k)) + else: + rlist2.append(rlist1[i]) + rlist.append(rlist2) + return rlist + +def parseNameList(istring): + s1=istring.split(',') + namelist=[] + for i in range(len(s1)): + tstr=s1[i].strip() + namelist.append(tstr) + return namelist + +def floodnorm(wkspName,floodfile): + # + # pixel by pixel efficiency correction for the linear detector + # + floodloaded=0 + a1=mtd.getWorkspaceNames() + for i in range(len(a1)): + if a1[i] == "ld240flood": + floodloaded=1 + if floodloaded == 0: + LoadNexusProcessed(Filename=floodfile,OutputWorkspace="ld240flood") + + Divide(wkspName,"ld240flood",wkspName) +# +# Plot a bunch of workspaces as 2D maps +# using the supplied limits and log scale settings +# +def plot2D(wkspNames,limits,logScales): + nplot=0 + workspace_mtx=[] + wNames=parseNameList(wkspNames) + for i in range(len(wNames)): + w1=mtd[wNames[i]] + if w1.isGroup(): + w1names=w1.getNames() + for j in range(len(w1names)): + #workspace_mtx.append(qti.app.mantidUI.importMatrixWorkspace(w1names[j])) + workspace_mtx.append(mantidplot.importMatrixWorkspace(w1names[j])) + gr2d=workspace_mtx[nplot].plotGraph2D() + nplot=nplot+1 + l=gr2d.activeLayer() + if logScales[0]=="0": + l.setAxisScale(mantidplot.Layer.Bottom,float(limits[0]),float(limits[1])) + elif logScales[0]=="2": + l.setAxisScale(mantidplot.Layer.Bottom,float(limits[0]),float(limits[1]),1) + if logScales[1]=="0": + l.setAxisScale(mantidplot.Layer.Left,float(limits[2]),float(limits[3])) + elif logScales[1]=="2": + l.setAxisScale(mantidplot.Layer.Left,float(limits[2]),float(limits[3]),1) + if logScales[2]=="0": + l.setAxisScale(mantidplot.Layer.Right,float(limits[4]),float(limits[5])) + elif logScales[2]=="2": + l.setAxisScale(mantidplot.Layer.Right,float(limits[4]),float(limits[5]),1) + else: + workspace_mtx.append(mantidplot.importMatrixWorkspace(wNames[i])) + gr2d=workspace_mtx[nplot].plotGraph2D() + nplot=nplot+1 + l=gr2d.activeLayer() + if logScales[0]=="0": + l.setAxisScale(mantidplot.Layer.Bottom,float(limits[0]),float(limits[1])) + elif logScales[0]=="2": + l.setAxisScale(mantidplot.Layer.Bottom,float(limits[0]),float(limits[1]),1) + if logScales[1]=="0": + l.setAxisScale(mantidplot.Layer.Left,float(limits[2]),float(limits[3])) + elif logScales[1]=="2": + l.setAxisScale(mantidplot.Layer.Left,float(limits[2]),float(limits[3]),1) + if logScales[2]=="0": + l.setAxisScale(mantidplot.Layer.Right,float(limits[4]),float(limits[5])) + elif logScales[2]=="2": + l.setAxisScale(mantidplot.Layer.Right,float(limits[4]),float(limits[5]),1) + + mtd.sendLogMessage("plot2D finished") +# +# Plot a bunch of workspaces as 2D maps +# using the supplied limits and log scale settings +# +def XYPlot(wkspNames,spectra,limits,logScales,errors,singleFigure): + wNames=parseNameList(wkspNames) + spec=parseNameList(spectra) + ploterr=0 + xLog=0 + yLog=0 + if errors == "2": + ploterr=1 + if logScales[0] == "2": + xLog=1 + if logScales[1] == "2": + yLog=1 + + if singleFigure == "2": + p1=plotSpectrum(wNames,spec,ploterr) + l=p1.activeLayer() + l.setAxisScale(mantidplot.Layer.Bottom,float(limits[0]),float(limits[1]),xLog) + l.setAxisScale(mantidplot.Layer.Left,float(limits[2]),float(limits[3]),yLog) + else: + for i in range(len(wNames)): + p1=plotSpectrum(wNames[i],spec,ploterr) + l=p1.activeLayer() + l.setAxisScale(mantidplot.Layer.Bottom,float(limits[0]),float(limits[1]),xLog) + l.setAxisScale(mantidplot.Layer.Left,float(limits[2]),float(limits[3]),yLog) + + mtd.sendLogMessage("XYPlot finished") + +def nrtestfn(runlist,wnames): + + rlist=parseRunList(runlist) + mtd.sendLogMessage("This is the runlist:"+str(rlist)) + namelist=parseNameList(wnames) + mtd.sendLogMessage("This is the nameslist:"+str(namelist)) + for i in range(len(rlist)): + addRuns(rlist[i],namelist[i]) + #Load(Filename="L:/RawData/cycle_10_1/OFFSPEC0000"+str(rlist[i][j])+".nxs",OutputWorkspace="w"+str(rlist[i][j]),LoaderName="LoadISISNexus") + + mtd.sendLogMessage("nrtestfn completed") +''' + output="w7503" + plotper=[1,2] + rebpars="0.5,0.025,14.5" + spmin=0 + spmax=239 + Load(Filename="L:/RawData/cycle_10_1/OFFSPEC00007503.nxs",OutputWorkspace=output,LoaderName="LoadISISNexus") + workspace_mtx=[] + nplot=0 + for i in plotper: + ConvertUnits(InputWorkspace=output+"_"+str(i),OutputWorkspace=output+"_"+str(i),Target="Wavelength",AlignBins="1") + Rebin(InputWorkspace=output+"_"+str(i),OutputWorkspace=output+"_"+str(i),Params=rebpars) + CropWorkspace(InputWorkspace=output+"_"+str(i),OutputWorkspace=output+"_"+str(i)+"m",StartWorkspaceIndex="1",EndWorkspaceIndex="1") + CropWorkspace(InputWorkspace=output+"_"+str(i),OutputWorkspace=output+"_"+str(i)+"d",StartWorkspaceIndex=str(spmin+4),EndWorkspaceIndex=str(spmax+4)) + Divide(output+"_"+str(i)+"d",output+"_"+str(i)+"m",output+"_"+str(i)+"n") + workspace_mtx.append(qti.app.mantidUI.importMatrixWorkspace(output+"_"+str(i)+"n")) + gr2d=workspace_mtx[nplot].plotGraph2D() + nplot=nplot+1 + l=gr2d.activeLayer() + + mtd.sendLogMessage("quickPlot Finished") +''' +def removeoutlayer(wksp): + # remove counts from bins where there are so few counts it makes a mess of the polarisation + # calculation + a1=mtd[wksp] + nspec=a1.getNumberHistograms() + x=a1.readX(0) + for i in range(nspec): + for j in range(len(x)-1): + y=a1.readY(i)[j] + if (y<2): + a1.dataY(i)[j]=0.0; + a1.dataE(i)[j]=0.0; + +def nrSESANSFn(runList,nameList,P0runList,P0nameList,minSpec,maxSpec,upPeriod,downPeriod,existingP0,SEConstants,gparams,convertToSEL,lnPOverLam,diagnostics="0",removeoutlayer="0",floodfile="none",): + nlist=parseNameList(nameList) + mtd.sendLogMessage("This is the sample nameslist:"+str(nlist)) + rlist=parseRunList(runList) + mtd.sendLogMessage("This is the sample runlist:"+str(rlist)) + for i in range(len(rlist)): + addRuns(rlist[i],nlist[i]) + + P0nlist=parseNameList(P0nameList) + mtd.sendLogMessage("This is the P0nameslist:"+str(P0nlist)) + if existingP0 != "2": + P0rlist=parseRunList(P0runList) + mtd.sendLogMessage("This is the P0runlist:"+str(P0rlist)) + for i in range(len(P0rlist)): + addRuns(P0rlist[i],P0nlist[i]) + + mon_spec=int(gparams[3])-1 + minSp=int(minSpec)-1 + maxSp=int(maxSpec)-1 + reb=gparams[0]+","+gparams[1]+","+gparams[2] + if len(gparams) == 5: + mapfile=gparams[4] + + for i in nlist: + a1=mtd[i+"_1"] + nspec=a1.getNumberHistograms() + if nspec == 1030 and minSp != 3: + GroupDetectors(InputWorkspace=i,OutputWorkspace=i,MapFile=mapfile) + ConvertUnits(i,i,"Wavelength",AlignBins=1) + Rebin(i,i,reb) + if (removeoutlayer != "0"): + removeoutlayer(i+"_1") + removeoutlayer(i+"_2") + CropWorkspace(i,i+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + if nspec == 245: + CropWorkspace(i,i+"2ddet",StartWorkspaceIndex=4,EndWorkspaceIndex=243) + if (floodfile != "none"): + floodnorm(i+"2ddet",floodfile) + if nspec == 1030: + CropWorkspace(i,i+"2ddet",StartWorkspaceIndex=3,EndWorkspaceIndex=124) + if int(maxSpec) > int(minSpec): + SumSpectra(i,i+"det",StartWorkspaceIndex=minSp,EndWorkspaceIndex=maxSp) + else: + CropWorkspace(i,i+"det",StartWorkspaceIndex=minSp,EndWorkspaceIndex=maxSp) + + Divide(i+"det",i+"mon",i+"norm") + if nspec > 4 and minSp != 3: + Divide(i+"2ddet",i+"mon",i+"2dnorm") + DeleteWorkspace(i+"mon") + if (diagnostics == "0"): + DeleteWorkspace(i+"det") + DeleteWorkspace(i) + Minus(i+"norm_"+upPeriod,i+"norm_"+downPeriod,"num") + Plus(i+"norm_2",i+"norm_1","den") + Divide("num","den",i+"pol") + ReplaceSpecialValues(i+"pol",i+"pol",0.0,0.0,0.0,0.0) + if nspec >4 and minSp != 3: + #print i+"2dnorm_"+upPeriod + #print i+"2dnorm_"+downPeriod + Minus(i+"2dnorm_"+upPeriod,i+"2dnorm_"+downPeriod,"num") + Plus(i+"2dnorm_2",i+"2dnorm_1","den") + Divide("num","den",i+"2dpol") + ReplaceSpecialValues(i+"2dpol",i+"2dpol",0.0,0.0,0.0,0.0) + #DeleteWorkspace(i+"norm_2") + #DeleteWorkspace(i+"norm_1") + DeleteWorkspace("num") + DeleteWorkspace("den") + + if existingP0 != "2": + for i in P0nlist: + ConvertUnits(i,i,"Wavelength",AlignBins=1) + Rebin(i,i,reb) + removeoutlayer(i+"_1") + removeoutlayer(i+"_2") + CropWorkspace(i,i+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + if int(maxSpec) > int(minSpec): + SumSpectra(i,i+"det",StartWorkspaceIndex=minSp,EndWorkspaceIndex=maxSp) + else: + CropWorkspace(i,i+"det",StartWorkspaceIndex=minSp,EndWorkspaceIndex=maxSp) + Divide(i+"det",i+"mon",i+"norm") + DeleteWorkspace(i+"mon") + DeleteWorkspace(i+"det") + DeleteWorkspace(i) + Minus(i+"norm_"+upPeriod,i+"norm_"+downPeriod,"num") + Plus(i+"norm_2",i+"norm_1","den") + Divide("num","den",i+"pol") + ReplaceSpecialValues(i+"pol",i+"pol",0.0,0.0,0.0,0.0) + DeleteWorkspace(i+"norm_2") + DeleteWorkspace(i+"norm_1") + DeleteWorkspace("num") + DeleteWorkspace("den") + + for i in range(len(nlist)): + if existingP0 != "2": + Divide(nlist[i]+"pol",P0nlist[i]+"pol",nlist[i]+"SESANS") + if nspec > 4 and minSp != 3: + Divide(nlist[i]+"2dpol",P0nlist[i]+"pol",nlist[i]+"2dSESANS") + else: + Divide(nlist[i]+"pol",P0nlist[i],nlist[i]+"SESANS") + if nspec > 4 and minSp != 3: + Divide(nlist[i]+"2dpol",P0nlist[i],nlist[i]+"2dSESANS") + ReplaceSpecialValues(nlist[i]+"SESANS",nlist[i]+"SESANS",0.0,0.0,0.0,0.0) + + SEConstList=parseNameList(SEConstants) + k=0 + for i in nlist: + if lnPOverLam == "2": + CloneWorkspace(InputWorkspace=i+"SESANS",OutputWorkspace=i+"SESANS_P") + a1=mtd[i+"SESANS"] + x=a1.readX(0) + for j in range(len(x)-1): + lam=((a1.readX(0)[j]+a1.readX(0)[j+1])/2.0)/10.0 + p=a1.readY(0)[j] + e=a1.readE(0)[j] + if lnPOverLam == "2": + if p > 0.0: + a1.dataY(0)[j]=log(p)/((lam)**2) + a1.dataE(0)[j]=(e/p)/((lam)**2) + else: + a1.dataY(0)[j]=0.0 + a1.dataE(0)[j]=0.0 + for j in range(len(x)): + if convertToSEL == "2": + lam=a1.readX(0)[j] + a1.dataX(0)[j]=1.0e-2*float(SEConstList[k])*lam*lam + #print str(lam)+" "+str(1.0e-2*float(SEConstList[k])*lam*lam) + k=k+1 + + + if nspec > 4 and minSp != 3: + k=0 + for i in nlist: + if lnPOverLam == "2": + CloneWorkspace(InputWorkspace=i+"2dSESANS",OutputWorkspace=i+"2dSESANS_P") + a1=mtd[i+"2dSESANS"] + nspec=a1.getNumberHistograms() + for l in range(nspec): + x=a1.readX(l) + for j in range(len(x)-1): + lam=((a1.readX(l)[j]+a1.readX(l)[j+1])/2.0)/10.0 + p=a1.readY(l)[j] + e=a1.readE(l)[j] + if lnPOverLam == "2": + if p > 0.0: + a1.dataY(l)[j]=log(p)/((lam*1.0e-9)**2) + a1.dataE(l)[j]=(e/p)/((lam*1.0e-9)**2) + else: + a1.dataY(l)[j]=0.0 + a1.dataE(l)[j]=0.0 + for j in range(len(x)): + if convertToSEL == "2": + lam=a1.readX(l)[j] + a1.dataX(l)[j]=1.0e-2*float(SEConstList[k])*lam*lam + #print str(lam)+" "+str(1.0e-2*float(SEConstList[k])*lam*lam) + k=k+1 + +def nrCalcSEConst(RFFrequency,poleShoeAngle): + if (RFFrequency=="0.5"): + B=0.53*34.288 + elif (RFFrequency=="1.0"): + B=34.288 + else: + B=2.0*34.288 + + h=6.62607e-34 + m=1.67493e-27 + L=1.0 + Gl=1.83247e8 + # + # correct the angle + # calibration of th0 using gold grating Dec 2010 + # + th0=float(poleShoeAngle) + th0=-0.0000000467796*(th0**5)+0.0000195413*(th0**4)-0.00326229*(th0**3)+0.271767*(th0**2)-10.4269*th0+198.108 + c1=Gl*m*2.0*B*L/(2.0*pi*h*tan(th0*pi/180.0)*1.0e20) + print c1*1e8 + return c1*1e8 + +def nrSESANSP0Fn(P0runList,P0nameList,minSpec,maxSpec,upPeriod,downPeriod,gparams,diagnostics="0"): + + P0nlist=parseNameList(P0nameList) + mtd.sendLogMessage("This is the P0nameslist:"+str(P0nlist)) + P0rlist=parseRunList(P0runList) + mtd.sendLogMessage("This is the P0runlist:"+str(P0rlist)) + for i in range(len(P0rlist)): + addRuns(P0rlist[i],P0nlist[i]) + + mon_spec=int(gparams[3])-1 + minSp=int(minSpec)-1 + maxSp=int(maxSpec)-1 + reb=gparams[0]+","+gparams[1]+","+gparams[2] + if len(gparams) == 5: + mapfile=gparams[4] + + for i in P0nlist: + a1=mtd[i+"_1"] + nspec=a1.getNumberHistograms() + if nspec == 1030 and minSp != 3: + GroupDetectors(InputWorkspace=i,OutputWorkspace=i,MapFile=mapfile) + ConvertUnits(i,i,"Wavelength",AlignBins=1) + Rebin(i,i,reb) + CropWorkspace(i,i+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + if int(maxSpec) > int(minSpec): + SumSpectra(i,i+"det",StartWorkspaceIndex=minSp,EndWorkspaceIndex=maxSp) + else: + CropWorkspace(i,i+"det",StartWorkspaceIndex=minSp,EndWorkspaceIndex=maxSp) + Divide(i+"det",i+"mon",i+"norm") + if (diagnostics=="0"): + DeleteWorkspace(i+"mon") + DeleteWorkspace(i+"det") + DeleteWorkspace(i) + Minus(i+"norm_"+upPeriod,i+"norm_"+downPeriod,"num") + Plus(i+"norm_2",i+"norm_1","den") + Divide("num","den",i+"pol") + ReplaceSpecialValues(i+"pol",i+"pol",0.0,0.0,0.0,0.0) + if (diagnostics=="0"): + DeleteWorkspace(i+"norm_2") + DeleteWorkspace(i+"norm_1") + DeleteWorkspace("num") + DeleteWorkspace("den") + +def nrSERGISFn(runList,nameList,P0runList,P0nameList,incidentAngles,SEConstants,specChan,minSpec,maxSpec,upPeriod,downPeriod,existingP0,gparams,lnPOverLam): + nlist=parseNameList(nameList) + mtd.sendLogMessage("This is the sample nameslist:"+str(nlist)) + rlist=parseRunList(runList) + mtd.sendLogMessage("This is the sample runlist:"+str(rlist)) + incAngles=parseNameList(incidentAngles) + mtd.sendLogMessage("This incident Angles are:"+str(incAngles)) + + for i in range(len(rlist)): + addRuns(rlist[i],nlist[i]) + + P0nlist=parseNameList(P0nameList) + mtd.sendLogMessage("This is the P0nameslist:"+str(P0nlist)) + if existingP0 != "2": + P0rlist=parseRunList(P0runList) + mtd.sendLogMessage("This is the P0runlist:"+str(P0rlist)) + for i in range(len(P0rlist)): + addRuns(P0rlist[i],P0nlist[i]) + + mon_spec=int(gparams[3])-1 + minSp=int(minSpec)-1 + maxSp=int(maxSpec)-1 + reb=gparams[0]+","+gparams[1]+","+gparams[2] + + k=0 + for i in nlist: + for j in range(2): + wksp=i+"_"+pnums[j] + ConvertUnits(wksp,wksp,"Wavelength",AlignBins=1) + Rebin(wksp,wksp,reb) + CropWorkspace(wksp,wksp+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + a1=mtd[wksp] + nspec=a1.getNumberHistograms() + if nspec == 4: + CropWorkspace(InputWorkspace=wksp,OutputWorkspace=wksp+"det",StartWorkspaceIndex=3,EndWorkspaceIndex=3) + RotateInstrumentComponent(wksp+"det","DetectorBench",X="-1.0",Angle=str(2.0*float(incAngles[k]))) + Divide(wksp+"det",wksp+"mon",wksp+"norm") + else: + CropWorkspace(InputWorkspace=wksp,OutputWorkspace=wksp+"det",StartWorkspaceIndex=4,EndWorkspaceIndex=243) + # move the first spectrum in the list onto the beam centre so that when the bench is rotated it's in the right place + MoveInstrumentComponent(wksp+"det","DetectorBench",Y=str((125.0-float(minSpec))*1.2e-3)) + # add a bit to the angle to put the first spectrum of the group in the right place + a1=2.0*float(incAngles[k])+atan((float(minSpec)-float(specChan))*1.2e-3/3.53)*180.0/pi + #print str(2.0*float(incAngles[k]))+" "+str(atan((float(minSpec)-float(specChan))*1.2e-3/3.63)*180.0/pi)+" "+str(a1) + RotateInstrumentComponent(wksp+"det","DetectorBench",X="-1.0",Angle=str(a1)) + GroupDetectors(wksp+"det",wksp+"sum",WorkspaceIndexList=range(int(minSpec)-5,int(maxSpec)-5+1),KeepUngroupedSpectra="0") + Divide(wksp+"sum",wksp+"mon",wksp+"norm") + Divide(wksp+"det",wksp+"mon",wksp+"detnorm") + floodnorm(wksp+"detnorm",floodfile) + DeleteWorkspace(wksp+"sum") + + DeleteWorkspace(wksp+"mon") + DeleteWorkspace(wksp+"det") + DeleteWorkspace(wksp) + + Minus(i+"_"+upPeriod+"norm",i+"_"+downPeriod+"norm","num") + Plus(i+"_1norm",i+"_2norm","den") + Divide("num","den",i+"pol") + ReplaceSpecialValues(i+"pol",i+"pol",0.0,0.0,0.0,0.0) + DeleteWorkspace(i+"_1norm") + DeleteWorkspace(i+"_2norm") + DeleteWorkspace("num") + DeleteWorkspace("den") + + if nspec != 4: + Minus(i+"_"+upPeriod+"detnorm",i+"_"+downPeriod+"detnorm","num") + Plus(i+"_1detnorm",i+"_2detnorm","den") + Divide("num","den",i+"2dpol") + ReplaceSpecialValues(i+"2dpol",i+"2dpol",0.0,0.0,0.0,0.0) + DeleteWorkspace(i+"_1detnorm") + DeleteWorkspace(i+"_2detnorm") + DeleteWorkspace("num") + DeleteWorkspace("den") + + if existingP0 != "2": + for i in P0nlist: + ConvertUnits(i,i,"Wavelength",AlignBins=1) + Rebin(i,i,reb) + CropWorkspace(i,i+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + if int(maxSpec) > int(minSpec): + SumSpectra(i,i+"det",StartWorkspaceIndex=minSp,EndWorkspaceIndex=maxSp) + else: + CropWorkspace(i,i+"det",StartWorkspaceIndex=minSp,EndWorkspaceIndex=maxSp) + Divide(i+"det",i+"mon",i+"norm") + DeleteWorkspace(i+"mon") + DeleteWorkspace(i+"det") + DeleteWorkspace(i) + Minus(i+"norm_"+upPeriod,i+"norm_"+downPeriod,"num") + Plus(i+"norm_2",i+"norm_1","den") + Divide("num","den",i+"pol") + ReplaceSpecialValues(i+"pol",i+"pol",0.0,0.0,0.0,0.0) + DeleteWorkspace(i+"norm_2") + DeleteWorkspace(i+"norm_1") + DeleteWorkspace("num") + DeleteWorkspace("den") + + for i in range(len(nlist)): + if existingP0 != "2": + Divide(nlist[i]+"pol",P0nlist[i]+"pol",nlist[i]+"SESANS") + else: + Divide(nlist[i]+"pol",P0nlist[i]+"pol",nlist[i]+"SESANS") + ReplaceSpecialValues(nlist[i]+"SESANS",nlist[i]+"SESANS",0.0,0.0,0.0,0.0) + + SEConstList=parseNameList(SEConstants) + k=0 + for i in nlist: + a1=mtd[i+"SESANS"] + x=a1.readX(0) + for j in range(len(x)-1): + lam=((a1.readX(0)[j]+a1.readX(0)[j+1])/2.0)/10.0 + p=a1.readY(0)[j] + a1.dataY(0)[j]=log(p)/((lam*1.0e-8)**2) + for j in range(len(x)): + lam=a1.readX(0)[j] + a1.dataX(0)[j]=1.0e-2*float(SEConstList[k])*lam*lam + #print str(lam)+" "+str(1.0e-2*float(SEConstList[k])*lam*lam) + k=k+1 + +def nrNRFn(runList,nameList,incidentAngles,DBList,specChan,minSpec,maxSpec,gparams,floodfile,diagnostics=0): + nlist=parseNameList(nameList) + mtd.sendLogMessage("This is the sample nameslist:"+str(nlist)) + rlist=parseRunList(runList) + mtd.sendLogMessage("This is the sample runlist:"+str(rlist)) + dlist=parseNameList(DBList) + mtd.sendLogMessage("This is the Direct Beam nameslist:"+str(dlist)) + incAngles=parseNameList(incidentAngles) + mtd.sendLogMessage("This incident Angles are:"+str(incAngles)) + + for i in range(len(rlist)): + addRuns(rlist[i],nlist[i]) + + mon_spec=int(gparams[3])-1 + reb=gparams[0]+","+gparams[1]+","+gparams[2] + + k=0 + for i in nlist: + if(mtd[i].isGroup()): + #RenameWorkspace(i+"_1",i) + snames=mtd[i].getNames() + Plus(i+"_1",i+"_2","wtemp") + if len(snames) > 2: + for j in range(2,len(snames)-1): + Plus("wtemp",snames[j],"wtemp") + for j in snames: + DeleteWorkspace(j) + RenameWorkspace("wtemp",i) + ConvertUnits(InputWorkspace=i,OutputWorkspace=i,Target="Wavelength",AlignBins="1") + a1=mtd[i] + nspec=a1.getNumberHistograms() + if nspec == 4: + Rebin(InputWorkspace=i,OutputWorkspace=i,Params=reb) + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"det",StartWorkspaceIndex=3,EndWorkspaceIndex=3) + RotateInstrumentComponent(i+"det","DetectorBench",X="-1.0",Angle=str(2.0*float(incAngles[k]))) + Divide(i+"det",i+"mon",i+"norm") + if dlist[k] != "none": + Divide(i+"norm",dlist[k],i+"norm") + ReplaceSpecialValues(i+"norm",i+"norm","0.0","0.0","0.0","0.0") + ConvertUnits(i+"norm",i+"RvQ",Target="MomentumTransfer") + else: + # Rebin using internal parameters to avoid problems with summing in Q + #internalreb=gparams[0]+",0.01,"+gparams[2] + #Rebin(InputWorkspace=i,OutputWorkspace=i,Params=internalreb) + minSp=int(minSpec) + maxSp=int(maxSpec) + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"det",StartWorkspaceIndex=4,EndWorkspaceIndex=243) + floodnorm(i+"det",floodfile) + # move the first spectrum in the list onto the beam centre so that when the bench is rotated it's in the right place + MoveInstrumentComponent(i+"det","DetectorBench",Y=str((125.0-float(minSpec))*1.2e-3)) + # add a bit to the angle to put the first spectrum of the group in the right place + a1=2.0*float(incAngles[k])+atan((float(minSpec)-float(specChan))*1.2e-3/3.53)*180.0/pi + #print str(2.0*float(incAngles[k]))+" "+str(atan((float(minSpec)-float(specChan))*1.2e-3/3.63)*180.0/pi)+" "+str(a1) + RotateInstrumentComponent(i+"det","DetectorBench",X="-1.0",Angle=str(a1)) + # Experimental convert to Q before summing + #CropWorkspace(InputWorkspace=i+"det",OutputWorkspace=i+"detQ",StartWorkspaceIndex=int(minSpec)-5,EndWorkspaceIndex=int(maxSpec)-5+1) + #ConvertUnits(InputWorkspace=i+"detQ",OutputWorkspace=i+"detQ",Target="MomentumTransfer",AlignBins="1") + #GroupDetectors(i+"detQ",i+"sum",WorkspaceIndexList=range(0,int(maxSpec)-int(minSpec)),KeepUngroupedSpectra="0") + #ConvertUnits(InputWorkspace=i+"sum",OutputWorkspace=i+"sum",Target="Wavelength",AlignBins="1") + #Rebin(InputWorkspace=i+"sum",OutputWorkspace=i+"sum",Params=reb) + #CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + #Rebin(InputWorkspace=i+"mon",OutputWorkspace=i+"mon",Params=reb) + #Rebin(InputWorkspace=i+"det",OutputWorkspace=i+"det",Params=reb) + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + Rebin(InputWorkspace=i+"mon",OutputWorkspace=i+"mon",Params=reb) + Rebin(InputWorkspace=i+"det",OutputWorkspace=i+"det",Params=reb) + GroupDetectors(i+"det",i+"sum",WorkspaceIndexList=range(int(minSpec)-5,int(maxSpec)-5+1),KeepUngroupedSpectra="0") + Divide(i+"sum",i+"mon",i+"norm") + Divide(i+"det",i+"mon",i+"detnorm") + if dlist[k] == "none": + a1=0 + elif dlist[k] == "function": + # polynomial + power law corrections based on Run numbers 8291 and 8292 + Divide(i+'norm',i+'norm',i+'normt1') + PolynomialCorrection(i+'normt1',i+'normPC','-0.0177398,0.00101695,0.0',Operation='Multiply') + PowerLawCorrection(i+'normt1',i+'normPLC','2.01332','-1.8188') + Plus(i+'normPC',i+'normPLC',i+'normt1') + Divide(i+'norm',i+'normt1',i+'norm') + ReplaceSpecialValues(i+'norm',i+'norm',0.0,0.0,0.0,0.0) + DeleteWorkspace(i+'normPC') + DeleteWorkspace(i+'normPLC') + DeleteWorkspace(i+'normt1') + else: + Divide(i+"norm",dlist[k],i+"norm") + ReplaceSpecialValues(i+"norm",i+"norm","0.0","0.0","0.0","0.0") + Divide(i+"detnorm",dlist[k],i+"detnorm") + ReplaceSpecialValues(i+"detnorm",i+"detnorm","0.0","0.0","0.0","0.0") + ConvertUnits(i+"norm",i+"RvQ",Target="MomentumTransfer") + #floodnorm(i+"detnorm",floodfile) + DeleteWorkspace(i+"sum") + + k=k+1 + DeleteWorkspace(i) + if(diagnostics==0): + DeleteWorkspace(i+"mon") + DeleteWorkspace(i+"det") + +def findbin(wksp,val): + a1=mtd[wksp] + x1=a1.readX(0) + bnum=-1 + for i in range(len(x1)-1): + if x1[i] > val: + break + return i-1 + +def nrDBFn(runListShort,nameListShort,runListLong,nameListLong,nameListComb,minSpec,maxSpec,minWavelength,gparams,floodfile="",diagnostics="0"): + nlistS=parseNameList(nameListShort) + rlistS=parseRunList(runListShort) + nlistL=parseNameList(nameListLong) + rlistL=parseRunList(runListLong) + nlistComb=parseNameList(nameListComb) + + for i in range(len(rlistS)): + addRuns(rlistS[i],nlistS[i]) + for i in range(len(rlistL)): + addRuns(rlistL[i],nlistL[i]) + + mon_spec=int(gparams[3])-1 + minSp=int(minSpec)-1 + maxSp=int(maxSpec)-1 + reb=gparams[0]+","+gparams[1]+","+gparams[2] + + for i in nlistS: + ConvertUnits(InputWorkspace=i,OutputWorkspace=i,Target="Wavelength",AlignBins="1") + Rebin(InputWorkspace=i,OutputWorkspace=i,Params=reb) + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + if(mtd[i].isGroup()): + snames=mtd[i].getNames() + a1=mtd[snames[0]] + else: + a1=mtd[i] + + nspec=a1.getNumberHistograms() + + if nspec == 4: + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"det",StartWorkspaceIndex=3,EndWorkspaceIndex=3) + Divide(i+"det",i+"mon",i+"norm") + ReplaceSpecialValues(i+"norm",i+"norm","0.0","0.0","0.0","0.0") + else: + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"det",StartWorkspaceIndex=4,EndWorkspaceIndex=243) + floodnorm(i+"det",floodfile) + GroupDetectors(i+"det",i+"sum",WorkspaceIndexList=range(int(minSpec)-5,int(maxSpec)-5+1),KeepUngroupedSpectra="0") + Divide(i+"sum",i+"mon",i+"norm") + ReplaceSpecialValues(i+"norm",i+"norm","0.0","0.0","0.0","0.0") + + for i in nlistL: + ConvertUnits(InputWorkspace=i,OutputWorkspace=i,Target="Wavelength",AlignBins="1") + Rebin(InputWorkspace=i,OutputWorkspace=i,Params=reb) + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + if(mtd[i].isGroup()): + lnames=mtd[i].getNames() + a1=mtd[lnames[0]] + else: + a1=mtd[i] + + nspec=a1.getNumberHistograms() + + if nspec == 4: + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"det",StartWorkspaceIndex=3,EndWorkspaceIndex=3) + Divide(i+"det",i+"mon",i+"norm") + ReplaceSpecialValues(i+"norm",i+"norm","0.0","0.0","0.0","0.0") + else: + CropWorkspace(InputWorkspace=i,OutputWorkspace=i+"det",StartWorkspaceIndex=4,EndWorkspaceIndex=243) + floodnorm(i+"det",floodfile) + GroupDetectors(i+"det",i+"sum",WorkspaceIndexList=range(int(minSpec)-5,int(maxSpec)-5+1),KeepUngroupedSpectra="0") + Divide(i+"sum",i+"mon",i+"norm") + ReplaceSpecialValues(i+"norm",i+"norm","0.0","0.0","0.0","0.0") + + for i in range(len(nlistS)): + if(mtd[nlistS[i]+"norm"].isGroup()): + snames=mtd[nlistS[i]+"norm"].getNames() + lnames=mtd[nlistL[i]+"norm"].getNames() + for k in range(len(snames)): + Integration(snames[k],snames[k]+"int",minWavelength,gparams[2]) + Integration(lnames[k],lnames[k]+"int",minWavelength,gparams[2]) + Multiply(snames[k],lnames[k]+"int",snames[k]) + Divide(snames[k],snames[k]+"int",snames[k]) + a1=findbin(lnames[k],float(minWavelength)) + MultiplyRange(lnames[k],lnames[k],"0",str(a1),"0.0") + WeightedMean(snames[k],lnames[k],nlistComb[i]+"_"+str(k+1)) + if (diagnostics=="0"): + DeleteWorkspace(snames[k]+"int") + DeleteWorkspace(lnames[k]+"int") + else: + Integration(nlistS[i]+"norm",nlistS[i]+"int",minWavelength,gparams[2]) + Integration(nlistL[i]+"norm",nlistL[i]+"int",minWavelength,gparams[2]) + Multiply(nlistS[i]+"norm",nlistL[i]+"int",nlistS[i]+"norm") + Divide(nlistS[i]+"norm",nlistS[i]+"int",nlistS[i]+"norm") + a1=findbin(nlistL[i]+"norm",float(minWavelength)) + MultiplyRange(nlistL[i]+"norm",nlistL[i]+"norm","0",str(a1),"0.0") + WeightedMean(nlistS[i]+"norm",nlistL[i]+"norm",nlistComb[i]) + if (diagnostics=="0"): + DeleteWorkspace(nlistS[i]+"int") + DeleteWorkspace(nlistL[i]+"int") + + if (diagnostics=="0"): + DeleteWorkspace(nlistS[i]+"mon") + DeleteWorkspace(nlistS[i]+"det") + if nspec != 4: + DeleteWorkspace(nlistS[i]+"sum") + DeleteWorkspace(nlistS[i]+"norm") + DeleteWorkspace(nlistS[i]) + DeleteWorkspace(nlistL[i]+"mon") + DeleteWorkspace(nlistL[i]+"det") + if nspec != 4: + DeleteWorkspace(nlistL[i]+"sum") + DeleteWorkspace(nlistL[i]+"norm") + DeleteWorkspace(nlistL[i]) + +def numberofbins(wksp): + a1=mtd[wksp] + y1=a1.readY(0) + return len(y1)-1 + +def maskbin(wksp,val): + a1=mtd[wksp] + x1=a1.readX(0) + for i in range(len(x1)-1): + if x1[i] > val: + break + a1.dataY(0)[i-1]=0.0 + a1.dataE(0)[i-1]=0.0 + +def arr2list(iarray): + # convert array of strings to a single string with commas + res="" + for i in range(len(iarray)-1): + res=res+iarray[i]+"," + res=res+iarray[len(iarray)-1] + return res + +def NRCombineDatafn(RunsNameList,CombNameList,applySFs,SFList,SFError,scaleOption,bparams,globalSF,applyGlobalSF,diagnostics=0): + qmin=bparams[0] + bin=bparams[1] + qmax=bparams[2] + rlist=parseNameList(RunsNameList) + listsfs=parseNameList(SFList) + listsfserr=parseNameList(SFError) + sfs=[] + sferrs=[] + for i in rlist: + Rebin(i,i+"reb",qmin+","+bin+","+qmax) + # find the overlap ranges + bol=[] #beginning of overlaps + eol=[] #end of overlaps + for i in range(len(rlist)-1): + a1=mtd[rlist[i+1]] + x=a1.readX(0) + bol.append(x[0]) + a1=mtd[rlist[i]] + x=a1.readX(0) + eol.append(x[len(x)-1]) + # set the edges of the rebinned data to 0.0 to avoid partial bin problems + maskbin(rlist[0]+"reb",eol[0]) + if len(rlist) > 2: + for i in range(1,len(rlist)-1): + maskbin(rlist[i]+"reb",bol[i-1]) + maskbin(rlist[i]+"reb",eol[i]) + maskbin(rlist[len(rlist)-1]+"reb",bol[len(rlist)-2]) + # Now find the various scale factors and store in temp workspaces + for i in range(len(rlist)-1): + Integration(rlist[i]+"reb","i"+str(i)+"1temp",str(bol[i]),str(eol[i])) + Integration(rlist[i+1]+"reb","i"+str(i)+"2temp",str(bol[i]),str(eol[i])) + if scaleOption != "2": + Divide("i"+str(i)+"1temp","i"+str(i)+"2temp","sf"+str(i)) + a1=mtd["sf"+str(i)] + print "sf"+str(i)+"="+str(a1.readY(0))+" +/- "+str(a1.readE(0)) + sfs.append(str(a1.readY(0)[0])) + sferrs.append(str(a1.readE(0)[0])) + else: + Divide("i"+str(i)+"2temp","i"+str(i)+"1temp","sf"+str(i)) + print "sf"+str(i)+"="+str(a1.readY(0))+" +/- "+str(a1.readE(0)) + sfs.append(str(a1.readY(0)[0])) + sferrs.append(str(a1.readE(0)[0])) + mtd.deleteWorkspace("i"+str(i)+"1temp") + mtd.deleteWorkspace("i"+str(i)+"2temp") + # if applying pre-defined scale factors substitute the given values now + # Note the errors are now set to 0 + if applySFs == "2": + for i in range(len(rlist)-1): + a1=mtd["sf"+str(i)] + a1.dataY(0)[0]=float(listsfs[i]) + a1.dataE(0)[0]=float(listsfserr[i]) + # Now scale the various data sets in the correct order + if scaleOption != "2": + for i in range(len(rlist)-1): + for j in range(i+1,len(rlist)): + Multiply(rlist[j]+"reb","sf"+str(i),rlist[j]+"reb") + else: + for i in range(len(rlist)-1,0,-1): + for j in range(i,0,-1): + Multiply(rlist[j]+"reb","sf"+str(i-1),rlist[j]+"reb") + + WeightedMean(rlist[0]+"reb",rlist[1]+"reb","currentSum") + if len(rlist) > 2: + for i in range(2,len(rlist)): + WeightedMean("currentSum",rlist[i]+"reb","currentSum") + + # if applying a global scale factor do it here + if applyGlobalSF == "2": + scaledData=mtd['currentSum']/float(globalSF) + RenameWorkspace('scaledData',CombNameList) + mtd.deleteWorkspace('currentSum') + else: + RenameWorkspace('currentSum',CombNameList) + for i in range(len(rlist)-1): + mtd.deleteWorkspace("sf"+str(i)) + if (diagnostics==0): + for i in range(len(rlist)): + mtd.deleteWorkspace(rlist[i]+"reb") + return [arr2list(sfs),arr2list(sferrs)] + +def nrWriteXYE(wksp,fname): + a1=mtd[wksp] + x1=a1.readX(0) + X1=n.zeros((len(x1)-1)) + for i in range(0,len(x1)-1): + X1[i]=(x1[i]+x1[i+1])/2.0 + y1=a1.readY(0) + e1=a1.readE(0) + f=open(fname,'w') + for i in range(len(X1)): + s="" + s+="%f," % X1[i] + s+="%f," % y1[i] + s+="%f\n" % e1[i] + f.write(s) + f.close() + +def nrPNRCorrection(UpWksp,DownWksp): +# crho=[0.941893,0.0234006,-0.00210536,0.0] +# calpha=[0.945088,0.0242861,-0.00213624,0.0] +# cAp=[1.00079,-0.0186778,0.00131546,0.0] +# cPp=[1.01649,-0.0228172,0.00214626,0.0] + # Constants Based on Runs 18350+18355 and 18351+18356 analyser theta at -0.1deg + # 2 RF Flippers as the polarising system + crho=[1.006831,-0.011467,0.002244,-0.000095] + calpha=[1.017526,-0.017183,0.003136,-0.000140] + cAp=[0.917940,0.038265,-0.006645,0.000282] + cPp=[0.972762,0.001828,-0.000261,0.0] + Ip = mtd[UpWksp] + Ia = mtd[DownWksp] + CloneWorkspace(Ip,"PCalpha") + CropWorkspace(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",StartWorkspaceIndex="0",EndWorkspaceIndex="0") + PCalpha=(mtd['PCalpha']*0.0)+1.0 + alpha=mtd['PCalpha'] + # a1=alpha.readY(0) + # for i in range(0,len(a1)): + # alpha.dataY(0)[i]=0.0 + # alpha.dataE(0)[i]=0.0 + CloneWorkspace("PCalpha","PCrho") + CloneWorkspace("PCalpha","PCAp") + CloneWorkspace("PCalpha","PCPp") + rho=mtd['PCrho'] + Ap=mtd['PCAp'] + Pp=mtd['PCPp'] + # for i in range(0,len(a1)): + # x=(alpha.dataX(0)[i]+alpha.dataX(0)[i])/2.0 + # for j in range(0,4): + # alpha.dataY(0)[i]=alpha.dataY(0)[i]+calpha[j]*x**j + # rho.dataY(0)[i]=rho.dataY(0)[i]+crho[j]*x**j + # Ap.dataY(0)[i]=Ap.dataY(0)[i]+cAp[j]*x**j + # Pp.dataY(0)[i]=Pp.dataY(0)[i]+cPp[j]*x**j + PolynomialCorrection(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",Coefficients=calpha,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCrho",OutputWorkspace="PCrho",Coefficients=crho,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCAp",OutputWorkspace="PCAp",Coefficients=cAp,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCPp",OutputWorkspace="PCPp",Coefficients=cPp,Operation="Multiply") + D=Pp*(1.0+rho) + nIp=(Ip*(rho*Pp+1.0)+Ia*(Pp-1.0))/D + nIa=(Ip*(rho*Pp-1.0)+Ia*(Pp+1.0))/D + RenameWorkspace(nIp,str(Ip)+"corr") + RenameWorkspace(nIa,str(Ia)+"corr") + iwksp=mtd.getWorkspaceNames() + list=[str(Ip),str(Ia),"PCalpha","PCrho","PCAp","PCPp","1_p"] + for i in range(len(iwksp)): + for j in list: + lname=len(j) + if iwksp[i] [0:lname+1] == j+"_": + mtd.deleteWorkspace(iwksp[i]) + mtd.deleteWorkspace("PCalpha") + mtd.deleteWorkspace("PCrho") + mtd.deleteWorkspace("PCAp") + mtd.deleteWorkspace("PCPp") + mtd.deleteWorkspace("D") + +def nrPACorrection(UpUpWksp,UpDownWksp,DownUpWksp,DownDownWksp): +# crho=[0.941893,0.0234006,-0.00210536,0.0] +# calpha=[0.945088,0.0242861,-0.00213624,0.0] +# cAp=[1.00079,-0.0186778,0.00131546,0.0] +# cPp=[1.01649,-0.0228172,0.00214626,0.0] + # Constants Based on Runs 18350+18355 and 18351+18356 analyser theta at -0.1deg + # 2 RF Flippers as the polarising system + crho=[1.006831,-0.011467,0.002244,-0.000095] + calpha=[1.017526,-0.017183,0.003136,-0.000140] + cAp=[0.917940,0.038265,-0.006645,0.000282] + cPp=[0.972762,0.001828,-0.000261,0.0] + Ipp = mtd[UpUpWksp] + Ipa = mtd[UpDownWksp] + Iap = mtd[DownUpWksp] + Iaa = mtd[DownDownWksp] + CloneWorkspace(Ipp,"PCalpha") + CropWorkspace(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",StartWorkspaceIndex="0",EndWorkspaceIndex="0") + PCalpha=(mtd['PCalpha']*0.0)+1.0 + alpha=mtd['PCalpha'] + # a1=alpha.readY(0) + # for i in range(0,len(a1)): + # alpha.dataY(0)[i]=0.0 + # alpha.dataE(0)[i]=0.0 + CloneWorkspace("PCalpha","PCrho") + CloneWorkspace("PCalpha","PCAp") + CloneWorkspace("PCalpha","PCPp") + rho=mtd['PCrho'] + Ap=mtd['PCAp'] + Pp=mtd['PCPp'] + # for i in range(0,len(a1)): + # x=(alpha.dataX(0)[i]+alpha.dataX(0)[i])/2.0 + # for j in range(0,4): + # alpha.dataY(0)[i]=alpha.dataY(0)[i]+calpha[j]*x**j + # rho.dataY(0)[i]=rho.dataY(0)[i]+crho[j]*x**j + # Ap.dataY(0)[i]=Ap.dataY(0)[i]+cAp[j]*x**j + # Pp.dataY(0)[i]=Pp.dataY(0)[i]+cPp[j]*x**j + # Use the polynomial corretion fn instead + PolynomialCorrection(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",Coefficients=calpha,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCrho",OutputWorkspace="PCrho",Coefficients=crho,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCAp",OutputWorkspace="PCAp",Coefficients=cAp,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCPp",OutputWorkspace="PCPp",Coefficients=cPp,Operation="Multiply") + + A0 = (Iaa * Pp * Ap) + (Ap * Ipa * rho * Pp) + (Ap * Iap * Pp * alpha) + (Ipp * Ap * alpha * rho * Pp) + A1 = Pp * Iaa + A2 = Pp * Iap + A3 = Ap * Iaa + A4 = Ap * Ipa + A5 = Ap * alpha * Ipp + A6 = Ap * alpha * Iap + A7 = Pp * rho * Ipp + A8 = Pp * rho * Ipa + D = Pp * Ap *( 1.0 + rho + alpha + (rho * alpha) ) + nIpp = (A0 - A1 + A2 - A3 + A4 + A5 - A6 + A7 - A8 + Ipp + Iaa - Ipa - Iap) / D + nIaa = (A0 + A1 - A2 + A3 - A4 - A5 + A6 - A7 + A8 + Ipp + Iaa - Ipa - Iap) / D + nIpa = (A0 - A1 + A2 + A3 - A4 - A5 + A6 + A7 - A8 - Ipp - Iaa + Ipa + Iap) / D + nIap = (A0 + A1 - A2 - A3 + A4 + A5 - A6 - A7 + A8 - Ipp - Iaa + Ipa + Iap) / D + RenameWorkspace(nIpp,str(Ipp)+"corr") + RenameWorkspace(nIpa,str(Ipa)+"corr") + RenameWorkspace(nIap,str(Iap)+"corr") + RenameWorkspace(nIaa,str(Iaa)+"corr") + ReplaceSpecialValues(str(Ipp)+"corr",str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") + ReplaceSpecialValues(str(Ipp)+"corr",str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") + ReplaceSpecialValues(str(Ipp)+"corr",str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") + ReplaceSpecialValues(str(Ipp)+"corr",str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") + iwksp=mtd.getWorkspaceNames() + list=[str(Ipp),str(Ipa),str(Iap),str(Iaa),"PCalpha","PCrho","PCAp","PCPp","1_p"] + for i in range(len(iwksp)): + for j in list: + lname=len(j) + if iwksp[i] [0:lname+1] == j+"_": + mtd.deleteWorkspace(iwksp[i]) + mtd.deleteWorkspace("PCalpha") + mtd.deleteWorkspace("PCrho") + mtd.deleteWorkspace("PCAp") + mtd.deleteWorkspace("PCPp") + mtd.deleteWorkspace('A0') + mtd.deleteWorkspace('A1') + mtd.deleteWorkspace('A2') + mtd.deleteWorkspace('A3') + mtd.deleteWorkspace('A4') + mtd.deleteWorkspace('A5') + mtd.deleteWorkspace('A6') + mtd.deleteWorkspace('A7') + mtd.deleteWorkspace('A8') + mtd.deleteWorkspace('D') + +def nrPNRFn(runList,nameList,incidentAngles,DBList,specChan,minSpec,maxSpec,gparams,floodfile,PNRwithPA,pnums,doCorrs,doLDCorrs="0",subbgd=0,diagnostics=0): + nlist=parseNameList(nameList) + mtd.sendLogMessage("This is the sample nameslist:"+str(nlist)) + rlist=parseRunList(runList) + mtd.sendLogMessage("This is the sample runlist:"+str(rlist)) + dlist=parseNameList(DBList) + mtd.sendLogMessage("This is the Direct Beam nameslist:"+str(dlist)) + incAngles=parseNameList(incidentAngles) + mtd.sendLogMessage("This incident Angles are:"+str(incAngles)) + + if PNRwithPA == "2": + nper=4 + mtd.sendLogMessage("PNRwithPA="+str(PNRwithPA)) + mtd.sendLogMessage(str(pnums)) + else: + nper=2 + + for i in range(len(rlist)): + addRuns(rlist[i],nlist[i]) + + mon_spec=int(gparams[3])-1 + minSp=int(minSpec) + maxSp=int(maxSpec) + reb=gparams[0]+","+gparams[1]+","+gparams[2] + + k=0 + for i in nlist: + a1=mtd[i+"_1"] + nspec=a1.getNumberHistograms() + if (subbgd==1 and nspec!=4): + # If a background subtraction is required sum the bgd outside the + # area of the detector that is visible through the analyser over all periods and average + CloneWorkspace(i,"bgdtemp") + ConvertUnits(InputWorkspace="bgdtemp",OutputWorkspace="bgdtemp",Target="Wavelength",AlignBins="1") + Rebin(InputWorkspace="bgdtemp",OutputWorkspace="bgdtemp",Params=reb) + CropWorkspace(InputWorkspace="bgdtemp",OutputWorkspace="bgdtemp",StartWorkspaceIndex=4,EndWorkspaceIndex=243) + Plus("bgdtemp"+"_"+pnums[0],"bgdtemp"+"_"+pnums[1],OutputWorkspace="wbgdsum") + if (nper>2): + for j in range(2,nper): + Plus("wbgdsum","bgdtemp"+"_"+pnums[j],OutputWorkspace="wbgdsum") + GroupDetectors("wbgdsum","bgd2",WorkspaceIndexList=range(0,50),KeepUngroupedSpectra="0") + GroupDetectors("wbgdsum","bgd1",WorkspaceIndexList=range(160,240),KeepUngroupedSpectra="0") + Plus("bgd1","bgd2",OutputWorkspace="bgd") + wbgdtemp=mtd["bgd"]/(130.0*nper) + mtd.deleteWorkspace("bgdtemp") + mtd.deleteWorkspace("wbgdsum") + mtd.deleteWorkspace("bgd1") + mtd.deleteWorkspace("bgd2") + mtd.deleteWorkspace("bgd") + + wksp=i + ConvertUnits(InputWorkspace=wksp,OutputWorkspace=wksp,Target="Wavelength",AlignBins="1") + Rebin(InputWorkspace=wksp,OutputWorkspace=wksp,Params=reb) + CropWorkspace(InputWorkspace=wksp,OutputWorkspace=wksp+"mon",StartWorkspaceIndex=mon_spec,EndWorkspaceIndex=mon_spec) + if nspec == 4: + CropWorkspace(InputWorkspace=wksp,OutputWorkspace=wksp+"det",StartWorkspaceIndex=3,EndWorkspaceIndex=3) + RotateInstrumentComponent(wksp+"det","DetectorBench",X="-1.0",Angle=str(2.0*float(incAngles[k]))) + Divide(LHSWorkspace=wksp+"det",RHSWorkspace=wksp+"mon",OutputWorkspace=wksp+"norm") + if dlist[k] != "none": + Divide(LHSWorkspace=wksp+"norm",RHSWorkspace=dlist[k],OutputWorkspace=wksp+"norm") + ReplaceSpecialValues(wksp+"norm",wksp+"norm","0.0","0.0","0.0","0.0") + ConvertUnits(wksp+"norm",wksp+"RvQ",Target="MomentumTransfer") + else: + CropWorkspace(InputWorkspace=wksp,OutputWorkspace=wksp+"det",StartWorkspaceIndex=4,EndWorkspaceIndex=243) + # move the first spectrum in the list onto the beam centre so that when the bench is rotated it's in the right place + MoveInstrumentComponent(wksp+"det","DetectorBench",Y=str((125.0-float(minSpec))*1.2e-3)) + # add a bit to the angle to put the first spectrum of the group in the right place + a1=2.0*float(incAngles[k])+atan((float(minSpec)-float(specChan))*1.2e-3/3.53)*180.0/pi + #print str(2.0*float(incAngles[k]))+" "+str(atan((float(minSpec)-float(specChan))*1.2e-3/3.63)*180.0/pi)+" "+str(a1) + RotateInstrumentComponent(wksp+"det","DetectorBench",X="-1.0",Angle=str(a1)) + floodnorm(wksp+"det",floodfile) + if (subbgd==1): + # Subract a per spectrum background + Minus(wksp+"det",wbgdtemp,OutputWorkspace=wksp+"det") + ResetNegatives(InputWorkspace=wksp+"det",OutputWorkspace=wksp+"det",AddMinimum='0',ResetValue="0.0") + GroupDetectors(wksp+"det",wksp+"sum",WorkspaceIndexList=range(int(minSpec)-5,int(maxSpec)-5+1),KeepUngroupedSpectra="0") + else: + GroupDetectors(wksp+"det",wksp+"sum",WorkspaceIndexList=range(int(minSpec)-5,int(maxSpec)-5+1),KeepUngroupedSpectra="0") + RebinToWorkspace(WorkspaceToRebin=wksp+"sum",WorkspaceToMatch=wksp+"mon",OutputWorkspace=wksp+"sum") + Divide(LHSWorkspace=wksp+"sum",RHSWorkspace=wksp+"mon",OutputWorkspace=wksp+"norm") + RebinToWorkspace(WorkspaceToRebin=wksp+"det",WorkspaceToMatch=wksp+"mon",OutputWorkspace=wksp+"det") + Divide(LHSWorkspace=wksp+"det",RHSWorkspace=wksp+"mon",OutputWorkspace=wksp+"detnorm") + if dlist[k] != "none": + Divide(LHSWorkspace=wksp+"norm",RHSWorkspace=dlist[k],OutputWorkspace=wksp+"norm") + ReplaceSpecialValues(wksp+"norm",wksp+"norm","0.0","0.0","0.0","0.0") + Divide(LHSWorkspace=wksp+"detnorm",RHSWorkspace=dlist[k],OutputWorkspace=wksp+"detnorm") + ReplaceSpecialValues(wksp+"detnorm",wksp+"detnorm","0.0","0.0","0.0","0.0") + ConvertUnits(wksp+"norm",wksp+"RvQ",Target="MomentumTransfer") + DeleteWorkspace(wksp+"sum") + + DeleteWorkspace(wksp+"mon") + DeleteWorkspace(wksp+"det") + if doCorrs != "0": + if nper == 2: + nrPNRCorrection(i+"norm_"+pnums[0],i+"norm_"+pnums[1]) + for j in range(2): + RenameWorkspace(InputWorkspace=i+"norm"+"_"+pnums[j]+"corr",OutputWorkspace=i+"normcorr"+"_"+pnums[j]) + GroupWorkspaces(InputWorkspaces=i+"normcorr_"+pnums[0]+","+i+"normcorr_"+pnums[1],OutputWorkspace=i+"normcorr") + ConvertUnits(InputWorkspace=i+"normcorr",OutputWorkspace=i+"normcorrRvQ",Target="MomentumTransfer") + if (nspec > 4 and doLDCorrs != "0"): + nrPNRCorrection(i+"detnorm_"+pnums[0],i+"detnorm_"+pnums[1]) + for j in range(4): + RenameWorkspace(InputWorkspace=i+"detnorm"+"_"+pnums[j]+"corr",OutputWorkspace=i+"detnormcorr"+"_"+pnums[j]) + GroupWorkspaces(InputWorkspaces=i+"detnormcorr_"+pnums[0]+","+i+"detnormcorr_"+pnums[1],OutputWorkspace=i+"detnormcorr") + else: + nrPACorrection(i+"norm"+"_"+pnums[0],i+"norm"+"_"+pnums[1],i+"norm"+"_"+pnums[2],i+"norm"+"_"+pnums[3]) + for j in range(4): + RenameWorkspace(InputWorkspace=i+"norm"+"_"+pnums[j]+"corr",OutputWorkspace=i+"normcorr"+"_"+pnums[j]) + GroupWorkspaces(InputWorkspaces=i+"normcorr_"+pnums[0]+","+i+"normcorr_"+pnums[1]+","+i+"normcorr_"+pnums[2]+","+i+"normcorr_"+pnums[3]+"",OutputWorkspace=i+"normcorr") + ConvertUnits(InputWorkspace=i+"normcorr",OutputWorkspace=i+"normcorrRvQ",Target="MomentumTransfer") + if (nspec > 4 and doLDCorrs != "0"): + nrPACorrection(i+"detnorm_"+pnums[0],i+"detnorm_"+pnums[1],i+"detnorm_"+pnums[2],i+"detnorm_"+pnums[3]) + for j in range(4): + RenameWorkspace(InputWorkspace=i+"detnorm"+"_"+pnums[j]+"corr",OutputWorkspace=i+"detnormcorr"+"_"+pnums[j]) + GroupWorkspaces(InputWorkspaces=i+"detnormcorr_"+pnums[0]+","+i+"detnormcorr_"+pnums[1]+","+i+"detnormcorr_"+pnums[2]+","+i+"detnormcorr_"+pnums[3]+"",OutputWorkspace=i+"detnormcorr") + if (diagnostics == 0 and doCorrs != "0"): + DeleteWorkspace(i+"norm") + DeleteWorkspace(i+"RvQ") + if (diagnostics == 0 and doLDCorrs != "0"): + DeleteWorkspace(i+"detnorm") + k=k+1 + DeleteWorkspace(i) + if (subbgd==1): + DeleteWorkspace("wbgdtemp") + +def tl(wksp,th0,schan): + pixel=1.2 + dist=3630 + ThetaInc=th0*pi/180.0 + a1=mtd[wksp] + y=a1.readY(0) + ntc=len(y) + nspec=a1.getNumberHistograms() + x1=n.zeros((nspec+1,ntc+1)) + theta=n.zeros((nspec+1,ntc+1)) + y1=n.zeros((nspec,ntc)) + e1=n.zeros((nspec,ntc)) + for i in range(0,nspec): + x=a1.readX(i) + y=a1.readY(i) + e=a1.readE(i) + x1[i,0:ntc+1]=x[0:ntc+1] + theta[i,:]=atan2( (i - schan-0.5) * pixel + dist * tan(ThetaInc) , dist)*180/pi + y1[i,0:ntc]=y[0:ntc] + e1[i,0:ntc]=e[0:ntc] + x1[nspec,:]=x1[nspec-1,:] + theta[nspec,:]=atan2( (nspec - schan-0.5) * pixel + dist * tan(ThetaInc) , dist)*180/pi + d1=[x1,theta,y1,e1] + return d1 + +def writemap_tab(dat,th0,spchan,fname): + a1=tl(dat,th0,spchan) + f=open(fname,'w') + x=a1[0] + y=a1[1] + z=a1[2] + e=a1[3] + s="\t" + for i in range(0,n.shape(z)[1]-1): + s+="%g\t" % ((x[0][i]+x[0][i+1])/2.0) + s+="%g\t" % ((x[0][i]+x[0][i+1])/2.0) + s+="\n" + f.write(s) + for i in range(0,n.shape(y)[0]-1): + s="" + s+="%g\t" % ((y[i][0]+y[i+1][0])/2.0) + for j in range(0,n.shape(z)[1]-1): + s+="%g\t" % z[i][j] + s+="%g\t" % e[i][j] + s+="\n" + f.write(s) + f.close() + +def xye(wksp): + a1=mtd[wksp] + x1=a1.readX(0) + X1=n.zeros((len(x1)-1)) + for i in range(0,len(x1)-1): + X1[i]=(x1[i]+x1[i+1])/2.0 + y1=a1.readY(0) + e1=a1.readE(0) + d1=[X1,y1,e1] + return d1 + +def writeXYE_tab(dat,fname): + a1=xye(dat) + f=open(fname,'w') + x=a1[0] + y=a1[1] + e=a1[2] + s="" + s+="x\ty\te\n" + f.write(s) + for i in range(len(x)): + s="" + s+="%f\t" % x[i] + s+="%f\t" % y[i] + s+="%f\n" % e[i] + f.write(s) + f.close() + +''' +def quickPlot(runlist,dataDir,lmin,reb,lmax,spmin,spmax,output,plotper,polper,zmin,zmax,zlog): + + isisDataDir=dataDir + mtd.sendLogMessage("setting dataDir="+dataDir+" "+isisDataDir) + deleteFromRootName(output) + addruns(runlist,output) + nper=nperFromList(output) + rebpars=str(lmin)+","+str(reb)+","+str(lmax) + + if nper == 1: + ConvertUnits(InputWorkspace=output,OutputWorkspace=output,Target="Wavelength",AlignBins="1") + Rebin(InputWorkspace=output,OutputWorkspace=output,Params=rebpars) + CropWorkspace(InputWorkspace=output,OutputWorkspace=output+"m",StartWorkspaceIndex="1",EndWorkspaceIndex="1") + CropWorkspace(InputWorkspace=output,OutputWorkspace=output+"d",StartWorkspaceIndex=str(spmin+4),EndWorkspaceIndex=str(spmax+4)) + Divide(output+"d",output+"m",output+"n") + workspace_mtx=qti.app.mantidUI.importMatrixWorkspace(output+"n") + gr2d=workspace_mtx.plotGraph2D() + l=gr2d.activeLayer() + if zlog == 0: + l.setAxisScale(1,zmin,zmax,0) + else: + l.setAxisScale(1,zmin,zmax,1) + else: + workspace_mtx=[] + nplot=0 + for i in plotper: + ConvertUnits(InputWorkspace=output+"_"+str(i),OutputWorkspace=output+"_"+str(i),Target="Wavelength",AlignBins="1") + Rebin(InputWorkspace=output+"_"+str(i),OutputWorkspace=output+"_"+str(i),Params=rebpars) + CropWorkspace(InputWorkspace=output+"_"+str(i),OutputWorkspace=output+"_"+str(i)+"m",StartWorkspaceIndex="1",EndWorkspaceIndex="1") + CropWorkspace(InputWorkspace=output+"_"+str(i),OutputWorkspace=output+"_"+str(i)+"d",StartWorkspaceIndex=str(spmin+4),EndWorkspaceIndex=str(spmax+4)) + Divide(output+"_"+str(i)+"d",output+"_"+str(i)+"m",output+"_"+str(i)+"n") + workspace_mtx.append(qti.app.mantidUI.importMatrixWorkspace(output+"_"+str(i)+"n")) + gr2d=workspace_mtx[nplot].plotGraph2D() + nplot=nplot+1 + l=gr2d.activeLayer() + if zlog == 0: + l.setAxisScale(1,zmin,zmax,0) + else: + l.setAxisScale(1,zmin,zmax,1) + + up=mtd[output+"_2d"] + down=mtd[output+"_1d"] + asym=(up-down)/(down+up) + RenameWorkspace(asym,output+"_asym") + ReplaceSpecialValues(output+"_asym",output+"_asym","0.0","0.0","0.0","0.0") + workspace_mtx.append(qti.app.mantidUI.importMatrixWorkspace(output+"_asym")) + gr2d=workspace_mtx[nplot].plotGraph2D() + l=gr2d.activeLayer() + l.setAxisScale(1,-1.0,1.0,0) + + + mtd.sendLogMessage("quickPlot Finished") +''' diff --git a/Code/Mantid/scripts/Reflectometry/isis_reflectometry/quick.py b/Code/Mantid/scripts/Reflectometry/isis_reflectometry/quick.py index d84e42c75eef..baf9a7401c08 100644 --- a/Code/Mantid/scripts/Reflectometry/isis_reflectometry/quick.py +++ b/Code/Mantid/scripts/Reflectometry/isis_reflectometry/quick.py @@ -10,131 +10,148 @@ #from ReflectometerCors import * from l2q import * from combineMulti import * -#from mantidsimple import * # Old API from mantid.simpleapi import * # New API -from mantid.api import WorkspaceGroup + +from mantid.api import WorkspaceGroup, MatrixWorkspace +from mantid.kernel import logger from convert_to_wavelength import ConvertToWavelength import math import re +import abc +def enum(**enums): + return type('Enum', (), enums) -def quick(run, theta=0, pointdet=1,roi=[0,0], db=[0,0], trans='', polcorr=0, usemon=-1,outputType='pd', debug=0): - ''' - call signature(s):: - - x=quick(RunNumber) - x=quick(RunNumber, roi=[0,0], db=[0,0], trans=0, outputType='pd') - x=quick(RunNumber,[1,10]) - x=quick(RunNumber,[1,10],[20,40]) - x=quick(RunNumber, trans=2568) - x=quick(RunNumber, trans='SomeSavedWorkspaceName') - - Reduces a ISIS raw or nexus file created on one of the reflectometers applying - only a minimum ammount of corrections. The data is left in terms of lambda. - - Required arguments - ========= ===================================================================== - RunNumber Either an ISIS run number when the paths are set up correctly or - the full path and filename if an ISIS raw file. - ========= ===================================================================== - - Optional keyword arguments: - ========= ===================================================================== - Keyword Description - ========= ===================================================================== - roi Region of interest marking the extent of the reflected beam. - default [0,0] - db Region of interest marking the extent of the direct beam. - default [0,0] - trans transmission run number or saved workspace. The default is 0 (No - transmission run). trans=-1 will supress the division of the - detector by the monitor. - polcorr polarisation correction, 0=no correction (unpolarised run) - usemon monitor to be used for normalisation (-1 is default from IDF) - outputType 'pd' = point detector (Default), 'md'= Multidetector Will use - this to build the equivalent of gd in the old matlab code but - keep all of the simple detector processing in one well organized - function. This should not be used by the average user. - ========= ===================================================================== - - Outputs: - ========= ===================================================================== - x Either a single mantid workspace or worspace group or an array - of them. - ========= ===================================================================== - - Working examples: - >>> # reduce a data set with the default parameters - >>> x=quick(/Users/trc/Dropbox/Work/PolrefTest/POLREF00003014.raw") - - >>> # reduce a data set with a transmission run - >>> t=quick(/Users/trc/Dropbox/Work/PolrefTest/POLREF00003010.raw") - >>> x=quick(/Users/trc/Dropbox/Work/PolrefTest/POLREF00003014.raw", trans=t) - - >>> # reduce a data set using the multidetector and output a single reflectivity - >>> # where the reflected beam is between channel 121 and 130. - >>> x=quick(/Users/trc/Dropbox/Work/PolrefTest/POLREF00003014.raw", [121,130]) - - - Also see: pol - - ToDo: - 1) code for the transmisson DONE! - 2) Similar to the genie on polref add extraction from the multidetector - 3) need to make the variables stored in the frame work contain the run number. DONE! - - ''' - - ''' Notes for developers: +PolarisationCorrection = enum(PNR=1, PA=2, NONE=3) - Naming conventions for workspaces which live in the mantid framework are as follows: +class CorrectionStrategy(object): + __metaclass__ = abc.ABCMeta # Mark as an abstract class + + @abc.abstractmethod + def apply(self, to_correct): + pass + +class ExponentialCorrectionStrategy(CorrectionStrategy): - It's nearly random. this needs to be fixed so that name clashes do not occur. - May try adding a pair of underscores to the front of the name. + def __init__(self, c0, c1): + self.__c0 = c0 + self.__c1 = c1 + + def apply(self, to_correct): + logger.information("Exponential Correction") + corrected = ExponentialCorrection(InputWorkspace=to_correct,C0=self.__c0, C1= self.__c1, Operation='Divide') + return corrected + +class PolynomialCorrectionStrategy(CorrectionStrategy): + def __init__(self, poly_string): + self.__poly_string = poly_string + + def apply(self, to_correct): + logger.information("Polynomial Correction") + corrected = PolynomialCorrection(InputWorkspace=to_correct, Coefficients=self.__poly_string, Operation='Divide') + return corrected + +class NullCorrectionStrategy(CorrectionStrategy): + def apply(self, to_correct): + logger.information("Null Correction") + out = to_correct.clone() + return out + +def quick(run, theta=0, pointdet=True,roi=[0,0], db=[0,0], trans='', polcorr=0, usemon=-1,outputType='pd', + debug=False, stitch_start_overlap=10, stitch_end_overlap=12, stitch_params=[1.5, 0.02, 17], + pol_corr=False, detector_component_name='point-detector', sample_component_name='some-surface-holder', + correct_positions=True ): + ''' + Original quick parameters fetched from IDF ''' - run_ws = ConvertToWavelength.to_workspace(run) idf_defaults = get_defaults(run_ws) - to_lam = ConvertToWavelength(run_ws) - nHist = run_ws.getNumberHistograms() - I0MonitorIndex = idf_defaults['I0MonitorIndex'] - MultiDetectorStart = idf_defaults['MultiDetectorStart'] + i0_monitor_index = idf_defaults['I0MonitorIndex'] + multi_detector_start = idf_defaults['MultiDetectorStart'] lambda_min = idf_defaults['LambdaMin'] lambda_max = idf_defaults['LambdaMax'] - detector_index_ranges = (idf_defaults['PointDetectorStart'], idf_defaults['PointDetectorStop']) + point_detector_start = idf_defaults['PointDetectorStart'] + point_detector_stop = idf_defaults['PointDetectorStop'] + multi_detector_start = idf_defaults['MultiDetectorStart'] background_min = idf_defaults['MonitorBackgroundMin'] background_max = idf_defaults['MonitorBackgroundMax'] - intmin = idf_defaults['MonitorIntegralMin'] - intmax = idf_defaults['MonitorIntegralMax'] + int_min = idf_defaults['MonitorIntegralMin'] + int_max = idf_defaults['MonitorIntegralMax'] + correction_strategy = idf_defaults['AlgoritmicCorrection'] + crho = None + calpha = None + cAp = None + cPp = None + if pol_corr: + crho = idf_defaults['crho'] + calpha = idf_defaults['calpha'] + cAp = idf_defaults['cAp'] + cPp = idf_defaults['cPp'] + + + return quick_explicit(run=run, i0_monitor_index = i0_monitor_index, lambda_min = lambda_min, lambda_max = lambda_max, + point_detector_start = point_detector_start, point_detector_stop = point_detector_stop, + multi_detector_start = multi_detector_start, background_min = background_min, background_max = background_max, + int_min = int_min, int_max = int_max, theta = theta, pointdet = pointdet, roi = roi, db = db, trans = trans, + debug = debug, correction_strategy = correction_strategy, stitch_start_overlap=stitch_start_overlap, + stitch_end_overlap=stitch_end_overlap, stitch_params=stitch_params, pol_corr=pol_corr, crho=crho, calpha=calpha, cAp=cAp, cPp=cPp, + detector_component_name=detector_component_name, sample_component_name=sample_component_name, correct_positions=correct_positions) + - _monitor_ws, _detector_ws = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=I0MonitorIndex, correct_monitor=True, bg_min=background_min, bg_max=background_max ) +def quick_explicit(run, i0_monitor_index, lambda_min, lambda_max, background_min, background_max, int_min, int_max, + point_detector_start=0, point_detector_stop=0, multi_detector_start=0, theta=0, + pointdet=True,roi=[0,0], db=[0,0], trans='', debug=False, correction_strategy=NullCorrectionStrategy(), + stitch_start_overlap=None, stitch_end_overlap=None, stitch_params=None, + pol_corr=False, crho=None, calpha=None, cAp=None, cPp=None, detector_component_name='point-detector', + sample_component_name='some-surface-holder', correct_positions=True ): + + ''' + Version of quick where all parameters are explicitly provided. + ''' + sample_ws = ConvertToWavelength.to_single_workspace(run) + to_lam = ConvertToWavelength(run) + nHist = sample_ws.getNumberHistograms() + + if pointdet: + detector_index_ranges = (point_detector_start, point_detector_stop) + else: + detector_index_ranges = (multi_detector_start, nHist-1) + + + _monitor_ws, _detector_ws = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=i0_monitor_index, correct_monitor=True, bg_min=background_min, bg_max=background_max ) - inst = run_ws.getInstrument() + inst = sample_ws.getInstrument() # Some beamline constants from IDF - print I0MonitorIndex + print i0_monitor_index print nHist - if (nHist > 5 and not(pointdet)): + + if (run=='0'): + RunNumber = '0' + else: + RunNumber = groupGet(sample_ws.getName(),'samp','run_number') + + if not pointdet: # Proccess Multi-Detector; assume MD goes to the end: # if roi or db are given in the function then sum over the apropriate channels print "This is a multidetector run." - try: - _I0M = RebinToWorkspace(WorkspaceToRebin=_monitor_ws,WorkspaceToMatch=_detector_ws) - IvsLam = _detector_ws / _IOM - if (roi != [0,0]) : - ReflectedBeam = SumSpectra(InputWorkspace=IvsLam, StartWorkspaceIndex=roi[0], EndWorkspaceIndex=roi[1]) - if (db != [0,0]) : - DirectBeam = SumSpectra(InputWorkspace=_detector_ws, StartWorkspaceIndex=db[0], EndWorkspaceIndex=db[1]) - except SystemExit: - print "Point-Detector only run." - RunNumber = groupGet(IvsLam.getName(),'samp','run_number') - if (theta): - IvsQ = l2q(ReflectedBeam, 'linear-detector', theta) # TODO: possible to get here and an invalid state if roi == [0,0] see above. + + _I0M = RebinToWorkspace(WorkspaceToRebin=_monitor_ws,WorkspaceToMatch=_detector_ws) + IvsLam = _detector_ws / _I0M + if (roi != [0,0]) : + ReflectedBeam = SumSpectra(InputWorkspace=IvsLam, StartWorkspaceIndex=roi[0], EndWorkspaceIndex=roi[1]) + if (db != [0,0]) : + DirectBeam = SumSpectra(InputWorkspace=_detector_ws, StartWorkspaceIndex=db[0], EndWorkspaceIndex=db[1]) + ReflectedBeam = ReflectedBeam / DirectBeam + polCorr(pol_corr, IvsLam, crho, calpha, cAp, cPp) + if (theta and correct_positions): + IvsQ = l2q(ReflectedBeam, 'linear-detector', theta) else: IvsQ = ConvertUnits(InputWorkspace=ReflectedBeam, Target="MomentumTransfer") - + + # Single Detector processing------------------------------------------------------------- else: print "This is a Point-Detector run." @@ -145,36 +162,27 @@ def quick(run, theta=0, pointdet=1,roi=[0,0], db=[0,0], trans='', polcorr=0, use # Normalise by good frames GoodFrames = groupGet(IvsLam.getName(),'samp','goodfrm') print "run frames: ", GoodFrames - if (run=='0'): - RunNumber = '0' - else: - RunNumber = groupGet(IvsLam.getName(),'samp','run_number') + if (trans==''): print "No transmission file. Trying default exponential/polynomial correction..." - inst=groupGet(_detector_ws.getName(),'inst') - corrType=inst.getStringParameter('correction')[0] - if (corrType=='polynomial'): - pString=inst.getStringParameter('polystring') - print pString - if len(pString): - IvsLam = PolynomialCorrection(InputWorkspace=_detector_ws,Coefficients=pString[0],Operation='Divide') - else: - print "No polynomial coefficients in IDF. Using monitor spectrum with no corrections." - elif (corrType=='exponential'): - c0=inst.getNumberParameter('C0') - c1=inst.getNumberParameter('C1') - print "Exponential parameters: ", c0[0], c1[0] - if len(c0): - IvsLam = ExponentialCorrection(InputWorkspace=_detector_ws,C0=c0[0],C1=c1[0],Operation='Divide') + IvsLam = correction_strategy.apply(_detector_ws) IvsLam = Divide(LHSWorkspace=IvsLam, RHSWorkspace=_I0P) else: # we have a transmission run - _monInt = Integration(InputWorkspace=_I0P,RangeLower=intmin,RangeUpper=intmax) + _monInt = Integration(InputWorkspace=_I0P,RangeLower=int_min,RangeUpper=int_max) IvsLam = Divide(LHSWorkspace=_detector_ws,RHSWorkspace=_monInt) names = mtd.getObjectNames() - IvsLam = transCorr(trans, IvsLam) + + IvsLam = transCorr(trans, IvsLam, lambda_min, lambda_max, background_min, background_max, + int_min, int_max, detector_index_ranges, i0_monitor_index, stitch_start_overlap, + stitch_end_overlap, stitch_params ) + RenameWorkspace(InputWorkspace=IvsLam, OutputWorkspace="IvsLam") # TODO: Hardcoded names are bad - + + + IvsLam = polCorr(pol_corr, IvsLam, crho, calpha, cAp, cPp) + + # Convert to I vs Q # check if detector in direct beam if (theta == 0 or theta == ''): @@ -182,87 +190,137 @@ def quick(run, theta=0, pointdet=1,roi=[0,0], db=[0,0], trans='', polcorr=0, use theta = 0 print "given theta = ",theta inst = groupGet('IvsLam','inst') - detLocation=inst.getComponentByName('point-detector').getPos() - sampleLocation=inst.getComponentByName('some-surface-holder').getPos() - detLocation=inst.getComponentByName('point-detector').getPos() + detLocation=inst.getComponentByName(detector_component_name).getPos() + sampleLocation=inst.getComponentByName(sample_component_name).getPos() + detLocation=inst.getComponentByName(detector_component_name).getPos() sample2detector=detLocation-sampleLocation # metres source=inst.getSource() beamPos = sampleLocation - source.getPos() PI = 3.1415926535 - theta = inst.getComponentByName('point-detector').getTwoTheta(sampleLocation, beamPos)*180.0/PI/2.0 + theta = inst.getComponentByName(detector_component_name).getTwoTheta(sampleLocation, beamPos)*180.0/PI/2.0 print "Det location: ", detLocation, "Calculated theta = ",theta - if detLocation.getY() == 0: # detector is not in correct place + if correct_positions: # detector is not in correct place # Get detector angle theta from NeXuS + logger.information('The detectorlocation is not at Y=0') theta = groupGet(run_ws,'samp','theta') print 'Nexus file theta =', theta - IvsQ = l2q(mtd['IvsLam'], 'point-detector', theta) + IvsQ = l2q(IvsLam, detector_component_name, theta, sample_component_name) else: - ConvertUnits(InputWorkspace='IvsLam',OutputWorkspace="IvsQ",Target="MomentumTransfer") + IvsQ = ConvertUnits(InputWorkspace=IvsLam,OutputWorkspace="IvsQ",Target="MomentumTransfer") - else: - theta = float(theta) - IvsQ = l2q(mtd['IvsLam'], 'point-detector', theta) + else: + if correct_positions: + theta = float(theta) + IvsQ = l2q(IvsLam, detector_component_name, theta, sample_component_name) + else: + IvsQ = ConvertUnits(InputWorkspace=IvsLam,OutputWorkspace="IvsQ",Target="MomentumTransfer") - RenameWorkspace(InputWorkspace='IvsLam',OutputWorkspace=RunNumber+'_IvsLam') - RenameWorkspace(InputWorkspace='IvsQ',OutputWorkspace=RunNumber+'_IvsQ') + RenameWorkspace(InputWorkspace=IvsLam,OutputWorkspace=RunNumber+'_IvsLam') + if isinstance(IvsLam, WorkspaceGroup): + counter = 0 + for ws in IvsLam: + RenameWorkspace(ws, OutputWorkspace=RunNumber+'_IvsLam_'+str(counter)) + counter += 1 + RenameWorkspace(InputWorkspace=IvsQ,OutputWorkspace=RunNumber+'_IvsQ') # delete all temporary workspaces unless in debug mode (debug=1) - if debug == 0: - pass + if debug != 0: cleanup() + return mtd[RunNumber+'_IvsLam'], mtd[RunNumber+'_IvsQ'], theta - -def transCorr(transrun, i_vs_lam): +def make_trans_corr(transrun, stitch_start_overlap, stitch_end_overlap, stitch_params, + lambda_min=None, lambda_max=None, background_min=None, + background_max=None, int_min=None, int_max=None, detector_index_ranges=None, + i0_monitor_index=None): + ''' + Make the transmission correction workspace. + ''' - run_ws = ConvertToWavelength.to_workspace(i_vs_lam) - idf_defaults = get_defaults(run_ws) - I0MonitorIndex = idf_defaults['I0MonitorIndex'] - MultiDetectorStart = idf_defaults['MultiDetectorStart'] - lambda_min = idf_defaults['LambdaMin'] - lambda_max = idf_defaults['LambdaMax'] - background_min = idf_defaults['MonitorBackgroundMin'] - background_max = idf_defaults['MonitorBackgroundMax'] - intmin = idf_defaults['MonitorIntegralMin'] - intmax = idf_defaults['MonitorIntegralMax'] - detector_index_ranges = (idf_defaults['PointDetectorStart'], idf_defaults['PointDetectorStop']) + ''' + Check to see whether all optional inputs have been provide. If not we have to get them from the IDF. + ''' + if not all((lambda_min, lambda_max, background_min, background_max, int_min, int_max, detector_index_ranges, i0_monitor_index)): + logger.notice("make_trans_corr: Fetching missing arguments from the IDF") + instrument_source = transrun + if isinstance(transrun, str): + instrument_source = transrun.split(',')[0] + trans_ws = ConvertToWavelength.to_workspace(instrument_source) + idf_defaults = get_defaults(trans_ws) + + # Fetch defaults for anything not specified + if not i0_monitor_index: + i0_monitor_index = idf_defaults['I0MonitorIndex'] + if not lambda_min: + lambda_min = idf_defaults['LambdaMin'] + if not lambda_max: + lambda_max = idf_defaults['LambdaMax'] + if not detector_index_ranges: + point_detector_start = idf_defaults['PointDetectorStart'] + point_detector_stop = idf_defaults['PointDetectorStop'] + detector_index_ranges = (point_detector_start, point_detector_stop) + if not background_min: + background_min = idf_defaults['MonitorBackgroundMin'] + if not background_max: + background_max = idf_defaults['MonitorBackgroundMax'] + if not int_min: + int_min = idf_defaults['MonitorIntegralMin'] + if not int_max: + int_max = idf_defaults['MonitorIntegralMax'] transWS = None - if ',' in transrun: + if isinstance(transrun, str) and (',' in transrun): slam = transrun.split(',')[0] llam = transrun.split(',')[1] print "Transmission runs: ", transrun to_lam = ConvertToWavelength(slam) - _monitor_ws_slam, _detector_ws_slam = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=I0MonitorIndex, correct_monitor=True, bg_min=background_min, bg_max=background_max ) + _monitor_ws_slam, _detector_ws_slam = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=i0_monitor_index, correct_monitor=True, bg_min=background_min, bg_max=background_max ) _i0p_slam = RebinToWorkspace(WorkspaceToRebin=_monitor_ws_slam, WorkspaceToMatch=_detector_ws_slam) - _mon_int_trans = Integration(InputWorkspace=_i0p_slam, RangeLower=intmin, RangeUpper=intmax) + _mon_int_trans = Integration(InputWorkspace=_i0p_slam, RangeLower=int_min, RangeUpper=int_max) _detector_ws_slam = Divide(LHSWorkspace=_detector_ws_slam, RHSWorkspace=_mon_int_trans) to_lam = ConvertToWavelength(llam) - _monitor_ws_llam, _detector_ws_llam = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=I0MonitorIndex, correct_monitor=True, bg_min=background_min, bg_max=background_max ) + _monitor_ws_llam, _detector_ws_llam = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=i0_monitor_index, correct_monitor=True, bg_min=background_min, bg_max=background_max ) _i0p_llam = RebinToWorkspace(WorkspaceToRebin=_monitor_ws_llam, WorkspaceToMatch=_detector_ws_llam) - _mon_int_trans = Integration(InputWorkspace=_i0p_llam, RangeLower=intmin,RangeUpper=intmax) + _mon_int_trans = Integration(InputWorkspace=_i0p_llam, RangeLower=int_min,RangeUpper=int_max) _detector_ws_llam = Divide(LHSWorkspace=_detector_ws_llam, RHSWorkspace=_mon_int_trans) - # TODO: HARDCODED STITCHING VALUES!!!!! - _transWS, outputScaling = Stitch1D(LHSWorkspace=_detector_ws_slam, RHSWorkspace=_detector_ws_llam, StartOverlap=10, EndOverlap=12, Params="%f,%f,%f" % (1.5, 0.02, 17)) + print stitch_start_overlap, stitch_end_overlap, stitch_params + _transWS, outputScaling = Stitch1D(LHSWorkspace=_detector_ws_slam, RHSWorkspace=_detector_ws_llam, StartOverlap=stitch_start_overlap, + EndOverlap=stitch_end_overlap, Params=stitch_params) else: to_lam = ConvertToWavelength(transrun) - _monitor_ws_trans, _detector_ws_trans = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=I0MonitorIndex, correct_monitor=True, bg_min=background_min, bg_max=background_max ) + _monitor_ws_trans, _detector_ws_trans = to_lam.convert(wavelength_min=lambda_min, wavelength_max=lambda_max, detector_workspace_indexes=detector_index_ranges, monitor_workspace_index=i0_monitor_index, correct_monitor=True, bg_min=background_min, bg_max=background_max ) _i0p_trans = RebinToWorkspace(WorkspaceToRebin=_monitor_ws_trans, WorkspaceToMatch=_detector_ws_trans) - _mon_int_trans = Integration( InputWorkspace=_i0p_trans, RangeLower=intmin, RangeUpper=intmax ) + _mon_int_trans = Integration( InputWorkspace=_i0p_trans, RangeLower=int_min, RangeUpper=int_max ) _transWS = Divide( LHSWorkspace=_detector_ws_trans, RHSWorkspace=_mon_int_trans ) - + return _transWS + + +def transCorr(transrun, i_vs_lam, lambda_min, lambda_max, background_min, background_max, int_min, int_max, detector_index_ranges, i0_monitor_index, + stitch_start_overlap, stitch_end_overlap, stitch_params ): + """ + Perform transmission corrections on i_vs_lam. + return the corrected result. + """ + if isinstance(transrun, MatrixWorkspace) and transrun.getAxis(0).getUnit().unitID() == "Wavelength" : + _transWS = transrun + else: + # Make the transmission correction workspace. + _transWS = make_trans_corr(transrun, stitch_start_overlap, stitch_end_overlap, stitch_params, + lambda_min, lambda_max, background_min, background_max, + int_min, int_max, detector_index_ranges, i0_monitor_index,) + #got sometimes very slight binning diferences, so do this again: _i_vs_lam_trans = RebinToWorkspace(WorkspaceToRebin=_transWS, WorkspaceToMatch=i_vs_lam) # Normalise by transmission run. @@ -270,16 +328,27 @@ def transCorr(transrun, i_vs_lam): return _i_vs_lam_corrected - +def polCorr(pol_corr, IvsLam, crho, calpha, cAp, cPp): + ''' + Perform polynomial correction + ''' + if pol_corr == PolarisationCorrection.NONE: + logger.information("No Polarization Correction Requested.") + elif pol_corr == PolarisationCorrection.PNR: + IvsLam = nrPNRCorrection(IvsLam.name(), crho[0],calpha[0],cAp[0],cPp[0]) + elif pol_corr == PolarisationCorrection.PA: + IvsLam = nrPACorrection(IvsLam.name(), crho[0],calpha[0],cAp[0],cPp[0]) + return IvsLam + def cleanup(): names = mtd.getObjectNames() for name in names: if re.search("^_", name): DeleteWorkspace(name) -def get_defaults(run_ws): +def get_defaults(run_ws, pol_corr = False): ''' - Temporary helper function. Aid refactoring by removing need to specifically ask things of parameter files. + Fetch out instrument level defaults. ''' defaults = dict() if isinstance(run_ws, WorkspaceGroup): @@ -288,16 +357,208 @@ def get_defaults(run_ws): instrument = run_ws.getInstrument() defaults['LambdaMin'] = float( instrument.getNumberParameter('LambdaMin')[0] ) defaults['LambdaMax'] = float( instrument.getNumberParameter('LambdaMax')[0] ) - defaults['MonitorBackgroundMin'] = float( instrument.getNumberParameter('MonitorBackgroundMin')[0] ) - defaults['MonitorBackgroundMax'] = float( instrument.getNumberParameter('MonitorBackgroundMax')[0] ) - defaults['MonitorIntegralMin'] = float( instrument.getNumberParameter('MonitorIntegralMin')[0] ) + defaults['MonitorBackgroundMin'] = float( instrument.getNumberParameter('MonitorBackgroundMin')[0] ) + defaults['MonitorBackgroundMax'] =float( instrument.getNumberParameter('MonitorBackgroundMax')[0] ) + defaults['MonitorIntegralMin'] = float( instrument.getNumberParameter('MonitorIntegralMin')[0] ) defaults['MonitorIntegralMax'] = float( instrument.getNumberParameter('MonitorIntegralMax')[0] ) defaults['MonitorsToCorrect'] = int( instrument.getNumberParameter('MonitorsToCorrect')[0] ) - defaults['PointDetectorStart'] = int( instrument.getNumberParameter('PointDetectorStart')[0] ) - defaults['PointDetectorStop'] = int( instrument.getNumberParameter('PointDetectorStop')[0] ) + defaults['PointDetectorStart'] = int( instrument.getNumberParameter('PointDetectorStart')[0] ) + defaults['PointDetectorStop'] = int( instrument.getNumberParameter('PointDetectorStop')[0] ) defaults['MultiDetectorStart'] = int( instrument.getNumberParameter('MultiDetectorStart')[0] ) defaults['I0MonitorIndex'] = int( instrument.getNumberParameter('I0MonitorIndex')[0] ) + if pol_corr: + defaults['crho'] = instrument.getNumberParameter('crho') + defaults['calpha'] = instrument.getNumberParameter('calpha') + defaults['cAp'] = instrument.getNumberParameter('cAp') + defaults['cPp'] = instrument.getNumberParameter('cPp') + + + + + correction = NullCorrectionStrategy() + corrType=instrument.getStringParameter('correction')[0] + if corrType == 'polynomial': + poly_string = instrument.getStringParameter('polystring')[0] + correction = PolynomialCorrectionStrategy(poly_string) + elif corrType == 'exponential': + c0=instrument.getNumberParameter('C0')[0] + c1=instrument.getNumberParameter('C1')[0] + correction = ExponentialCorrectionStrategy(c0, c1) + + defaults['AlgoritmicCorrection'] = correction return defaults + + +def doPNR(wksp): + inst = groupGet("_W",'inst') + # Some beamline constants from IDF + crho = inst.getStringParameter('crho') + calpha = inst.getStringParameter('calpha') + cAp = inst.getStringParameter('cAp') + cPp = inst.getStringParameter('cPp') + nrPNRCorrection(wksp,crho[0],calpha[0],cAp[0],cPp[0]) + + +def doPA(wksp): + inst = groupGet("_W",'inst') + # Some beamline constants from IDF + crho = inst.getStringParameter('crho') + calpha = inst.getStringParameter('calpha') + cAp = inst.getStringParameter('cAp') + cPp = inst.getStringParameter('cPp') + nrPACorrection(wksp,crho[0],calpha[0],cAp[0],cPp[0]) + + +def nrPNRCorrection(Wksp,crho,calpha,cAp,cPp): + + # Constants Based on Runs 18350+18355 and 18351+18356 analyser theta at -0.1deg + # 2 RF Flippers as the polarising system + # crho=[1.006831,-0.011467,0.002244,-0.000095] + # calpha=[1.017526,-0.017183,0.003136,-0.000140] + # cAp=[0.917940,0.038265,-0.006645,0.000282] + # cPp=[0.972762,0.001828,-0.000261,0.0] + print "Performing PNR correction with parameters from IDF..." + CloneWorkspace(Wksp,OutputWorkspace='_'+Wksp+'_uncorrected') + if ( (not isinstance(mtd[Wksp], WorkspaceGroup)) or (not mtd[Wksp].size()==2) ): + print "PNR correction works only with exactly 2 periods!" + return mtd[Wksp] + else: + Ip = mtd[Wksp][0] + Ia = mtd[Wksp][1] + + CloneWorkspace(Ip,OutputWorkspace="PCalpha") + CropWorkspace(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",StartWorkspaceIndex="0",EndWorkspaceIndex="0") + PCalpha=(mtd['PCalpha']*0.0)+1.0 + alpha=mtd['PCalpha'] + # a1=alpha.readY(0) + # for i in range(0,len(a1)): + # alpha.dataY(0)[i]=0.0 + # alpha.dataE(0)[i]=0.0 + CloneWorkspace("PCalpha",OutputWorkspace="PCrho") + CloneWorkspace("PCalpha",OutputWorkspace="PCAp") + CloneWorkspace("PCalpha",OutputWorkspace="PCPp") + rho=mtd['PCrho'] + Ap=mtd['PCAp'] + Pp=mtd['PCPp'] + # for i in range(0,len(a1)): + # x=(alpha.dataX(0)[i]+alpha.dataX(0)[i])/2.0 + # for j in range(0,4): + # alpha.dataY(0)[i]=alpha.dataY(0)[i]+calpha[j]*x**j + # rho.dataY(0)[i]=rho.dataY(0)[i]+crho[j]*x**j + # Ap.dataY(0)[i]=Ap.dataY(0)[i]+cAp[j]*x**j + # Pp.dataY(0)[i]=Pp.dataY(0)[i]+cPp[j]*x**j + PolynomialCorrection(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",Coefficients=calpha,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCrho",OutputWorkspace="PCrho",Coefficients=crho,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCAp",OutputWorkspace="PCAp",Coefficients=cAp,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCPp",OutputWorkspace="PCPp",Coefficients=cPp,Operation="Multiply") + D=Pp*(1.0+rho) + nIp=(Ip*(rho*Pp+1.0)+Ia*(Pp-1.0))/D + nIa=(Ip*(rho*Pp-1.0)+Ia*(Pp+1.0))/D + RenameWorkspace(nIp,OutputWorkspace=str(Ip)+"corr") + RenameWorkspace(nIa,OutputWorkspace=str(Ia)+"corr") + + out = GroupWorkspaces(str(Ip)+"corr"+','+str(Ia)+"corr",OutputWorkspace=Wksp) + + #CloneWorkspace=(InputWorkspace="gr",OutputWorkspace=Wksp) + iwksp = mtd.getObjectNames() + list=[str(Ip),str(Ia),"PCalpha","PCrho","PCAp","PCPp","1_p"] + for i in range(len(iwksp)): + for j in list: + lname=len(j) + if iwksp[i] [0:lname+1] == j+"_": + + DeleteWorkspace(iwksp[i]) + + DeleteWorkspace("PCalpha") + DeleteWorkspace("PCrho") + DeleteWorkspace("PCAp") + DeleteWorkspace("PCPp") + DeleteWorkspace("D") + + return out +# +# +def nrPACorrection(Wksp,crho,calpha,cAp,cPp):#UpUpWksp,UpDownWksp,DownUpWksp,DownDownWksp): +# crho=[0.941893,0.0234006,-0.00210536,0.0] +# calpha=[0.945088,0.0242861,-0.00213624,0.0] +# cAp=[1.00079,-0.0186778,0.00131546,0.0] +# cPp=[1.01649,-0.0228172,0.00214626,0.0] + # Constants Based on Runs 18350+18355 and 18351+18356 analyser theta at -0.1deg + # 2 RF Flippers as the polarising system + # Ipa and Iap appear to be swapped in the sequence on CRISP 4 perido data!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + print "Performing PA correction with parameters from IDF..." + CloneWorkspace(Wksp,OutputWorkspace='_'+Wksp+'_uncorrected') + if (not isinstance(mtd[Wksp], WorkspaceGroup)) or (not mtd[Wksp].size()==4) : + print "PNR correction works only with exactly 4 periods (uu,ud,du,dd)!" + return mtd[Wksp] + else: + Ipp = mtd[Wksp][0] + Ipa = mtd[Wksp][1] + Iap = mtd[Wksp][2] + Iaa = mtd[Wksp][3] + + CloneWorkspace(Ipp,OutputWorkspace="PCalpha") + CropWorkspace(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",StartWorkspaceIndex=0,EndWorkspaceIndex=0) + PCalpha=(mtd['PCalpha']*0.0)+1.0 + alpha=mtd['PCalpha'] + CloneWorkspace("PCalpha",OutputWorkspace="PCrho") + CloneWorkspace("PCalpha",OutputWorkspace="PCAp") + CloneWorkspace("PCalpha",OutputWorkspace="PCPp") + rho=mtd['PCrho'] + Ap=mtd['PCAp'] + Pp=mtd['PCPp'] + + # Use the polynomial corretion fn instead + PolynomialCorrection(InputWorkspace="PCalpha",OutputWorkspace="PCalpha",Coefficients=calpha,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCrho",OutputWorkspace="PCrho",Coefficients=crho,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCAp",OutputWorkspace="PCAp",Coefficients=cAp,Operation="Multiply") + PolynomialCorrection(InputWorkspace="PCPp",OutputWorkspace="PCPp",Coefficients=cPp,Operation="Multiply") + + A0 = (Iaa * Pp * Ap) + (Ap * Ipa * rho * Pp) + (Ap * Iap * Pp * alpha) + (Ipp * Ap * alpha * rho * Pp) + A1 = Pp * Iaa + A2 = Pp * Iap + A3 = Ap * Iaa + A4 = Ap * Ipa + A5 = Ap * alpha * Ipp + A6 = Ap * alpha * Iap + A7 = Pp * rho * Ipp + A8 = Pp * rho * Ipa + D = Pp * Ap *( 1.0 + rho + alpha + (rho * alpha) ) + nIpp = (A0 - A1 + A2 - A3 + A4 + A5 - A6 + A7 - A8 + Ipp + Iaa - Ipa - Iap) / D + nIaa = (A0 + A1 - A2 + A3 - A4 - A5 + A6 - A7 + A8 + Ipp + Iaa - Ipa - Iap) / D + nIpa = (A0 - A1 + A2 + A3 - A4 - A5 + A6 + A7 - A8 - Ipp - Iaa + Ipa + Iap) / D + nIap = (A0 + A1 - A2 - A3 + A4 + A5 - A6 - A7 + A8 - Ipp - Iaa + Ipa + Iap) / D + RenameWorkspace(nIpp,OutputWorkspace=str(Ipp)+"corr") + RenameWorkspace(nIpa,OutputWorkspace=str(Ipa)+"corr") + RenameWorkspace(nIap,OutputWorkspace=str(Iap)+"corr") + RenameWorkspace(nIaa,OutputWorkspace=str(Iaa)+"corr") + ReplaceSpecialValues(str(Ipp)+"corr",OutputWorkspace=str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") + ReplaceSpecialValues(str(Ipp)+"corr",OutputWorkspace=str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") + ReplaceSpecialValues(str(Ipp)+"corr",OutputWorkspace=str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") + ReplaceSpecialValues(str(Ipp)+"corr",OutputWorkspace=str(Ipp)+"corr",NaNValue="0.0",NaNError="0.0",InfinityValue="0.0",InfinityError="0.0") + iwksp=mtd.getObjectNames() + list=[str(Ipp),str(Ipa),str(Iap),str(Iaa),"PCalpha","PCrho","PCAp","PCPp","1_p"] + for i in range(len(iwksp)): + for j in list: + lname=len(j) + if iwksp[i] [0:lname+1] == j+"_": + DeleteWorkspace(iwksp[i]) + DeleteWorkspace("PCalpha") + DeleteWorkspace("PCrho") + DeleteWorkspace("PCAp") + DeleteWorkspace("PCPp") + DeleteWorkspace('A0') + DeleteWorkspace('A1') + DeleteWorkspace('A2') + DeleteWorkspace('A3') + DeleteWorkspace('A4') + DeleteWorkspace('A5') + DeleteWorkspace('A6') + DeleteWorkspace('A7') + DeleteWorkspace('A8') + DeleteWorkspace('D') + + def groupGet(wksp,whattoget,field=''): ''' diff --git a/Code/Mantid/scripts/Reflectometry/isis_reflgui/reflgui.py b/Code/Mantid/scripts/Reflectometry/isis_reflgui/reflgui.py index 1c7c0209d2ba..d99785c04240 100644 --- a/Code/Mantid/scripts/Reflectometry/isis_reflgui/reflgui.py +++ b/Code/Mantid/scripts/Reflectometry/isis_reflgui/reflgui.py @@ -18,7 +18,7 @@ from mantid.simpleapi import * # New API # import qti as qti -from isis_reflectometry.quick import * +from isis_reflectometry.quick import quick from isis_reflectometry.combineMulti import * from mantid.api import WorkspaceGroup from settings import * diff --git a/Code/Mantid/scripts/SANS/SANSBatchMode.py b/Code/Mantid/scripts/SANS/SANSBatchMode.py index cd0962887faa..2586dd28a0ac 100644 --- a/Code/Mantid/scripts/SANS/SANSBatchMode.py +++ b/Code/Mantid/scripts/SANS/SANSBatchMode.py @@ -30,6 +30,7 @@ #Make the reduction module available from ISISCommandInterface import * from mantid.simpleapi import * +from mantid.api import WorkspaceGroup from mantid.kernel import Logger sanslog = Logger.get("SANS") import copy @@ -217,8 +218,15 @@ def BatchReduce(filename, format, plotresults=False, saveAlgs={'SaveRKH':'txt'}, file = run['output_as'] #saving if optional and doesn't happen if the result workspace is left blank. Is this feature used? if file: + save_names = [] + for n in names: + w = mtd[n] + if isinstance(w,WorkspaceGroup): + save_names.extend(w.getNames()) + else: + save_names.append(n) for algor in saveAlgs.keys(): - for workspace_name in names: + for workspace_name in save_names: #add the file extension, important when saving different types of file so they don't over-write each other ext = saveAlgs[algor] if not ext.startswith('.'): diff --git a/Code/Mantid/scripts/SCD_Reduction/ReduceSCD.config b/Code/Mantid/scripts/SCD_Reduction/ReduceSCD.config index 55653e97b564..7f3d6a0b3437 100644 --- a/Code/Mantid/scripts/SCD_Reduction/ReduceSCD.config +++ b/Code/Mantid/scripts/SCD_Reduction/ReduceSCD.config @@ -44,6 +44,13 @@ calibration_file_2 None data_directory None output_directory /SNS/TOPAZ/IPTS-9890/shared/SPAnH +# +# If use_monitor_counts is True, then the integrated beam monitor +# counts will be used for scaling. If use_monitor_counts is False, +# then the integrated proton charge will be used for scaling. These +# values will be listed under MONCNT in the integrate file. +use_monitor_counts False + # # Min & max tof determine the range of events loaded. # Max Q determines the range of Q values that will be mapped to diff --git a/Code/Mantid/scripts/SCD_Reduction/ReduceSCD_OneRun.py b/Code/Mantid/scripts/SCD_Reduction/ReduceSCD_OneRun.py index 67b4d9917b77..fb1aae072322 100644 --- a/Code/Mantid/scripts/SCD_Reduction/ReduceSCD_OneRun.py +++ b/Code/Mantid/scripts/SCD_Reduction/ReduceSCD_OneRun.py @@ -23,14 +23,16 @@ # This version now includes the posibility to use the 1D cylindrical integration method # and the posibility to load a UB matrix which will be used for integration of the individual # runs and to index the combined file (Code from Xiapoing). -# - # # _v2: December 3rd 2013. Mads Joergensen # Adds the posibility to optimize the loaded UB for each run for a better peak prediction # It is also possible to find the common UB by using lattice parameters of the first # run or the loaded matirix instead of the default FFT method # +# _v3: December 5 2013. A. J. Schultz +# This version includes the Boolean parameter use_monitor_counts to allow +# the use of either monitor counts (True) or proton charge (False) for +# scaling. import os import sys @@ -70,7 +72,8 @@ data_directory = params_dictionary[ "data_directory" ] output_directory = params_dictionary[ "output_directory" ] min_tof = params_dictionary[ "min_tof" ] -max_tof = params_dictionary[ "max_tof" ] +max_tof = params_dictionary[ "max_tof" ] +use_monitor_counts = params_dictionary[ "use_monitor_counts" ] min_monitor_tof = params_dictionary[ "min_monitor_tof" ] max_monitor_tof = params_dictionary[ "max_monitor_tof" ] monitor_index = params_dictionary[ "monitor_index" ] @@ -161,13 +164,15 @@ Filename=calibration_file_1, Filename2=calibration_file_2 ) monitor_ws = LoadNexusMonitors( Filename=full_name ) +proton_charge = monitor_ws.getRun().getProtonCharge() * 1000.0 # get proton charge +print "\n", run, " has integrated proton charge x 1000 of", proton_charge, "\n" integrated_monitor_ws = Integration( InputWorkspace=monitor_ws, RangeLower=min_monitor_tof, RangeUpper=max_monitor_tof, StartWorkspaceIndex=monitor_index, EndWorkspaceIndex=monitor_index ) monitor_count = integrated_monitor_ws.dataY(0)[0] -print "\n", run, " has calculated monitor count", monitor_count, "\n" +print "\n", run, " has integrated monitor count", monitor_count, "\n" minVals= "-"+max_Q +",-"+max_Q +",-"+max_Q maxVals = max_Q +","+max_Q +","+ max_Q @@ -234,7 +239,14 @@ num_peaks = peaks_ws.getNumberPeaks() for i in range(num_peaks): peak = peaks_ws.getPeak(i) - peak.setMonitorCount( monitor_count ) + if use_monitor_counts: + peak.setMonitorCount( monitor_count ) + else: + peak.setMonitorCount( proton_charge ) +if use_monitor_counts: + print '\n*** Beam monitor counts used for scaling.' +else: + print '\n*** Proton charge x 1000 used for scaling.\n' if use_sphere_integration: # @@ -307,8 +319,6 @@ ProfilesFile = profiles_filename, PeaksWorkspace=peaks_ws, ) - if (not cell_type is None) and (not centering is None): - print "WARNING: Cylindrical profiles are NOT transformed!!!" # # Save the final integrated peaks, using the Niggli reduced cell. @@ -326,7 +336,7 @@ # If requested, also switch to the specified conventional cell and save the # corresponding matrix and integrate file # -if not use_ellipse_integration: +else: if (not cell_type is None) and (not centering is None) : run_conventional_matrix_file = output_directory + "/" + run + "_" + \ cell_type + "_" + centering + ".mat" diff --git a/Code/Mantid/scripts/test/ReflectometryQuickAuxiliaryTest.py b/Code/Mantid/scripts/test/ReflectometryQuickAuxiliaryTest.py index 7eb579787cea..1237b39cf3c8 100644 --- a/Code/Mantid/scripts/test/ReflectometryQuickAuxiliaryTest.py +++ b/Code/Mantid/scripts/test/ReflectometryQuickAuxiliaryTest.py @@ -88,6 +88,45 @@ def test_groupGet_unknown_log_error_code(self): # Test with group workspace as input self.assertEquals(errorCode, quick.groupGet(mtd[self.__wsName][0].name(), 'samp','MADE-UP-LOG-NAME')) - + def test_exponential_correction_strategy(self): + test_ws = CreateWorkspace(UnitX="TOF", DataX=[0,1,2,3], DataY=[1,1,1], NSpec=1) + + correction = quick.ExponentialCorrectionStrategy(1, 0) # Should have no effect. + self.assertTrue(isinstance(correction, quick.CorrectionStrategy), msg="Should be of type Correction") + + corrected = correction.apply(test_ws) + + self.assertTrue( all( test_ws.readY(0) == corrected.readY(0) ), msg="Input and outputs should be identical" ) + + DeleteWorkspace(test_ws) + DeleteWorkspace(corrected) + + def test_polynomial_correction_strategy(self): + test_ws = CreateWorkspace(UnitX="TOF", DataX=[0,1,2,3], DataY=[1,1,1], NSpec=1) + + correction = quick.PolynomialCorrectionStrategy("1, 0") # Should have no effect. + self.assertTrue(isinstance(correction, quick.CorrectionStrategy), msg="Should be of type Correction") + + corrected = correction.apply(test_ws) + + self.assertTrue( all( test_ws.readY(0) == corrected.readY(0) ), msg="Input and outputs should be identical" ) + + DeleteWorkspace(test_ws) + DeleteWorkspace(corrected) + + def test_null_correction_strategy(self): + test_ws = CreateWorkspace(UnitX="TOF", DataX=[0,1,2,3], DataY=[1,1,1], NSpec=1) + + correction = quick.NullCorrectionStrategy() # Should have no effect. + self.assertTrue(isinstance(correction, quick.CorrectionStrategy), msg="Should be of type Correction") + + corrected = correction.apply(test_ws) + + self.assertTrue( all( test_ws.readY(0) == corrected.readY(0) ), msg="Input and outputs should be identical" ) + + DeleteWorkspace(test_ws) + DeleteWorkspace(corrected) + + if __name__ == '__main__': unittest.main() diff --git a/Test/AutoTestData/ADARAMonitors.nxs b/Test/AutoTestData/ADARAMonitors.nxs new file mode 100644 index 000000000000..24c315f3e05e Binary files /dev/null and b/Test/AutoTestData/ADARAMonitors.nxs differ diff --git a/Test/AutoTestData/Test_characterizations_char.txt b/Test/AutoTestData/Test_characterizations_char.txt new file mode 100644 index 000000000000..430793a6f56d --- /dev/null +++ b/Test/AutoTestData/Test_characterizations_char.txt @@ -0,0 +1,9 @@ +#S 1 characterization runs +#L frequency(Hz) center_wavelength(angstrom) bank_num vanadium_run empty_run vanadium_back d_min(angstrom) d_max(angstrom) +60 0.900 1 15030 15039 0 0.20 4.12 4700.00 21200.00 +60 1.066 2 15050 15084 0 0.30 4.60 8333.33 25000.00 +60 1.333 3 15031 15040 0 0.43 5.40 12500.00 29166.67 +60 2.665 4 15032 15041 0 1.15 9.20 33333.33 50000.00 +60 4.797 5 15138 15085 0 2.00 15.35 66666.67 83333.67 +10 3.198 1 15033 15042 0 0.05 15.40 00000.00 100000.00 + diff --git a/Test/AutoTestData/Test_characterizations_focus.txt b/Test/AutoTestData/Test_characterizations_focus.txt new file mode 100644 index 000000000000..7d59f1b6fc94 --- /dev/null +++ b/Test/AutoTestData/Test_characterizations_focus.txt @@ -0,0 +1,8 @@ +Instrument parameter file: NOMAD_11_22_11.prm +1 2 15 +2 2 31 +3 2 67 +4 2 122 +5 2 154 +6 2 7 +L1 19.5 diff --git a/Test/AutoTestData/Test_characterizations_focus_and_char.txt b/Test/AutoTestData/Test_characterizations_focus_and_char.txt new file mode 100644 index 000000000000..6b0692de5b0a --- /dev/null +++ b/Test/AutoTestData/Test_characterizations_focus_and_char.txt @@ -0,0 +1,12 @@ +Instrument parameter file: dummy.iparm +1 3.18 90.0000 +L1 60.0 +#S 1 characterization runs +#L frequency(Hz) center_wavelength(angstrom) bank_num vanadium_run empty_run vanadium_back d_min(angstrom) d_max(angstrom) +60 0.900 1 15030 15039 0 0.20 4.12 4700.00 21200.00 +60 1.066 2 15050 15084 0 0.30 4.60 8333.33 25000.00 +60 1.333 3 15031 15040 0 0.43 5.40 12500.00 29166.67 +60 2.665 4 15032 15041 0 1.15 9.20 33333.33 50000.00 +60 4.797 5 15138 15085 0 2.00 15.35 66666.67 83333.67 +10 3.198 1 15033 15042 0 0.05 15.40 00000.00 100000.00 + diff --git a/Test/AutoTestData/Test_characterizations_focus_and_char2.txt b/Test/AutoTestData/Test_characterizations_focus_and_char2.txt new file mode 100644 index 000000000000..81087a1e4627 --- /dev/null +++ b/Test/AutoTestData/Test_characterizations_focus_and_char2.txt @@ -0,0 +1,11 @@ +Instrument parameter file: NOMAD_11_22_11.prm +1 2 15 +2 2 31 +3 2 67 +4 2 122 +5 2 154 +6 2 7 +L1 19.5 +#S 1 characterization runs +#L frequency(Hz) center_wavelength(angstrom) bank_num vanadium_run empty_run vanadium_back d_min(angstrom) d_max(angstrom) +60 1.4 1 0 0 0 .31,.25,.13,.13,.13,.42 13.66,5.83,3.93,2.09,1.57,31.42 300.00 16666.67