diff --git a/Code/Mantid/Framework/Algorithms/src/AsymmetryCalc.cpp b/Code/Mantid/Framework/Algorithms/src/AsymmetryCalc.cpp index 5d4e392d793cd19fb0e2ca8f6a6ddb6dcd90ca43..dccdb036bc8811ded03c46e84d79dcc13949fabd 100644 --- a/Code/Mantid/Framework/Algorithms/src/AsymmetryCalc.cpp +++ b/Code/Mantid/Framework/Algorithms/src/AsymmetryCalc.cpp @@ -110,6 +110,11 @@ void AsymmetryCalc::exec() specIDs[1] = backward; std::vector<size_t> indices; tmpWS->getIndicesFromSpectra( specIDs, indices ); + + // If some spectra were not found, can't continue + if(specIDs.size() != indices.size()) + throw std::invalid_argument("Some of the spectra specified do not exist in a workspace"); + forward = static_cast<int>( indices[0] ); backward = static_cast<int>( indices[1] ); } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt index 8d92af6411327588e3d126aa8199ad13e9fc9a39..cbc2272ecfd278f05e05456c477c96391a2e53e9 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt +++ b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt @@ -148,6 +148,7 @@ set( TEST_FILES CreateMDWorkspaceAlgDialogTest.h WorkspaceMementoTest.h WorkspaceInADSTest.h RawFileMementoTest.h + IO_MuonGroupingTest.h ) include_directories ( inc ) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/IO_MuonGrouping.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/IO_MuonGrouping.h index c4310b465560c39d8901359e768e37d3d5211824..2ab1b8280fbec7f801efe436bc570e4d865522ea 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/IO_MuonGrouping.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/IO_MuonGrouping.h @@ -5,8 +5,9 @@ // Includes //---------------------- #include "ui_MuonAnalysis.h" -#include "MantidQtCustomInterfaces/MuonAnalysis.h" #include "MantidQtAPI/UserSubWindow.h" +#include "MantidQtCustomInterfaces/MuonAnalysis.h" +#include "MantidQtCustomInterfaces/DllConfig.h" #include "MantidQtMantidWidgets/pythonCalc.h" #include "MantidQtMantidWidgets/MWRunFiles.h" @@ -51,20 +52,53 @@ File change history is stored at: <https://github.com/mantidproject/mantid> Code Documentation is available at: <http://doxygen.mantidproject.org> */ -/// save XML grouping file -void saveGroupingTabletoXML(Ui::MuonAnalysis& m_uiForm, const std::string& filename); +using namespace Mantid; +using namespace Mantid::API; + +/// Structure to represent grouping information for Muon Analysis +typedef struct { + std::vector<std::string> groupNames; + std::vector<std::string> groups; // Range strings, e.g. "1-32" + + std::vector<std::string> pairNames; + std::vector<std::pair<size_t, size_t> > pairs; // Pairs of group ids + std::vector<double> pairAlphas; + + std::string description; + std::string defaultName; // Not storing id because can be either group or pair +} Grouping; + +/// Saves grouping to the XML file specified +void MANTIDQT_CUSTOMINTERFACES_DLL saveGroupingToXML(const Grouping& grouping, + const std::string& filename); + +/// Loads grouping from the XML file specified +void MANTIDQT_CUSTOMINTERFACES_DLL loadGroupingFromXML(const std::string& filename, + Grouping& grouping); + +/// Parses information from the grouping table and saves to Grouping struct +void MANTIDQT_CUSTOMINTERFACES_DLL parseGroupingTable(const Ui::MuonAnalysis& form, + Grouping& grouping); + +/// Fills in the grouping table using information from provided Grouping struct +void MANTIDQT_CUSTOMINTERFACES_DLL fillGroupingTable(const Grouping& grouping, + Ui::MuonAnalysis& form); -/// load XML grouping file -void loadGroupingXMLtoTable(Ui::MuonAnalysis& m_uiForm, const std::string& filename); +/// Groups the workspace according to grouping provided +MatrixWorkspace_sptr MANTIDQT_CUSTOMINTERFACES_DLL groupWorkspace(MatrixWorkspace_const_sptr ws, + const Grouping& g); /// create 'map' relating group number to row number in group table -void whichGroupToWhichRow(Ui::MuonAnalysis& m_uiForm, std::vector<int>& groupToRow); +void MANTIDQT_CUSTOMINTERFACES_DLL whichGroupToWhichRow(const Ui::MuonAnalysis& m_uiForm, + std::vector<int>& groupToRow); /// create 'map' relating pair number to row number in pair table -void whichPairToWhichRow(Ui::MuonAnalysis& m_uiForm, std::vector<int>& pairToRow); +void MANTIDQT_CUSTOMINTERFACES_DLL whichPairToWhichRow(const Ui::MuonAnalysis& m_uiForm, + std::vector<int>& pairToRow); /// Set Group / Group Pair name -void setGroupGroupPair(Ui::MuonAnalysis& m_uiForm, const std::string& name); +void MANTIDQT_CUSTOMINTERFACES_DLL setGroupGroupPair(Ui::MuonAnalysis& m_uiForm, + const std::string& name); } } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/MuonAnalysis.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/MuonAnalysis.h index 667e9d376b5deddebe612a7f1bbca61ef3cb2e4a..44c70a4c0e52e4739dc677a4bcefefb206195576 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/MuonAnalysis.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/MuonAnalysis.h @@ -228,10 +228,6 @@ private: /// is grouping set bool isGroupingSet(); - /// Apply grouping specified in xml file to workspace - bool applyGroupingToWS( const std::string& inputWS, const std::string& outputWS, - const std::string& filename); - /// create WS contained the data for a plot void createPlotWS(const std::string& groupName, const std::string& inputWS, const std::string& outWS); @@ -344,9 +340,6 @@ private: /// List of current group names std::vector<std::string> m_groupNames; - /// name for file to temperary store grouping - std::string m_groupingTempFilename; - /// Deal with input file changes. void handleInputFileChanges(); @@ -412,6 +405,9 @@ private: /// Boolean to show whether the gui is being updated bool m_updating; + /// Flag to indicate that grouping table is being updated + bool m_updatingGrouping; + /// Boolean to show when data has been loaded. (Can't auto-update data that hasn't been loaded) bool m_loaded; diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/IO_MuonGrouping.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/IO_MuonGrouping.cpp index 27ba9bc214b8d15bb7cf4e20a5c7cbe4817266d8..a0114e100984530ef36e1cc6e5be957378ede140 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/IO_MuonGrouping.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/IO_MuonGrouping.cpp @@ -28,6 +28,7 @@ #include <Poco/Path.h> #include <boost/shared_ptr.hpp> +#include <boost/bind.hpp> #include <fstream> //----------------------------------------------------------------------------- using namespace Poco::XML; @@ -39,91 +40,79 @@ namespace CustomInterfaces namespace Muon { - using namespace Poco::XML; - using namespace MantidQt::API; +using namespace Poco::XML; +using namespace MantidQt::API; /** - * save XML grouping file + * Save grouping to the XML file specified. + * + * @param g :: Struct with grouping information + * @param filename :: XML filename where information will be saved */ -void saveGroupingTabletoXML(Ui::MuonAnalysis& m_uiForm, const std::string& filename) +void saveGroupingToXML(const Grouping& g, const std::string& filename) { std::ofstream outFile(filename.c_str()); if (!outFile) - { - throw Mantid::Kernel::Exception::FileError("Unable to open file:", filename); - } + throw Mantid::Kernel::Exception::FileError("Unable to open output file", filename); DOMWriter writer; writer.setNewLine("\n"); writer.setOptions(XMLWriter::PRETTY_PRINT); Poco::XML::Document* mDoc = new Document(); + + // Create root element with a description Element* rootElem = mDoc->createElement("detector-grouping"); - rootElem->setAttribute("description", m_uiForm.groupDescription->text().toStdString()); + rootElem->setAttribute("description", g.description); mDoc->appendChild(rootElem); - // loop over groups in table - - std::vector<int> groupToRow; - whichGroupToWhichRow(m_uiForm, groupToRow); - - int num = static_cast<int>(groupToRow.size()); - for (int i = 0; i < num; i++) + // Create group elements + for (size_t gi = 0; gi < g.groups.size(); gi++) { Element* gElem = mDoc->createElement("group"); - gElem->setAttribute("name", m_uiForm.groupTable->item(groupToRow[i],0)->text().toStdString()); + gElem->setAttribute("name", g.groupNames[gi]); rootElem->appendChild(gElem); + Element* idsElem = mDoc->createElement("ids"); - idsElem->setAttribute("val", m_uiForm.groupTable->item(groupToRow[i],1)->text().toStdString()); + idsElem->setAttribute("val", g.groups[gi]); gElem->appendChild(idsElem); } - // loop over pairs in pair table - - num = m_uiForm.pairTable->rowCount(); - for (int i = 0; i < num; i++) + // Create pair elements + for (size_t pi = 0; pi < g.pairs.size(); pi++) { - QTableWidgetItem *itemName = m_uiForm.pairTable->item(i,0); - if (!itemName) - break; - if ( itemName->text().isEmpty() ) - break; - QTableWidgetItem *itemAlpha = m_uiForm.pairTable->item(i,3); - if (!itemAlpha) - break; - if ( itemAlpha->text().isEmpty() ) - break; - - QComboBox* qw1 = static_cast<QComboBox*>(m_uiForm.pairTable->cellWidget(i,1)); - QComboBox* qw2 = static_cast<QComboBox*>(m_uiForm.pairTable->cellWidget(i,2)); - Element* gElem = mDoc->createElement("pair"); - gElem->setAttribute("name", itemName->text().toStdString()); + gElem->setAttribute("name", g.pairNames[pi]); rootElem->appendChild(gElem); + Element* fwElem = mDoc->createElement("forward-group"); - fwElem->setAttribute("val", qw1->currentText().toStdString()); + fwElem->setAttribute("val", g.groupNames[g.pairs[pi].first]); gElem->appendChild(fwElem); + Element* bwElem = mDoc->createElement("backward-group"); - bwElem->setAttribute("val", qw2->currentText().toStdString()); + bwElem->setAttribute("val", g.groupNames[g.pairs[pi].second]); gElem->appendChild(bwElem); + Element* alphaElem = mDoc->createElement("alpha"); - alphaElem->setAttribute("val", itemAlpha->text().toStdString()); + alphaElem->setAttribute("val", boost::lexical_cast<std::string>(g.pairAlphas[pi])); gElem->appendChild(alphaElem); } - // save default - + // Create default group/pair name element Element* gElem = mDoc->createElement("default"); - gElem->setAttribute("name", m_uiForm.frontGroupGroupPairComboBox->currentText().toStdString()); + gElem->setAttribute("name", g.defaultName); rootElem->appendChild(gElem); writer.writeNode(outFile, mDoc); } /** - * load XML grouping file. It is assumed that tables and combo box cleared before this method is called + * Loads grouping from the XML file specified. + * + * @param filename :: XML filename to load grouping information from + * @param g :: Struct to store grouping information to */ -void loadGroupingXMLtoTable(Ui::MuonAnalysis& m_uiForm, const std::string& filename) +void loadGroupingFromXML(const std::string& filename, Grouping& g) { // Set up the DOM parser and parse xml file DOMParser pParser; @@ -134,174 +123,237 @@ void loadGroupingXMLtoTable(Ui::MuonAnalysis& m_uiForm, const std::string& filen } catch(...) { - throw Mantid::Kernel::Exception::FileError("Unable to parse File:" , filename); + throw Mantid::Kernel::Exception::FileError("Unable to parse File" , filename); } + // Get pointer to root element Element* pRootElem = pDoc->documentElement(); - if ( !pRootElem->hasChildNodes() ) - { - throw Mantid::Kernel::Exception::FileError("No root element in XML grouping file:" , filename); - } - - NodeList* pNL_group = pRootElem->getElementsByTagName("group"); - if ( pNL_group->length() == 0 ) - { - throw Mantid::Kernel::Exception::FileError("XML group file contains no group elements:" , filename); - } + if (!pRootElem->hasChildNodes()) + throw Mantid::Kernel::Exception::FileError("No root element in XML grouping file" , filename); + // Parse information for groups + NodeList* groups = pRootElem->getElementsByTagName("group"); + if (groups->length() == 0) + throw Mantid::Kernel::Exception::FileError("No groups specified in XML grouping file" , filename); - // add content to group table + // Resize vectors + g.groupNames.resize(groups->length()); + g.groups.resize(groups->length()); - QStringList allGroupNames; // used to populate combo boxes - int numberGroups = static_cast<int>(pNL_group->length()); - for (int iGroup = 0; iGroup < numberGroups; iGroup++) + for (size_t ig = 0; ig < groups->length(); ig++) { - Element* pGroupElem = static_cast<Element*>(pNL_group->item(iGroup)); + Element* pGroupElem = static_cast<Element*>(groups->item(ig)); - if ( !pGroupElem->hasAttribute("name") ) + if (!pGroupElem->hasAttribute("name")) throw Mantid::Kernel::Exception::FileError("Group element without name" , filename); - std::string gName = pGroupElem->getAttribute("name"); + g.groupNames[ig] = pGroupElem->getAttribute("name"); Element* idlistElement = pGroupElem->getChildElement("ids"); - if (idlistElement) - { - std::string ids = idlistElement->getAttribute("val"); + if (!idlistElement) + throw Mantid::Kernel::Exception::FileError("Group element without <ids>" , filename); - // add info to table - m_uiForm.groupTable->setItem(iGroup, 0, new QTableWidgetItem(gName.c_str()) ); - m_uiForm.groupTable->setItem(iGroup,1, new QTableWidgetItem(ids.c_str()) ); - allGroupNames.push_back( m_uiForm.groupTable->item(static_cast<int>(iGroup),0)->text() ); - } - else - { - throw Mantid::Kernel::Exception::FileError("XML group file contains no <ids> elements:" , filename); - } + g.groups[ig] = idlistElement->getAttribute("val"); } - pNL_group->release(); + + groups->release(); - // populate pair table combo boxes + // Parse information for pairs + NodeList* pairs = pRootElem->getElementsByTagName("pair"); + + // Resize vectors + g.pairNames.resize(pairs->length()); + g.pairs.resize(pairs->length()); + g.pairAlphas.resize(pairs->length()); - int rowNum = m_uiForm.pairTable->rowCount(); - for (int i = 0; i < rowNum; i++) + for (size_t ip = 0; ip < pairs->length(); ip++) { - QComboBox* qw1 = static_cast<QComboBox*>(m_uiForm.pairTable->cellWidget(i,1)); - QComboBox* qw2 = static_cast<QComboBox*>(m_uiForm.pairTable->cellWidget(i,2)); + Element* pPairElem = static_cast<Element*>(pairs->item(ip)); + + if ( !pPairElem->hasAttribute("name") ) + throw Mantid::Kernel::Exception::FileError("Pair element without name" , filename); - for (int ii = 0; ii < allGroupNames.size(); ii++) + g.pairNames[ip] = pPairElem->getAttribute("name"); + + size_t fwdGroupId, bwdGroupId; // Ids of forward/backward groups + + // Try to get id of the first group + if (Element* fwdElement = pPairElem->getChildElement("forward-group")) { + if(!fwdElement->hasAttribute("val")) + throw Mantid::Kernel::Exception::FileError("Pair forward-group without <val>" , filename); + + // Find the group with the given name + auto it = std::find(g.groupNames.begin(), g.groupNames.end(), fwdElement->getAttribute("val")); - qw1->addItem( allGroupNames[ii] ); - qw2->addItem( allGroupNames[ii] ); - } - - if ( qw2->count() > 1 ) - qw2->setCurrentIndex(1); - } + if(it == g.groupNames.end()) + throw Mantid::Kernel::Exception::FileError("Pair forward-group name not recognized" , filename); + // Get index of the iterator + fwdGroupId = it - g.groupNames.begin(); + } + else + { + throw Mantid::Kernel::Exception::FileError("Pair element without <forward-group>" , filename); + } + // Try to get id of the second group + if(Element* bwdElement = pPairElem->getChildElement("backward-group")) + { + if(!bwdElement->hasAttribute("val")) + throw Mantid::Kernel::Exception::FileError("Pair backward-group without <val>" , filename); + // Find the group with the given name + auto it = std::find(g.groupNames.begin(), g.groupNames.end(), bwdElement->getAttribute("val")); - // add content to pair table + if(it == g.groupNames.end()) + throw Mantid::Kernel::Exception::FileError("Pair backward-group name not recognized" , filename); - QStringList allPairNames; - NodeList* pNL_pair = pRootElem->getElementsByTagName("pair"); - int nPairs = static_cast<int>(pNL_pair->length()); - if ( pNL_pair->length() > 0 ) - { - for (int iPair = 0; iPair < nPairs; iPair++) + // Get index of the iterator + bwdGroupId = it - g.groupNames.begin(); + } + else { - Element* pGroupElem = static_cast<Element*>(pNL_pair->item(iPair)); + throw Mantid::Kernel::Exception::FileError("Pair element without <backward-group>" , filename); + } - if ( !pGroupElem->hasAttribute("name") ) - throw Mantid::Kernel::Exception::FileError("pair element without name" , filename); - std::string gName = pGroupElem->getAttribute("name"); - m_uiForm.pairTable->setItem(iPair,0, new QTableWidgetItem(gName.c_str()) ); - allPairNames.push_back(gName.c_str()); + g.pairs[ip] = std::make_pair(fwdGroupId, bwdGroupId); - Element* fwElement = pGroupElem->getChildElement("forward-group"); - if (fwElement) + // Try to get alpha element + if (Element* aElement = pPairElem->getChildElement("alpha")) + { + if (!aElement->hasAttribute("val") ) + throw Mantid::Kernel::Exception::FileError("Pair alpha element with no <val>" , filename); + + try // ... to convert value to double { - std::string ids = fwElement->getAttribute("val"); - QComboBox* qw1 = static_cast<QComboBox*>(m_uiForm.pairTable->cellWidget(iPair,1)); - int comboIndex = qw1->findText(ids.c_str()); - if ( comboIndex < 0 ) - throw Mantid::Kernel::Exception::FileError("XML pair group contains forward-group with unrecognised group name" , filename); - qw1->setCurrentIndex(comboIndex); + g.pairAlphas[ip] = boost::lexical_cast<double>(aElement->getAttribute("val")); } - else + catch(boost::bad_lexical_cast&) { - throw Mantid::Kernel::Exception::FileError("XML pair group contains no <forward-group> elements:" , filename); + throw Mantid::Kernel::Exception::FileError("Pair alpha value is not a number" , filename); } + } + // If alpha element not there, default it to 1.0 + else + { + g.pairAlphas[ip] = 1.0; + } - Element* bwElement = pGroupElem->getChildElement("backward-group"); - if (bwElement) - { - std::string ids = bwElement->getAttribute("val"); - QComboBox* qw2 = static_cast<QComboBox*>(m_uiForm.pairTable->cellWidget(iPair,2)); - int comboIndex = qw2->findText(ids.c_str()); - if ( comboIndex < 0 ) - throw Mantid::Kernel::Exception::FileError("XML pair group contains backward-group with unrecognised group name" , filename); - qw2->setCurrentIndex(comboIndex); - } - else - { - throw Mantid::Kernel::Exception::FileError("XML pair group contains no <backward-group> elements:" , filename); - } + } + + pairs->release(); - Element* element = pGroupElem->getChildElement("alpha"); - if (element) - { - if ( element->hasAttribute("val") ) - { - m_uiForm.pairTable->setItem(iPair,3, new QTableWidgetItem(element->getAttribute("val").c_str())); - } - else - throw Mantid::Kernel::Exception::FileError("XML pair group contains an <alpha> element with no 'val' attribute:" , filename); - } - // if alpha element not there for now just default it to 1.0 - else - { - m_uiForm.pairTable->setItem(iPair,3, new QTableWidgetItem(1.0)); - } + // Try to get description + if (pRootElem->hasAttribute("description")) + { + g.description = pRootElem->getAttribute("description"); + } - } + // Try to get default group/pair name + if(Element* defaultElement = pRootElem->getChildElement("default")) + { + if(!defaultElement->hasAttribute("name")) + throw Mantid::Kernel::Exception::FileError("Default element with no <name>" , filename); + + g.defaultName = defaultElement->getAttribute("name"); } - pNL_pair->release(); + pDoc->release(); +} - // populate front combobox +/** + * Parses information from the grouping table and saves to Grouping struct. + * + * @param form :: Muon Analysis UI containing table widgets + * @param g :: Grouping struct to store parsed info to + */ +void parseGroupingTable(const Ui::MuonAnalysis& form, Grouping& g) +{ + // Parse description + g.description = form.groupDescription->text().toStdString(); - //m_uiForm.frontGroupGroupPairComboBox->addItems(allGroupNames); - //m_uiForm.frontGroupGroupPairComboBox->addItems(allPairNames); + // Parse grouping info + std::vector<int> groupToRow; + whichGroupToWhichRow(form, groupToRow); + // Resize group arrays + g.groupNames.resize(groupToRow.size()); + g.groups.resize(groupToRow.size()); - if ( pRootElem->hasAttribute("description") ) + // Fill group arrays + for (size_t gi = 0; gi < groupToRow.size(); gi++) { - m_uiForm.groupDescription->setText(pRootElem->getAttribute("description").c_str()); + g.groupNames[gi] = form.groupTable->item(groupToRow[gi],0)->text().toStdString(); + g.groups[gi] = form.groupTable->item(groupToRow[gi],1)->text().toStdString(); } - else + + // Parse pair info + std::vector<int> pairToRow; + whichPairToWhichRow(form, pairToRow); + + // Resize pair arrays + g.pairNames.resize(pairToRow.size()); + g.pairs.resize(pairToRow.size()); + g.pairAlphas.resize(pairToRow.size()); + + // Fill pair arrays + for (size_t pi = 0; pi < pairToRow.size(); pi++) { - m_uiForm.groupDescription->setText(""); - } + g.pairNames[pi] = form.pairTable->item(pairToRow[pi],0)->text().toStdString(); + + QComboBox* fwd = static_cast<QComboBox*>(form.pairTable->cellWidget(pairToRow[pi],1)); + QComboBox* bwd = static_cast<QComboBox*>(form.pairTable->cellWidget(pairToRow[pi],2)); - // reads default choice + g.pairs[pi] = std::make_pair(fwd->currentIndex(), bwd->currentIndex()); - Element* element = pRootElem->getChildElement("default"); - if (element) + g.pairAlphas[pi] = form.pairTable->item(pairToRow[pi],3)->text().toDouble(); + } + + // Use currently selected group/pair as default value + g.defaultName = form.frontGroupGroupPairComboBox->currentText().toStdString(); +} + +/** + * Fills in the grouping table using information from provided Grouping struct. + * + * @param g :: Grouping struct to use for filling the table + * @param form :: Muon Analysis UI containing table widgets + */ +void fillGroupingTable(const Grouping& g, Ui::MuonAnalysis& form) +{ + // Add groups to a table + for(int gi = 0; gi < static_cast<int>(g.groups.size()); gi++) { - if ( element->hasAttribute("name") ) - { - setGroupGroupPair(m_uiForm, element->getAttribute("name")); - } + form.groupTable->setItem(gi, 0, new QTableWidgetItem(g.groupNames[gi].c_str())); + form.groupTable->setItem(gi, 1, new QTableWidgetItem(g.groups[gi].c_str())); + } + + // Add pairs to the table + for(int pi = 0; pi < static_cast<int>(g.pairs.size()); pi++) + { + // Set the name + form.pairTable->setItem(pi, 0, new QTableWidgetItem(g.pairNames[pi].c_str())); + + // Set selected forward/backward groups + QComboBox* fwd = static_cast<QComboBox*>(form.pairTable->cellWidget(pi,1)); + fwd->setCurrentIndex(static_cast<int>(g.pairs[pi].first)); + QComboBox* bwd = static_cast<QComboBox*>(form.pairTable->cellWidget(pi,2)); + bwd->setCurrentIndex(static_cast<int>(g.pairs[pi].second)); + + // Set alpha + form.pairTable->setItem(pi, 3, + new QTableWidgetItem(boost::lexical_cast<std::string>(g.pairAlphas[pi]).c_str())); } + // Set description + form.groupDescription->setText(g.description.c_str()); - pDoc->release(); + // Select default element + setGroupGroupPair(form, g.defaultName); } - /** * Set Group / Group Pair name * @@ -321,7 +373,49 @@ void setGroupGroupPair(Ui::MuonAnalysis& m_uiForm, const std::string& name) } } +/** + * Groups the workspace according to grouping provided. + * + * @param ws :: Workspace to group + * @param g :: The grouping information + * @return Sptr to created grouped workspace + */ +MatrixWorkspace_sptr groupWorkspace(MatrixWorkspace_const_sptr ws, const Grouping& g) +{ + // As I couldn't specify multiple groups for GroupDetectors, I am going down quite a complicated + // route - for every group distinct grouped workspace is created using GroupDetectors. These + // workspaces are then merged into the output workspace. + + // Create output workspace + MatrixWorkspace_sptr outWs = + WorkspaceFactory::Instance().create(ws, g.groups.size(), ws->readX(0).size(), ws->blocksize()); + + for(size_t gi = 0; gi < g.groups.size(); gi++) + { + Mantid::API::IAlgorithm_sptr alg = AlgorithmManager::Instance().create("GroupDetectors"); + alg->setChild(true); // So Output workspace is not added to the ADS + alg->initialize(); + alg->setProperty("InputWorkspace", boost::const_pointer_cast<MatrixWorkspace>(ws)); + alg->setPropertyValue("SpectraList", g.groups[gi]); + alg->setPropertyValue("OutputWorkspace", "grouped"); // Is not actually used, just to make validators happy + alg->execute(); + + MatrixWorkspace_sptr grouped = alg->getProperty("OutputWorkspace"); + + // Copy the spectrum + *(outWs->getSpectrum(gi)) = *(grouped->getSpectrum(0)); + + // Update spectrum number + outWs->getSpectrum(gi)->setSpectrumNo(static_cast<specid_t>(gi)); + + // Copy to the output workspace + outWs->dataY(gi) = grouped->readY(0); + outWs->dataX(gi) = grouped->readX(0); + outWs->dataE(gi) = grouped->readE(0); + } + return outWs; +} /** * create 'map' relating group number to row number in group table @@ -329,7 +423,7 @@ void setGroupGroupPair(Ui::MuonAnalysis& m_uiForm, const std::string& name) * @param m_uiForm :: The UI form * @param groupToRow :: The 'map' returned */ -void whichGroupToWhichRow(Ui::MuonAnalysis& m_uiForm, std::vector<int>& groupToRow) +void whichGroupToWhichRow(const Ui::MuonAnalysis& m_uiForm, std::vector<int>& groupToRow) { groupToRow.clear(); @@ -369,7 +463,7 @@ void whichGroupToWhichRow(Ui::MuonAnalysis& m_uiForm, std::vector<int>& groupToR * @param m_uiForm :: The UI form * @param pairToRow :: The 'map' returned */ -void whichPairToWhichRow(Ui::MuonAnalysis& m_uiForm, std::vector<int>& pairToRow) +void whichPairToWhichRow(const Ui::MuonAnalysis& m_uiForm, std::vector<int>& pairToRow) { pairToRow.clear(); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysis.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysis.cpp index bed038f634e70f29e8c75be0595528e1ff27a145..106ceb3c37c4551f3d145f1c452406fbedad3003 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysis.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/MuonAnalysis.cpp @@ -85,27 +85,7 @@ MuonAnalysis::MuonAnalysis(QWidget *parent) : m_groupTableRowInFocus(0), m_pairTableRowInFocus(0),m_tabNumber(0), m_groupNames(), m_settingsGroup("CustomInterfaces/MuonAnalysis/"), m_updating(false), m_loaded(false), m_deadTimesChanged(false), m_textToDisplay(""), m_nexusTimeZero(0.0) -{ - try - { - Poco::File tempFile(ConfigService::Instance().getInstrumentDirectory()); - - // If the instrument directory can't be written to... (linux problem) - if (tempFile.exists() && !tempFile.canWrite() ) - { - g_log.information() << "Instrument directory is read only, writing temp grouping to system temp.\n"; - m_groupingTempFilename = ConfigService::Instance().getTempDir()+"tempMuonAnalysisGrouping.xml"; - } - else - { - m_groupingTempFilename = ConfigService::Instance().getInstrumentDirectory()+"Grouping/tempMuonAnalysisGrouping.xml"; - } - } - catch(...) - { - g_log.debug() << "Problem writing temp grouping file"; - } -} +{} /** * Initialize local Python environmnet. @@ -405,7 +385,9 @@ void MuonAnalysis::runSaveGroupButton() if( ! groupingFile.isEmpty() ) { - saveGroupingTabletoXML(m_uiForm, groupingFile.toStdString()); + Grouping groupingToSave; + parseGroupingTable(m_uiForm, groupingToSave); + saveGroupingToXML(groupingToSave, groupingFile.toStdString()); QString directory = QFileInfo(groupingFile).path(); prevValues.setValue("dir", directory); @@ -438,20 +420,23 @@ void MuonAnalysis::runLoadGroupButton() QString directory = QFileInfo(groupingFile).path(); prevValues.setValue("dir", directory); - saveGroupingTabletoXML(m_uiForm, m_groupingTempFilename); - clearTablesAndCombo(); + Grouping loadedGrouping; try { - loadGroupingXMLtoTable(m_uiForm, groupingFile.toStdString()); + loadGroupingFromXML(groupingFile.toStdString(), loadedGrouping); } catch (Exception::FileError& e) { + g_log.error("Unable to load grouping. Data left unchanged"); g_log.error(e.what()); - g_log.error("Revert to previous grouping"); - loadGroupingXMLtoTable(m_uiForm, m_groupingTempFilename); + m_updating = false; + return; } + clearTablesAndCombo(); + fillGroupingTable(loadedGrouping, m_uiForm); + // add number of detectors column to group table int numRows = m_uiForm.groupTable->rowCount(); for (int i = 0; i < numRows; i++) @@ -1201,8 +1186,6 @@ void MuonAnalysis::inputFileChanged(const QStringList& files) setDummyGrouping(static_cast<int>(matrix_workspace->getInstrument()->getDetectorIDs().size())); if ( !applyGroupingToWS(m_workspace_name, m_workspace_name+"Grouped") ) - // TODO: applyGroupingToWS shows it's own messages as well. Should make it throw exceptions - // instead. throw std::runtime_error("Couldn't apply grouping"); // Populate instrument fields @@ -2612,63 +2595,86 @@ bool MuonAnalysis::isGroupingSet() } /** - * Apply grouping specified in xml file to workspace - * - * @param inputWS :: The input workspace to apply grouping - * @param outputWS :: The resulting workspace - * @param filename :: Name of grouping file + * Apply whatever grouping is specified in GUI tables to workspace. */ -bool MuonAnalysis::applyGroupingToWS( const std::string& inputWS, const std::string& outputWS, - const std::string& filename) +bool MuonAnalysis::applyGroupingToWS( const std::string& inputWsName, const std::string& outputWsName) { - if ( AnalysisDataService::Instance().doesExist(inputWS) ) - { - AnalysisDataService::Instance().remove(outputWS); + if (!isGroupingSet() || !AnalysisDataService::Instance().doesExist(inputWsName)) + return false; - Mantid::API::IAlgorithm_sptr alg = Mantid::API::AlgorithmManager::Instance().create("GroupDetectors"); - alg->setPropertyValue("InputWorkspace", inputWS); - alg->setPropertyValue("OutputWorkspace", outputWS); - alg->setPropertyValue("MapFile", filename); - try - { - alg->execute(); - return true; - } - catch(...) - { - m_optionTab->noDataAvailable(); - QMessageBox::warning(this, "MantidPlot - MuonAnalysis", "Can't group data file according to group-table. Plotting disabled."); - 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(); } - return false; -} -/** - * Apply whatever grouping is specified in GUI tables to workspace. - */ -bool MuonAnalysis::applyGroupingToWS( const std::string& inputWS, const std::string& outputWS) -{ - if ( isGroupingSet() && AnalysisDataService::Instance().doesExist(inputWS) ) + // 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(); + } - 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; - } + 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<MatrixWorkspace>(inputWs)) { - if (!m_uiForm.frontPlotButton->isEnabled() ) - m_optionTab->nowDataAvailable(); + outputWs = groupWorkspace(ws, tableGrouping); } + // Workspace group + else if(WorkspaceGroup_sptr group = boost::dynamic_pointer_cast<WorkspaceGroup>(inputWs)) + { + // Create output group + WorkspaceGroup_sptr outputGroup = boost::make_shared<WorkspaceGroup>(); + + for(size_t i = 0; i < group->size(); i++) + { + if(MatrixWorkspace_sptr member = boost::dynamic_pointer_cast<MatrixWorkspace>(group->getItem(i))) + { + MatrixWorkspace_sptr groupedMember = groupWorkspace(member, tableGrouping); + + outputGroup->addWorkspace(groupedMember); + } + else + throw std::invalid_argument("Group contains unsupported workspace type"); + } - saveGroupingTabletoXML(m_uiForm, m_groupingTempFilename); - return applyGroupingToWS(inputWS, outputWS, m_groupingTempFilename); + outputWs = outputGroup; + } + else + throw std::invalid_argument("Unsupported workspace type"); } - return false; + catch(std::exception& e) + { + m_optionTab->noDataAvailable(); + g_log.error(e.what()); + return false; + } + + AnalysisDataService::Instance().add(outputWsName, outputWs); + + return true; } /** @@ -2885,7 +2891,6 @@ void MuonAnalysis::startUpLook() */ void MuonAnalysis::setGroupingFromNexus(const QString& nexusFile) { - // for now do try to set grouping from nexus file if it is already set if ( isGroupingSet() ) return; @@ -3087,15 +3092,20 @@ void MuonAnalysis::setGroupingFromIDF(const std::string& mainFieldDirection, Mat if ( groupFile.size() == 1 ) { + Grouping loadedGrouping; + try { - loadGroupingXMLtoTable(m_uiForm, directoryName+groupFile[0]); + loadGroupingFromXML(directoryName+groupFile[0], loadedGrouping); } catch (...) { - QMessageBox::warning(this, "MantidPlot - MuonAnalysis", QString("Can't load default grouping file in IDF.\n") - + "with name: " + groupFile[0].c_str()); + QMessageBox::warning(this, "MantidPlot - MuonAnalysis", + QString("Can't load default grouping file in IDF. \n With name: ") + groupFile[0].c_str()); + return; } + + fillGroupingTable(loadedGrouping, m_uiForm); } } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/IO_MuonGroupingTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/IO_MuonGroupingTest.h new file mode 100644 index 0000000000000000000000000000000000000000..9ad7e7f22b46d0db6601b5971fddb1183ddb79d9 --- /dev/null +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/IO_MuonGroupingTest.h @@ -0,0 +1,179 @@ +#ifndef MANTID_CUSTOMINTERFACES_IO_MUONGROUPINGTEST_H_ +#define MANTID_CUSTOMINTERFACES_IO_MUONGROUPINGTEST_H_ + +#include <numeric> + +#include <cxxtest/TestSuite.h> +#include <Poco/Path.h> +#include <Poco/File.h> + +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/FrameworkManager.h" +#include "MantidQtCustomInterfaces/IO_MuonGrouping.h" + +using namespace Mantid::API; +using namespace MantidQt::CustomInterfaces::Muon; + +class IO_MuonGroupingTest : 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 IO_MuonGroupingTest *createSuite() { return new IO_MuonGroupingTest(); } + static void destroySuite( IO_MuonGroupingTest *suite ) { delete suite; } + + /// Constructor + IO_MuonGroupingTest() + { + using Mantid::Kernel::ConfigService; + + auto dataPaths = ConfigService::Instance().getDataSearchDirs(); + + // Find the path of AutoTestData + for(auto it = dataPaths.begin(); it != dataPaths.end(); ++it) + { + Poco::Path path(*it); + + if(path.directory(path.depth() - 1) == "AutoTestData") + { + m_testDataDir = *it; + break; + } + } + + TSM_ASSERT("Unable to find AutoTestData directory", !m_testDataDir.empty()); + + m_tmpDir = ConfigService::Instance().getTempDir(); + + // To make sure API is initialized properly + FrameworkManager::Instance(); + } + + void test_loadGroupingFromXML() + { + Grouping g; + + TS_ASSERT_THROWS_NOTHING(loadGroupingFromXML(m_testDataDir + "MUSRGrouping.xml", g)); + + TS_ASSERT_EQUALS(g.groupNames.size(), 2); + TS_ASSERT_EQUALS(g.groupNames[0], "fwd"); + TS_ASSERT_EQUALS(g.groupNames[1], "bwd"); + + TS_ASSERT_EQUALS(g.groups.size(), 2); + TS_ASSERT_EQUALS(g.groups[0], "33-64"); + TS_ASSERT_EQUALS(g.groups[1], "1-32"); + + TS_ASSERT_EQUALS(g.pairNames.size(), 1); + TS_ASSERT_EQUALS(g.pairNames[0], "long"); + + TS_ASSERT_EQUALS(g.pairs.size(), 1); + TS_ASSERT_EQUALS(g.pairs[0].first, 0); + TS_ASSERT_EQUALS(g.pairs[0].second, 1); + + TS_ASSERT_EQUALS(g.pairAlphas.size(), 1); + TS_ASSERT_EQUALS(g.pairAlphas[0], 1); + + TS_ASSERT_EQUALS(g.description, "musr longitudinal (64 detectors)"); + TS_ASSERT_EQUALS(g.defaultName, "long"); + } + + void test_saveGroupingToXML() + { + Grouping g, lg; + + std::string tmpFile = m_tmpDir + "tmp_MUSRGrouping.xml"; + + // Load grouping first + TS_ASSERT_THROWS_NOTHING(loadGroupingFromXML(m_testDataDir + "MUSRGrouping.xml", g)); + + // Then save it + TS_ASSERT_THROWS_NOTHING(saveGroupingToXML(g, tmpFile)); + + // And load it again + TS_ASSERT_THROWS_NOTHING(loadGroupingFromXML(tmpFile, lg)); + + // Check that all the information was saved + TS_ASSERT_EQUALS(lg.groupNames.size(), 2); + TS_ASSERT_EQUALS(lg.groupNames[0], "fwd"); + TS_ASSERT_EQUALS(lg.groupNames[1], "bwd"); + + TS_ASSERT_EQUALS(lg.groups.size(), 2); + TS_ASSERT_EQUALS(lg.groups[0], "33-64"); + TS_ASSERT_EQUALS(lg.groups[1], "1-32"); + + TS_ASSERT_EQUALS(lg.pairNames.size(), 1); + TS_ASSERT_EQUALS(lg.pairNames[0], "long"); + + TS_ASSERT_EQUALS(lg.pairs.size(), 1); + TS_ASSERT_EQUALS(lg.pairs[0].first, 0); + TS_ASSERT_EQUALS(lg.pairs[0].second, 1); + + TS_ASSERT_EQUALS(lg.pairAlphas.size(), 1); + TS_ASSERT_EQUALS(lg.pairAlphas[0], 1); + + TS_ASSERT_EQUALS(lg.description, "musr longitudinal (64 detectors)"); + TS_ASSERT_EQUALS(lg.defaultName, "long"); + + // Remove temporary file + Poco::File(tmpFile).remove(); + } + + void test_groupWorkspace() + { + // Load grouping for MUSR + Grouping g; + TS_ASSERT_THROWS_NOTHING(loadGroupingFromXML(m_testDataDir + "MUSRGrouping.xml", g)); + + // Load MUSR data file + IAlgorithm_sptr loadAlg = AlgorithmManager::Instance().create("LoadMuonNexus"); + loadAlg->setChild(true); // So outptu ws don't end up in the ADS + loadAlg->initialize(); + loadAlg->setPropertyValue("Filename", m_testDataDir + "MUSR00015189.nxs"); + loadAlg->setPropertyValue("OutputWorkspace", "data"); // Is not used, just for validator + loadAlg->execute(); + + Workspace_sptr loadedWs = loadAlg->getProperty("OutputWorkspace"); + WorkspaceGroup_sptr loadedGroup = boost::dynamic_pointer_cast<WorkspaceGroup>(loadedWs); + MatrixWorkspace_sptr ws = boost::dynamic_pointer_cast<MatrixWorkspace>(loadedGroup->getItem(0)); + + // Group the loaded workspace using loaded grouping + MatrixWorkspace_sptr gWs; // Grouped workspace + TS_ASSERT_THROWS_NOTHING(gWs = groupWorkspace(ws, g)); + TS_ASSERT(gWs); + + if(!gWs) + return; + + // Check that was grouped properly + TS_ASSERT_EQUALS(gWs->getNumberHistograms(), 2); + + TS_ASSERT_EQUALS(gWs->getSpectrum(0)->getDetectorIDs(), setFromRange(33, 64)); + TS_ASSERT_EQUALS(gWs->getSpectrum(1)->getDetectorIDs(), setFromRange(1, 32)); + + TS_ASSERT_EQUALS(std::accumulate(gWs->readY(0).begin(), gWs->readY(0).end(), 0.0), 355655); + TS_ASSERT_DELTA(std::accumulate(gWs->readX(0).begin(), gWs->readX(0).end(), 0.0), 30915.5, 0.1); + TS_ASSERT_DELTA(std::accumulate(gWs->readE(0).begin(), gWs->readE(0).end(), 0.0), 14046.9, 0.1); + + TS_ASSERT_EQUALS(std::accumulate(gWs->readY(1).begin(), gWs->readY(1).end(), 0.0), 262852); + TS_ASSERT_EQUALS(gWs->readX(1), gWs->readX(0)); + TS_ASSERT_DELTA(std::accumulate(gWs->readE(1).begin(), gWs->readE(1).end(), 0.0), 12079.8, 0.1); + } + +private: + std::string m_testDataDir; + std::string m_tmpDir; + + std::set<int> setFromRange(int from, int to) + { + std::set<int> result; + + for(int i = from; i <= to; i++) + result.insert(i); + + return result; + } + +}; + + +#endif /* MANTID_CUSTOMINTERFACES_IO_MUONGROUPINGTEST_H_ */ \ No newline at end of file