diff --git a/Code/Mantid/Framework/Algorithms/CMakeLists.txt b/Code/Mantid/Framework/Algorithms/CMakeLists.txt index 515acd347bdf4d6398b69679cd78b2188c7424b6..55370d9d0a3873182193815d201a1f2f1311815b 100644 --- a/Code/Mantid/Framework/Algorithms/CMakeLists.txt +++ b/Code/Mantid/Framework/Algorithms/CMakeLists.txt @@ -165,6 +165,7 @@ set ( SRC_FILES src/NormaliseToMonitor.cpp src/NormaliseToUnity.cpp src/OneMinusExponentialCor.cpp + src/PDDetermineCharacterizations.cpp src/PDFFourierTransform.cpp src/Pause.cpp src/PerformIndexOperations.cpp @@ -433,6 +434,7 @@ set ( INC_FILES inc/MantidAlgorithms/NormaliseToMonitor.h inc/MantidAlgorithms/NormaliseToUnity.h inc/MantidAlgorithms/OneMinusExponentialCor.h + inc/MantidAlgorithms/PDDetermineCharacterizations.h inc/MantidAlgorithms/PDFFourierTransform.h inc/MantidAlgorithms/Pause.h inc/MantidAlgorithms/PerformIndexOperations.h @@ -700,6 +702,7 @@ set ( TEST_FILES NormaliseByDetectorTest.h NormaliseToMonitorTest.h OneMinusExponentialCorTest.h + PDDetermineCharacterizationsTest.h PDFFourierTransformTest.h PauseTest.h PerformIndexOperationsTest.h diff --git a/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/PDDetermineCharacterizations.h b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/PDDetermineCharacterizations.h new file mode 100644 index 0000000000000000000000000000000000000000..76b6145a1267334102d6e03b8ce0af0aeb1544a5 --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/inc/MantidAlgorithms/PDDetermineCharacterizations.h @@ -0,0 +1,69 @@ +#ifndef MANTID_ALGORITHMS_PDDETERMINECHARACTERIZATIONS_H_ +#define MANTID_ALGORITHMS_PDDETERMINECHARACTERIZATIONS_H_ + +#include "MantidKernel/System.h" +#include "MantidAPI/Algorithm.h" +#include "MantidAPI/ITableWorkspace_fwd.h" + +namespace Mantid { + +namespace Kernel { +/// forward declaration +class PropertyMantager; +/// Typedef for a shared pointer to a PropertyManager +typedef boost::shared_ptr<PropertyManager> PropertyManager_sptr; +} + +namespace Algorithms { + +/** PDDetermineCharacterizations + + Copyright © 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport PDDetermineCharacterizations : public API::Algorithm { +public: + PDDetermineCharacterizations(); + virtual ~PDDetermineCharacterizations(); + + virtual const std::string name() const; + virtual int version() const; + virtual const std::string category() const; + virtual const std::string summary() const; + virtual std::map<std::string, std::string> validateInputs(); + +private: + double getLogValue(API::Run &run, const std::string &propName); + void getInformationFromTable(const double frequency, const double wavelength); + void setDefaultsInPropManager(); + void overrideRunNumProperty(const std::string &inputName, + const std::string &propName); + void init(); + void exec(); + + Kernel::PropertyManager_sptr m_propertyManager; + API::ITableWorkspace_sptr m_characterizations; +}; + +} // namespace Algorithms +} // namespace Mantid + +#endif /* MANTID_ALGORITHMS_PDDETERMINECHARACTERIZATIONS_H_ */ diff --git a/Code/Mantid/Framework/Algorithms/src/PDDetermineCharacterizations.cpp b/Code/Mantid/Framework/Algorithms/src/PDDetermineCharacterizations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..99e49da894b361e9e6de5577fd2b63e08b49fd58 --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/src/PDDetermineCharacterizations.cpp @@ -0,0 +1,359 @@ +#include "MantidAlgorithms/PDDetermineCharacterizations.h" +#include "MantidAPI/PropertyManagerDataService.h" +#include "MantidAPI/ITableWorkspace.h" +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/TimeSeriesProperty.h" + +namespace Mantid { +namespace Algorithms { + +using Mantid::API::ITableWorkspace_const_sptr; +using Mantid::API::PropertyManagerDataService; +using Mantid::API::WorkspaceProperty; +using Mantid::Kernel::ArrayProperty; +using Mantid::Kernel::Direction; +using Mantid::Kernel::PropertyWithValue; +using Mantid::Kernel::TimeSeriesProperty; + +namespace { // anonymous namespace +const std::string CHAR_PROP_NAME("Characterizations"); +const std::string FREQ_PROP_NAME("FrequencyLogNames"); +const std::string WL_PROP_NAME("WaveLengthLogNames"); +} + +// Register the algorithm into the AlgorithmFactory +DECLARE_ALGORITHM(PDDetermineCharacterizations) + +//---------------------------------------------------------------------------------------------- +/// Constructor +PDDetermineCharacterizations::PDDetermineCharacterizations() {} + +//---------------------------------------------------------------------------------------------- +/// Destructor +PDDetermineCharacterizations::~PDDetermineCharacterizations() {} + +//---------------------------------------------------------------------------------------------- + +/// Algorithms name for identification. @see Algorithm::name +const std::string PDDetermineCharacterizations::name() const { + return "PDDetermineCharacterizations"; +} + +/// Algorithm's version for identification. @see Algorithm::version +int PDDetermineCharacterizations::version() const { return 1; } + +/// Algorithm's category for identification. @see Algorithm::category +const std::string PDDetermineCharacterizations::category() const { + return "Workflow\\Diffraction\\UsesPropertyManager"; +} + +/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary +const std::string PDDetermineCharacterizations::summary() const { + return "Determines the characterizations of a workspace."; +} + +/** + * These should match those in LoadPDCharacterizations + * @return The list of expected column names + */ +std::vector<std::string> getColumnNames() { + std::vector<std::string> names; + names.push_back("frequency"); // double + names.push_back("wavelength"); // double + names.push_back("bank"); // integer + names.push_back("container"); // string + names.push_back("vanadium"); // string + names.push_back("empty"); // string + names.push_back("d_min"); // string + names.push_back("d_max"); // string + names.push_back("tof_min"); // double + names.push_back("tof_max"); // double + return names; +} + +/// More intesive input checking. @see Algorithm::validateInputs +std::map<std::string, std::string> +PDDetermineCharacterizations::validateInputs() { + std::map<std::string, std::string> result; + + ITableWorkspace_const_sptr characterizations = getProperty(CHAR_PROP_NAME); + + if (!bool(characterizations)) + return result; + + std::vector<std::string> expectedNames = getColumnNames(); + std::vector<std::string> names = characterizations->getColumnNames(); + if (names.size() < expectedNames.size()) { // allow for extra columns + std::stringstream msg; + msg << "Encountered invalid number of columns in " + << "TableWorkspace. Found " << names.size() << " expected " + << expectedNames.size(); + result[CHAR_PROP_NAME] = msg.str(); + } else { + for (auto it = expectedNames.begin(); it != expectedNames.end(); ++it) { + if (std::find(names.begin(), names.end(), *it) == names.end()) { + std::stringstream msg; + msg << "Failed to find column named " << (*it); + result[CHAR_PROP_NAME] = msg.str(); + } + } + } + return result; +} + +/// Initialize the algorithm's properties. +void PDDetermineCharacterizations::init() { + declareProperty( + new WorkspaceProperty<>("InputWorkspace", "", Direction::Input, + API::PropertyMode::Optional), + "Workspace with logs to help identify frequency and wavelength"); + + declareProperty( + new WorkspaceProperty<API::ITableWorkspace>( + CHAR_PROP_NAME, "", Direction::Input, API::PropertyMode::Optional), + "Table of characterization information"); + + declareProperty("ReductionProperties", "__pd_reduction_properties", + "Property manager name for the reduction"); + + const std::string defaultMsg = + " run to use. 0 to use value in table, -1 to not use."; + + declareProperty("BackRun", 0, "Empty container" + defaultMsg); + declareProperty("NormRun", 0, "Normalization" + defaultMsg); + declareProperty("NormBackRun", 0, "Normalization background" + defaultMsg); + + std::vector<std::string> defaultFrequencyNames; + defaultFrequencyNames.push_back("SpeedRequest1"); + defaultFrequencyNames.push_back("Speed1"); + defaultFrequencyNames.push_back("frequency"); + declareProperty(new Kernel::ArrayProperty<std::string>( + FREQ_PROP_NAME, defaultFrequencyNames), + "Candidate log names for frequency"); + + std::vector<std::string> defaultWavelengthNames; + defaultWavelengthNames.push_back("LambdaRequest"); + defaultWavelengthNames.push_back("lambda"); + declareProperty(new Kernel::ArrayProperty<std::string>( + WL_PROP_NAME, defaultWavelengthNames), + "Candidate log names for wave length"); +} + +/** + * Compare two numbers to be in agreement within 5% + * @param left + * @param right + * @return + */ +bool closeEnough(const double left, const double right) { + // the same value + const double diff = fabs(left - right); + if (diff == 0.) + return true; + + // same within 5% + const double relativeDiff = diff * 2 / (left + right); + if (relativeDiff < .05) + return true; + + return false; +} + +/// Fill in the property manager from the correct line in the table +void PDDetermineCharacterizations::getInformationFromTable( + const double frequency, const double wavelength) { + size_t numRows = m_characterizations->rowCount(); + + for (size_t i = 0; i < numRows; ++i) { + const double rowFrequency = + m_characterizations->getRef<double>("frequency", i); + const double rowWavelength = + m_characterizations->getRef<double>("wavelength", i); + + if (closeEnough(frequency, rowFrequency) && + closeEnough(wavelength, rowWavelength)) { + g_log.information() << "Using information from row " << i + << " with frequency = " << rowFrequency + << " and wavelength = " << rowWavelength << "\n"; + + m_propertyManager->setProperty("frequency", frequency); + m_propertyManager->setProperty("wavelength", wavelength); + + m_propertyManager->setProperty( + "bank", m_characterizations->getRef<int>("bank", i)); + + m_propertyManager->setProperty( + "vanadium", m_characterizations->getRef<int>("vanadium", i)); + m_propertyManager->setProperty( + "container", m_characterizations->getRef<int>("container", i)); + m_propertyManager->setProperty( + "empty", m_characterizations->getRef<int>("empty", i)); + + m_propertyManager->setPropertyValue( + "d_min", m_characterizations->getRef<std::string>("d_min", i)); + m_propertyManager->setPropertyValue( + "d_max", m_characterizations->getRef<std::string>("d_max", i)); + + m_propertyManager->setProperty( + "tof_min", m_characterizations->getRef<double>("tof_min", i)); + m_propertyManager->setProperty( + "tof_max", m_characterizations->getRef<double>("tof_max", i)); + return; + } + } + g_log.warning("Failed to find compatible row in characterizations table"); +} + +/** + * Get a value from one of a set of logs. + * @param run + * @param propName + * @return + */ +double PDDetermineCharacterizations::getLogValue(API::Run &run, + const std::string &propName) { + std::vector<std::string> names = getProperty(propName); + + std::string label = "frequency"; + if (propName == WL_PROP_NAME) + label = "wavelength"; + + std::set<std::string> validUnits; + if (propName == WL_PROP_NAME) { + validUnits.insert("Angstrom"); + validUnits.insert("A"); + } else { + validUnits.insert("Hz"); + } + + for (auto name = names.begin(); name != names.end(); ++name) { + if (run.hasProperty(*name)) { + const std::string units = run.getProperty(*name)->units(); + + if (validUnits.find(units) != validUnits.end()) { + double value = run.getLogAsSingleValue(*name); + if (value == 0.) { + std::stringstream msg; + msg << "'" << *name << "' has a mean value of zero " << units; + g_log.information(msg.str()); + } else { + std::stringstream msg; + msg << "Found " << label << " in log '" << *name + << "' with mean value " << value << " " << units; + g_log.information(msg.str()); + return value; + } + } else { + std::stringstream msg; + msg << "When looking at " << *name + << " log encountered unknown units for " << label << ":" << units; + g_log.warning(msg.str()); + } + } + } + g_log.warning("Failed to determine " + label); + return 0.; +} + +/// Set the default values in the property manager +void PDDetermineCharacterizations::setDefaultsInPropManager() { + if (!m_propertyManager->existsProperty("frequency")) { + m_propertyManager->declareProperty( + new PropertyWithValue<double>("frequency", 0.)); + } + if (!m_propertyManager->existsProperty("wavelength")) { + m_propertyManager->declareProperty( + new PropertyWithValue<double>("wavelength", 0.)); + } + if (!m_propertyManager->existsProperty("bank")) { + m_propertyManager->declareProperty(new PropertyWithValue<int>("bank", 1)); + } + if (!m_propertyManager->existsProperty("vanadium")) { + m_propertyManager->declareProperty( + new PropertyWithValue<int32_t>("vanadium", 0)); + } + if (!m_propertyManager->existsProperty("container")) { + m_propertyManager->declareProperty( + new PropertyWithValue<int32_t>("container", 0)); + } + if (!m_propertyManager->existsProperty("empty")) { + m_propertyManager->declareProperty( + new PropertyWithValue<int32_t>("empty", 0)); + } + if (!m_propertyManager->existsProperty("d_min")) { + m_propertyManager->declareProperty( + new ArrayProperty<double>("d_min")); + } + if (!m_propertyManager->existsProperty("d_max")) { + m_propertyManager->declareProperty( + new ArrayProperty<double>("d_max")); + } + if (!m_propertyManager->existsProperty("tof_min")) { + m_propertyManager->declareProperty( + new PropertyWithValue<double>("tof_min", 0.)); + } + if (!m_propertyManager->existsProperty("tof_max")) { + m_propertyManager->declareProperty( + new PropertyWithValue<double>("tof_max", 0.)); + } +} + +/** + * Set the run number in the property manager from algoritm inputs. + * @param inputName + * @param propName + */ +void PDDetermineCharacterizations::overrideRunNumProperty( + const std::string &inputName, const std::string &propName) { + int32_t runnumber = this->getProperty(inputName); + if (runnumber != 0) { + if (runnumber < 0) + runnumber = 0; + m_propertyManager->setProperty(propName, runnumber); + } +} + +/// Execute the algorithm. +void PDDetermineCharacterizations::exec() { + // setup property manager to return + const std::string managerName = getPropertyValue("ReductionProperties"); + if (PropertyManagerDataService::Instance().doesExist(managerName)) { + m_propertyManager = + PropertyManagerDataService::Instance().retrieve(managerName); + } else { + m_propertyManager = boost::make_shared<Kernel::PropertyManager>(); + PropertyManagerDataService::Instance().addOrReplace(managerName, + m_propertyManager); + } + + setDefaultsInPropManager(); + + m_characterizations = getProperty(CHAR_PROP_NAME); + if (bool(m_characterizations) && (m_characterizations->rowCount() > 0)) { + API::MatrixWorkspace_sptr inputWS = getProperty("InputWorkspace"); + auto run = inputWS->mutableRun(); + + double frequency = getLogValue(run, FREQ_PROP_NAME); + + double wavelength = getLogValue(run, WL_PROP_NAME); + + getInformationFromTable(frequency, wavelength); + } + + overrideRunNumProperty("BackRun", "container"); + overrideRunNumProperty("NormRun", "vanadium"); + overrideRunNumProperty("NormBackRun", "empty"); + + std::vector<std::string> expectedNames = getColumnNames(); + for (auto it = expectedNames.begin(); it != expectedNames.end(); ++it) { + if (m_propertyManager->existsProperty(*it)) { + g_log.debug() << (*it) << ":" << m_propertyManager->getPropertyValue(*it) + << "\n"; + } else { + g_log.warning() << (*it) << " DOES NOT EXIST\n"; + } + } +} + +} // namespace Algorithms +} // namespace Mantid diff --git a/Code/Mantid/Framework/Algorithms/src/PlotAsymmetryByLogValue.cpp b/Code/Mantid/Framework/Algorithms/src/PlotAsymmetryByLogValue.cpp index 63977e949b669a36f6e2cb6cfd48dcc80bfdcdca..a6a088567548493a7307820d22e2b2e21acb93d8 100644 --- a/Code/Mantid/Framework/Algorithms/src/PlotAsymmetryByLogValue.cpp +++ b/Code/Mantid/Framework/Algorithms/src/PlotAsymmetryByLogValue.cpp @@ -180,7 +180,7 @@ void PlotAsymmetryByLogValue::exec() { // Create the 2D workspace for the output int nplots = m_greenY.size() ? 4 : 1; - size_t npoints = ie - is + 1; + size_t npoints = m_logValue.size(); MatrixWorkspace_sptr outWS = WorkspaceFactory::Instance().create( "Workspace2D", nplots, // the number of plots diff --git a/Code/Mantid/Framework/Algorithms/test/PDDetermineCharacterizationsTest.h b/Code/Mantid/Framework/Algorithms/test/PDDetermineCharacterizationsTest.h new file mode 100644 index 0000000000000000000000000000000000000000..d75da34b47c21de03bc894ea8f3dc8b384f4b9ca --- /dev/null +++ b/Code/Mantid/Framework/Algorithms/test/PDDetermineCharacterizationsTest.h @@ -0,0 +1,246 @@ +#ifndef MANTID_ALGORITHMS_PDDETERMINECHARACTERIZATIONSTEST_H_ +#define MANTID_ALGORITHMS_PDDETERMINECHARACTERIZATIONSTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidAlgorithms/PDDetermineCharacterizations.h" +#include "MantidAPI/FrameworkManager.h" +#include "MantidAPI/ITableWorkspace.h" +#include "MantidAPI/PropertyManagerDataService.h" +#include "MantidAPI/TableRow.h" +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/PropertyWithValue.h" + +using Mantid::Algorithms::PDDetermineCharacterizations; +using namespace Mantid::API; +using namespace Mantid::Kernel; + +const std::string PROPERTY_MANAGER_NAME = "__pd_reduction_properties"; + +class PDDetermineCharacterizationsTest : public CxxTest::TestSuite { +private: + std::string m_logWSName; + +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static PDDetermineCharacterizationsTest *createSuite() { + return new PDDetermineCharacterizationsTest(); + } + static void destroySuite(PDDetermineCharacterizationsTest *suite) { + delete suite; + } + + void createLogWksp() { + m_logWSName = "_det_char_log"; + + { + auto alg = + FrameworkManager::Instance().createAlgorithm("CreateWorkspace"); + alg->setPropertyValue("DataX", + "-1.0,-0.8,-0.6,-0.4,-0.2,0.0,0.2,0.4,0.6,0.8,1.0"); + alg->setPropertyValue("DataY", + "-1.0,-0.8,-0.6,-0.4,-0.2,0.0,0.2,0.4,0.6,0.8"); + alg->setPropertyValue("OutputWorkspace", m_logWSName); + TS_ASSERT(alg->execute()); + } + { + auto alg = FrameworkManager::Instance().createAlgorithm("AddSampleLog"); + alg->setPropertyValue("LogName", "frequency"); + alg->setPropertyValue("LogText", "60."); + alg->setPropertyValue("LogUnit", "Hz"); + alg->setPropertyValue("LogType", "Number"); + alg->setPropertyValue("Workspace", m_logWSName); + TS_ASSERT(alg->execute()); + } + { + auto alg = FrameworkManager::Instance().createAlgorithm("AddSampleLog"); + alg->setPropertyValue("LogName", "LambdaRequest"); + alg->setPropertyValue("LogText", "0.533"); + alg->setPropertyValue("LogUnit", "Angstrom"); + alg->setPropertyValue("LogType", "Number"); + alg->setPropertyValue("Workspace", m_logWSName); + TS_ASSERT(alg->execute()); + } + } + + void addRow(ITableWorkspace_sptr wksp, double freq, double wl, int bank, + int van, int can, int empty, std::string dmin, std::string dmax, + double tofmin, double tofmax) { + Mantid::API::TableRow row = wksp->appendRow(); + row << freq; + row << wl; + row << bank; + row << van; + row << can; + row << empty; + row << dmin; + row << dmax; + row << tofmin; + row << tofmax; + } + + ITableWorkspace_sptr createTableWksp(bool full) { + 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"); + + if (full) { + addRow(wksp, 60., 0.533, 1, 17702, 17711, 0, "0.05", "2.20", 0000.00, + 16666.67); + addRow(wksp, 60., 1.333, 3, 17703, 17712, 0, "0.43", "5.40", 12500.00, + 29166.67); + addRow(wksp, 60., 2.665, 4, 17704, 17713, 0, "1.15", "9.20", 33333.33, + 50000.00); + addRow(wksp, 60., 4.797, 5, 17705, 17714, 0, "2.00", "15.35", 66666.67, + 83333.67); + } + + return wksp; + } + + PropertyManager_sptr + createExpectedInfo(const double freq, const double wl, const int bank, + const int van, const int can, const int empty, + const std::string &dmin, const std::string &dmax, + const double tofmin, const double tofmax) { + + PropertyManager_sptr expectedInfo = boost::make_shared<PropertyManager>(); + expectedInfo->declareProperty( + new PropertyWithValue<double>("frequency", freq)); + expectedInfo->declareProperty( + new PropertyWithValue<double>("wavelength", wl)); + expectedInfo->declareProperty(new PropertyWithValue<int>("bank", bank)); + expectedInfo->declareProperty( + new PropertyWithValue<int32_t>("vanadium", van)); + expectedInfo->declareProperty( + new PropertyWithValue<int32_t>("container", can)); + expectedInfo->declareProperty( + new PropertyWithValue<int32_t>("empty", empty)); + expectedInfo->declareProperty( + new ArrayProperty<std::vector<double>>("d_min")); + expectedInfo->setPropertyValue("d_min", dmin); + expectedInfo->declareProperty( + new ArrayProperty<std::vector<double>>("d_max")); + expectedInfo->setPropertyValue("d_max", dmax); + expectedInfo->declareProperty( + new PropertyWithValue<double>("tof_min", tofmin)); + expectedInfo->declareProperty( + new PropertyWithValue<double>("tof_max", tofmax)); + + return expectedInfo; + } + + void compareResult(PropertyManager_sptr expected, + PropertyManager_sptr observed) { + TS_ASSERT_EQUALS(expected->propertyCount(), observed->propertyCount()); + + const std::vector<Property *> &expectedProps = expected->getProperties(); + + for (std::size_t i = 0; i < expectedProps.size(); ++i) { + const std::string name = expectedProps[i]->name(); + TS_ASSERT_EQUALS(expected->getPropertyValue(name), + observed->getPropertyValue(name)); + } + } + + void test_Init() { + PDDetermineCharacterizations alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT(alg.isInitialized()); + } + + void testNoChar() { + createLogWksp(); + // don't create characterization table + + PDDetermineCharacterizations alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("InputWorkspace", m_logWSName)); + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("ReductionProperties", PROPERTY_MANAGER_NAME)); + TS_ASSERT_THROWS_NOTHING(alg.execute();); + TS_ASSERT(alg.isExecuted()); + + auto expectedInfo = createExpectedInfo(0., 0., 1, 0, 0, 0, "", "", 0., 0.); + + compareResult(expectedInfo, PropertyManagerDataService::Instance().retrieve( + PROPERTY_MANAGER_NAME)); + } + + void testEmptyChar() { + createLogWksp(); + auto tableWS = createTableWksp(false); + + PDDetermineCharacterizations alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("InputWorkspace", m_logWSName)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Characterizations", tableWS)); + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("ReductionProperties", PROPERTY_MANAGER_NAME)); + TS_ASSERT_THROWS_NOTHING(alg.execute();); + TS_ASSERT(alg.isExecuted()); + + auto expectedInfo = createExpectedInfo(0., 0., 1, 0, 0, 0, "", "", 0., 0.); + + compareResult(expectedInfo, PropertyManagerDataService::Instance().retrieve( + PROPERTY_MANAGER_NAME)); + } + + void testFullChar() { + createLogWksp(); + auto tableWS = createTableWksp(true); + + PDDetermineCharacterizations alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("InputWorkspace", m_logWSName)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Characterizations", tableWS)); + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("ReductionProperties", PROPERTY_MANAGER_NAME)); + TS_ASSERT_THROWS_NOTHING(alg.execute();); + TS_ASSERT(alg.isExecuted()); + + auto expectedInfo = createExpectedInfo(60., 0.533, 1, 17702, 17711, 0, + "0.05", "2.20", 0000.00, 16666.67); + + compareResult(expectedInfo, PropertyManagerDataService::Instance().retrieve( + PROPERTY_MANAGER_NAME)); + } + + void testFullCharDisableChar() { + createLogWksp(); + auto tableWS = createTableWksp(true); + + PDDetermineCharacterizations alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("InputWorkspace", m_logWSName)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("Characterizations", tableWS)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("BackRun", -1)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("NormRun", -1)); + TS_ASSERT_THROWS_NOTHING(alg.setProperty("NormBackRun", -1)); + TS_ASSERT_THROWS_NOTHING( + alg.setPropertyValue("ReductionProperties", PROPERTY_MANAGER_NAME)); + TS_ASSERT_THROWS_NOTHING(alg.execute();); + TS_ASSERT(alg.isExecuted()); + + auto expectedInfo = createExpectedInfo(60., 0.533, 1, 0, 0, 0, "0.05", + "2.20", 0000.00, 16666.67); + + compareResult(expectedInfo, PropertyManagerDataService::Instance().retrieve( + PROPERTY_MANAGER_NAME)); + } +}; + +#endif /* MANTID_ALGORITHMS_PDDETERMINECHARACTERIZATIONS2TEST_H_ */ diff --git a/Code/Mantid/Framework/Crystal/src/PredictFractionalPeaks.cpp b/Code/Mantid/Framework/Crystal/src/PredictFractionalPeaks.cpp index 11058245f55677f8418d84b9f7546c057a3fba2c..146e9ade0342ecc2a0cbd36457a2697042aeff55 100644 --- a/Code/Mantid/Framework/Crystal/src/PredictFractionalPeaks.cpp +++ b/Code/Mantid/Framework/Crystal/src/PredictFractionalPeaks.cpp @@ -37,29 +37,13 @@ void PredictFractionalPeaks::init() { new WorkspaceProperty<IPeaksWorkspace>("FracPeaks", "", Direction::Output), "Workspace of Peaks with peaks with fractional h,k, and/or l values"); - - std::vector<double> hOffsetDefault(3); - hOffsetDefault[0] = -0.5; - hOffsetDefault[1] = 0.0; - hOffsetDefault[2] = 0.5; - - - declareProperty( - new Kernel::ArrayProperty<double>(string("HOffset"), hOffsetDefault), - "Offset in the h direction"); - - //const std::vector<double> kOffsetDefault(1, 0); - //std::vector<double>(1, 0) declareProperty( - new Kernel::ArrayProperty<double>(string("KOffset"), std::vector<double>(1, 0)), + new Kernel::ArrayProperty<double>(string("HOffset"), "-0.5,0.0,0.5"), "Offset in the h direction"); - - std::vector<double> lOffsetDefault(3); - lOffsetDefault[0] = -0.5; - lOffsetDefault[1] = 0.5; - + declareProperty(new Kernel::ArrayProperty<double>(string("KOffset"), "0"), + "Offset in the h direction"); declareProperty( - new Kernel::ArrayProperty<double>(string("LOffset"), lOffsetDefault), + new Kernel::ArrayProperty<double>(string("LOffset"), "-0.5,0.5"), "Offset in the h direction"); declareProperty("IncludeAllPeaksInRange", false, diff --git a/Code/Mantid/Framework/DataHandling/src/LoadNexusProcessed.cpp b/Code/Mantid/Framework/DataHandling/src/LoadNexusProcessed.cpp index 2d1c89864c2909de20e63524dc56e7a90163bdc7..b7b3e22b1f09ecd55814fc31781154f865ed6fae 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadNexusProcessed.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadNexusProcessed.cpp @@ -1024,10 +1024,19 @@ API::Workspace_sptr LoadNexusProcessed::loadPeaksEntry(NXEntry &entry) { // Get information from all but data group std::string parameterStr; // Hop to the right point /mantid_workspace_1 - m_cppFile->openPath(entry.path()); // This is + try { + m_cppFile->openPath(entry.path()); // This is + } catch (std::runtime_error &re) { + throw std::runtime_error("Error while opening a path in a Peaks entry in a " + "Nexus processed file. " + "This path is wrong: " + + entry.path() + + ". Lower level error description: " + re.what()); + } try { // This loads logs, sample, and instrument. - peakWS->loadExperimentInfoNexus(getPropertyValue("Filename"), m_cppFile, parameterStr); + peakWS->loadExperimentInfoNexus(getPropertyValue("Filename"), m_cppFile, + parameterStr); } catch (std::exception &e) { g_log.information("Error loading Instrument section of nxs file"); g_log.information(e.what()); @@ -1036,7 +1045,15 @@ API::Workspace_sptr LoadNexusProcessed::loadPeaksEntry(NXEntry &entry) { // Coordinates - Older versions did not have the separate field but used a log // value uint32_t loadCoord(0); - m_cppFile->openGroup("peaks_workspace", "NXentry"); + const std::string peaksWSName = "peaks_workspace"; + try { + m_cppFile->openGroup(peaksWSName, "NXentry"); + } catch (std::runtime_error &re) { + throw std::runtime_error( + "Error while opening a peaks workspace in a Nexus processed file. " + "Cannot open gropu " + + peaksWSName + ". Lower level error description: " + re.what()); + } try { m_cppFile->readData("coordinate_system", loadCoord); peakWS->setCoordinateSystem( diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/ArrayProperty.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/ArrayProperty.h index c24cdc880f56fd825e46c57878b06a9eb28351c3..00925093a7f803229f197caa0b86ffff958b28b6 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/ArrayProperty.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/ArrayProperty.h @@ -60,6 +60,7 @@ public: * @param validator :: The validator to use for this property, if required * @param direction :: The direction (Input/Output/InOut) of this property */ + ArrayProperty(const std::string &name, IValidator_sptr validator, const unsigned int direction = Direction::Input) : PropertyWithValue<std::vector<T>>(name, std::vector<T>(), validator, @@ -77,12 +78,14 @@ public: : PropertyWithValue<std::vector<T>>(name, std::vector<T>(), IValidator_sptr(new NullValidator), direction) {} - /** Constructor from which you can set the property's values through a string - * - * The constructor of the base class is called with an empty vector for the default values - * The values are set directly from the string. Since the default values are never initialized, isDefault - * will return false for any non-empty string input. - * + + /** Constructor from which you can set the property's values through a string: + * + * Inherits from the constructor of PropertyWithValue specifically made to + * handle a list + * of numeric values in a string format so that initial value is set + * correctly. + * * @param name :: The name to assign to the property * @param values :: A comma-separated string containing the values to * store in the property @@ -94,14 +97,8 @@ public: ArrayProperty(const std::string &name, const std::string &values, IValidator_sptr validator = IValidator_sptr(new NullValidator), const unsigned int direction = Direction::Input) - : PropertyWithValue<std::vector<T>>(name, std::vector<T>(), validator, - direction) { - std::string result = this->setValue(values); - if (!result.empty()) { - throw std::invalid_argument( - "Invalid values string passed to constructor: " + result); - } - } + : PropertyWithValue<std::vector<T>>(name, std::vector<T>(), values, + validator, direction) {} /// Copy constructor ArrayProperty(const ArrayProperty &right) diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyWithValue.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyWithValue.h index a9f01380e8f7da408ae397de663029a483c03701..7372466d8515faaf84e9b68e7d38150eb1f36003 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyWithValue.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyWithValue.h @@ -183,6 +183,16 @@ void toValue(const std::string &strvalue, std::vector<std::vector<T>> &value, } } +/*Used specifically to retrieve a vector of type T populated with values + * given to it from strvalue parameter, Using toValue method. + (See constructor used specifically for vector assignments) + */ +template <typename T> T extractToValueVector(const std::string &strvalue) { + T valueVec; + toValue(strvalue, valueVec); + return valueVec; +} + /// Macro for the vector<int> specializations #define PROPERTYWITHVALUE_TOVALUE(type) \ template <> \ @@ -277,6 +287,30 @@ public: m_initialValue(defaultValue), m_validator(boost::make_shared<NullValidator>()) {} + /* + Constructor to handle vector value assignments to m_initialValue + so they can be remembered when the algorithm dialog is reloaded. + */ + /**Constructor + * @param name :: The name to assign to the property. + * @param defaultValue :: A vector of numerical type, empty to comply with + * other definitions. + * @param defaultValueStr :: The numerical values you wish to assign to the + * property + * @param validator :: The validator to use for this property + * @param direction :: Whether this is a Direction::Input, Direction::Output + * or Direction::InOut (Input & Output) property + */ + PropertyWithValue(const std::string &name, const TYPE &defaultValue, + const std::string defaultValueStr, + IValidator_sptr validator, const unsigned int direction) + : Property(name, typeid(TYPE), direction), + m_value(extractToValueVector<TYPE>(defaultValueStr)), + m_initialValue(extractToValueVector<TYPE>(defaultValueStr)), + m_validator(validator) { + UNUSED_ARG(defaultValue); + } + /**Copy constructor * Note the default value of the copied object is the initial value of * original @@ -473,7 +507,8 @@ protected: /// The value of the property TYPE m_value; /// the property's default value which is also its initial value - const TYPE m_initialValue; + // const TYPE m_initialValue; + TYPE m_initialValue; private: /** diff --git a/Code/Mantid/Framework/Kernel/test/ArrayPropertyTest.h b/Code/Mantid/Framework/Kernel/test/ArrayPropertyTest.h index afffba5d8a5c0f858fccf17378048f0988127958..2ab39ea4e6a34369ae51e73bc4ca97835f95201b 100644 --- a/Code/Mantid/Framework/Kernel/test/ArrayPropertyTest.h +++ b/Code/Mantid/Framework/Kernel/test/ArrayPropertyTest.h @@ -7,279 +7,292 @@ using namespace Mantid::Kernel; -class ArrayPropertyTest : public CxxTest::TestSuite -{ +class ArrayPropertyTest : public CxxTest::TestSuite { public: - void setUp() - { + void setUp() { iProp = new ArrayProperty<int>("intProp"); dProp = new ArrayProperty<double>("doubleProp"); - sProp = new ArrayProperty<std::string>("stringProp"); + sProp = new ArrayProperty<std::string>("stringProp"); } - - void tearDown() - { + + void tearDown() { delete iProp; delete dProp; delete sProp; } - - void testConstructor() - { - TS_ASSERT( ! iProp->name().compare("intProp") ) - TS_ASSERT( ! iProp->documentation().compare("") ) - TS_ASSERT( typeid( std::vector<int> ) == *iProp->type_info() ) - TS_ASSERT( iProp->isDefault() ) - TS_ASSERT( iProp->operator()().empty() ) - - TS_ASSERT( ! dProp->name().compare("doubleProp") ) - TS_ASSERT( ! dProp->documentation().compare("") ) - TS_ASSERT( typeid( std::vector<double> ) == *dProp->type_info() ) - TS_ASSERT( dProp->isDefault() ) - TS_ASSERT( dProp->operator()().empty() ) - - TS_ASSERT( ! sProp->name().compare("stringProp") ) - TS_ASSERT( ! sProp->documentation().compare("") ) - TS_ASSERT( typeid( std::vector<std::string> ) == *sProp->type_info() ) - TS_ASSERT( sProp->isDefault() ) - TS_ASSERT( sProp->operator()().empty() ) - - std::vector<int> i(5,2); - ArrayProperty<int> ip("ip",i); - TS_ASSERT_EQUALS( ip.operator()().size(), 5 ) - TS_ASSERT_EQUALS( ip.operator()()[3], 2 ) - - std::vector<double> d(4,6.66); - ArrayProperty<double> dp("dp",d); - TS_ASSERT_EQUALS( dp.operator()().size(), 4 ) - TS_ASSERT_EQUALS( dp.operator()()[1], 6.66 ) - - std::vector<std::string> s(3,"yyy"); - ArrayProperty<std::string> sp("sp",s); - TS_ASSERT_EQUALS( sp.operator()().size(), 3 ) - TS_ASSERT( ! sp.operator()()[2].compare("yyy") ) + + void testConstructor() { + TS_ASSERT(!iProp->name().compare("intProp")) + TS_ASSERT(!iProp->documentation().compare("")) + TS_ASSERT(typeid(std::vector<int>) == *iProp->type_info()) + TS_ASSERT(iProp->isDefault()) + TS_ASSERT(iProp->operator()().empty()) + + TS_ASSERT(!dProp->name().compare("doubleProp")) + TS_ASSERT(!dProp->documentation().compare("")) + TS_ASSERT(typeid(std::vector<double>) == *dProp->type_info()) + TS_ASSERT(dProp->isDefault()) + TS_ASSERT(dProp->operator()().empty()) + + TS_ASSERT(!sProp->name().compare("stringProp")) + TS_ASSERT(!sProp->documentation().compare("")) + TS_ASSERT(typeid(std::vector<std::string>) == *sProp->type_info()) + TS_ASSERT(sProp->isDefault()) + TS_ASSERT(sProp->operator()().empty()) + + std::vector<int> i(5, 2); + ArrayProperty<int> ip("ip", i); + TS_ASSERT_EQUALS(ip.operator()().size(), 5) + TS_ASSERT_EQUALS(ip.operator()()[3], 2) + + std::vector<double> d(4, 6.66); + ArrayProperty<double> dp("dp", d); + TS_ASSERT_EQUALS(dp.operator()().size(), 4) + TS_ASSERT_EQUALS(dp.operator()()[1], 6.66) + + std::vector<std::string> s(3, "yyy"); + ArrayProperty<std::string> sp("sp", s); + TS_ASSERT_EQUALS(sp.operator()().size(), 3) + TS_ASSERT(!sp.operator()()[2].compare("yyy")) } - void testSize() - { + void testSize() { TS_ASSERT_EQUALS(0, iProp->size()); TS_ASSERT_EQUALS(0, dProp->size()); TS_ASSERT_EQUALS(0, sProp->size()); // Make something bigger and test that. - Property* a = new ArrayProperty<int>("int_property", "1, 2, 3"); + Property *a = new ArrayProperty<int>("int_property", "1, 2, 3"); TS_ASSERT_EQUALS(3, a->size()); delete a; // Test vector of vector. - std::vector<std::vector<int> > input; - input.push_back(std::vector<int>(10)); // Make it 10 elements long, but should only be the size of the parent vector that is counted. - Property* b = new ArrayProperty<std::vector<int> >("vec_property", input); + std::vector<std::vector<int>> input; + input.push_back(std::vector<int>(10)); // Make it 10 elements long, but + // should only be the size of the + // parent vector that is counted. + Property *b = new ArrayProperty<std::vector<int>>("vec_property", input); TS_ASSERT_EQUALS(1, b->size()); delete b; } - void testConstructorByString() - { - ArrayProperty<int> i("i","1,2,3"); - TS_ASSERT_EQUALS( i.operator()()[0], 1 ) - TS_ASSERT_EQUALS( i.operator()()[1], 2 ) - TS_ASSERT_EQUALS( i.operator()()[2], 3 ) + void testConstructorByString() { + const std::string &i_stringValue = "1,2,3"; + ArrayProperty<int> i("i", i_stringValue); + TS_ASSERT_EQUALS(i.operator()()[0], 1); + TS_ASSERT_EQUALS(i.operator()()[1], 2); + TS_ASSERT_EQUALS(i.operator()()[2], 3); + TS_ASSERT_EQUALS(i.getDefault(), i_stringValue); + TS_ASSERT(i.isDefault()); ArrayProperty<int> i2("i", "-1-1"); - TS_ASSERT_EQUALS( i2.operator()()[0], -1); - TS_ASSERT_EQUALS( i2.operator()()[1], 0); - TS_ASSERT_EQUALS( i2.operator()()[2], 1); + TS_ASSERT_EQUALS(i2.operator()()[0], -1); + TS_ASSERT_EQUALS(i2.operator()()[1], 0); + TS_ASSERT_EQUALS(i2.operator()()[2], 1); ArrayProperty<int> i4("i", "-1:1"); - TS_ASSERT_EQUALS( i4.operator()()[0], -1); - TS_ASSERT_EQUALS( i4.operator()()[1], 0); - TS_ASSERT_EQUALS( i4.operator()()[2], 1); + TS_ASSERT_EQUALS(i4.operator()()[0], -1); + TS_ASSERT_EQUALS(i4.operator()()[1], 0); + TS_ASSERT_EQUALS(i4.operator()()[2], 1); ArrayProperty<int> i5("i", "-3--1"); - TS_ASSERT_EQUALS( i5.operator()()[0], -3); - TS_ASSERT_EQUALS( i5.operator()()[1], -2); - TS_ASSERT_EQUALS( i5.operator()()[2], -1); + TS_ASSERT_EQUALS(i5.operator()()[0], -3); + TS_ASSERT_EQUALS(i5.operator()()[1], -2); + TS_ASSERT_EQUALS(i5.operator()()[2], -1); ArrayProperty<int> i7("i", "-3:-1"); - TS_ASSERT_EQUALS( i7.operator()()[0], -3); - TS_ASSERT_EQUALS( i7.operator()()[1], -2); - TS_ASSERT_EQUALS( i7.operator()()[2], -1); + TS_ASSERT_EQUALS(i7.operator()()[0], -3); + TS_ASSERT_EQUALS(i7.operator()()[1], -2); + TS_ASSERT_EQUALS(i7.operator()()[2], -1); ArrayProperty<unsigned int> i3("i", "0:2,5"); - TS_ASSERT_EQUALS( i3.operator()()[0], 0); - TS_ASSERT_EQUALS( i3.operator()()[1], 1); - TS_ASSERT_EQUALS( i3.operator()()[2], 2); - TS_ASSERT_EQUALS( i3.operator()()[3], 5); + TS_ASSERT_EQUALS(i3.operator()()[0], 0); + TS_ASSERT_EQUALS(i3.operator()()[1], 1); + TS_ASSERT_EQUALS(i3.operator()()[2], 2); + TS_ASSERT_EQUALS(i3.operator()()[3], 5); ArrayProperty<unsigned int> i6("i", "5,0-2,5"); - TS_ASSERT_EQUALS( i6.operator()()[0], 5); - TS_ASSERT_EQUALS( i6.operator()()[1], 0); - TS_ASSERT_EQUALS( i6.operator()()[2], 1); - TS_ASSERT_EQUALS( i6.operator()()[3], 2); - TS_ASSERT_EQUALS( i6.operator()()[4], 5); - - ArrayProperty<double> d("d","7.77,8.88,9.99"); - TS_ASSERT_EQUALS( d.operator()()[0], 7.77 ) - TS_ASSERT_EQUALS( d.operator()()[1], 8.88 ) - TS_ASSERT_EQUALS( d.operator()()[2], 9.99 ) - - ArrayProperty<std::string> s("d","a,b,c"); - TS_ASSERT( ! s.operator()()[0].compare("a") ) - TS_ASSERT( ! s.operator()()[1].compare("b") ) - TS_ASSERT( ! s.operator()()[2].compare("c") ) - - TS_ASSERT_THROWS( ArrayProperty<int> ii("ii","aa,bb"), std::invalid_argument ) - TS_ASSERT_THROWS( ArrayProperty<int> ii("ii","5.5,6.6"), std::invalid_argument ) - TS_ASSERT_THROWS( ArrayProperty<double> dd("dd","aa,bb"), std::invalid_argument ) + TS_ASSERT_EQUALS(i6.operator()()[0], 5); + TS_ASSERT_EQUALS(i6.operator()()[1], 0); + TS_ASSERT_EQUALS(i6.operator()()[2], 1); + TS_ASSERT_EQUALS(i6.operator()()[3], 2); + TS_ASSERT_EQUALS(i6.operator()()[4], 5); + + ArrayProperty<double> d("d", "7.77,8.88,9.99"); + TS_ASSERT_EQUALS(d.operator()()[0], 7.77) + TS_ASSERT_EQUALS(d.operator()()[1], 8.88) + TS_ASSERT_EQUALS(d.operator()()[2], 9.99) + + ArrayProperty<double> d2("d", "-0.15,0.0,0.15"); + TS_ASSERT_EQUALS(d2.operator()()[0], -0.15) + TS_ASSERT_EQUALS(d2.operator()()[1], 0.0) + TS_ASSERT_EQUALS(d2.operator()()[2], 0.15) + // Now we change the values that are set in the indices of d2 + // by using a string + d2.setValue("0.3,0.1,-0.2"); + TS_ASSERT_EQUALS(d2.operator()()[0], 0.3) + TS_ASSERT_EQUALS(d2.operator()()[1], 0.1) + TS_ASSERT_EQUALS(d2.operator()()[2], -0.2) + TS_ASSERT_EQUALS(d2.value(), "0.3,0.1,-0.2"); + TS_ASSERT(!d2.isDefault()); + + ArrayProperty<std::string> s("d", "a,b,c"); + TS_ASSERT(!s.operator()()[0].compare("a")) + TS_ASSERT(!s.operator()()[1].compare("b")) + TS_ASSERT(!s.operator()()[2].compare("c")) + + TS_ASSERT_THROWS(ArrayProperty<int> ii("ii", "aa,bb"), std::bad_cast) + TS_ASSERT_THROWS(ArrayProperty<int> ii("ii", "5.5,6.6"), std::bad_cast) + TS_ASSERT_THROWS(ArrayProperty<double> dd("dd", "aa,bb"), std::bad_cast) } - - void testCopyConstructor() - { + + void testCopyConstructor() { ArrayProperty<int> i = *iProp; - TS_ASSERT( ! i.name().compare("intProp") ) - TS_ASSERT( ! i.documentation().compare("") ) - TS_ASSERT( typeid( std::vector<int> ) == *i.type_info() ) - TS_ASSERT( i.isDefault() ) - TS_ASSERT( i.operator()().empty() ) - + TS_ASSERT(!i.name().compare("intProp")) + TS_ASSERT(!i.documentation().compare("")) + TS_ASSERT(typeid(std::vector<int>) == *i.type_info()) + TS_ASSERT(i.isDefault()) + TS_ASSERT(i.operator()().empty()) + ArrayProperty<double> d = *dProp; - TS_ASSERT( ! d.name().compare("doubleProp") ) - TS_ASSERT( ! d.documentation().compare("") ) - TS_ASSERT( typeid( std::vector<double> ) == *d.type_info() ) - TS_ASSERT( d.isDefault() ) - TS_ASSERT( d.operator()().empty() ) - + TS_ASSERT(!d.name().compare("doubleProp")) + TS_ASSERT(!d.documentation().compare("")) + TS_ASSERT(typeid(std::vector<double>) == *d.type_info()) + TS_ASSERT(d.isDefault()) + TS_ASSERT(d.operator()().empty()) + ArrayProperty<std::string> s = *sProp; - TS_ASSERT( ! s.name().compare("stringProp") ) - TS_ASSERT( ! s.documentation().compare("") ) - TS_ASSERT( typeid( std::vector<std::string> ) == *s.type_info() ) - TS_ASSERT( s.isDefault() ) - TS_ASSERT( s.operator()().empty() ) + TS_ASSERT(!s.name().compare("stringProp")) + TS_ASSERT(!s.documentation().compare("")) + TS_ASSERT(typeid(std::vector<std::string>) == *s.type_info()) + TS_ASSERT(s.isDefault()) + TS_ASSERT(s.operator()().empty()) } - void testValue() - { - std::vector<int> i(3,3); - ArrayProperty<int> ip("ip",i); - TS_ASSERT( ! ip.value().compare("3,3,3") ) - - std::vector<double> d(4,1.23); - ArrayProperty<double> dp("dp",d); - TS_ASSERT( ! dp.value().compare("1.23,1.23,1.23,1.23") ) - - std::vector<std::string> s(2,"yyy"); - ArrayProperty<std::string> sp("sp",s); - TS_ASSERT( ! sp.value().compare("yyy,yyy") ) + void testValue() { + std::vector<int> i(3, 3); + ArrayProperty<int> ip("ip", i); + TS_ASSERT(!ip.value().compare("3,3,3")) + + std::vector<double> d(4, 1.23); + ArrayProperty<double> dp("dp", d); + TS_ASSERT(!dp.value().compare("1.23,1.23,1.23,1.23")) + + std::vector<std::string> s(2, "yyy"); + ArrayProperty<std::string> sp("sp", s); + TS_ASSERT(!sp.value().compare("yyy,yyy")) } - void testSetValueAndIsDefault() - { - std::string couldnt = "Could not set property ", cant = ". Can not convert \""; - - TS_ASSERT_EQUALS( iProp->setValue("1.1,2,2"), - couldnt + iProp->name() + cant + "1.1,2,2\" to " + iProp->type() ) - TS_ASSERT( iProp->operator()().empty() ) - TS_ASSERT( iProp->isDefault() ) - TS_ASSERT_EQUALS( iProp->setValue("aaa,bbb"), - couldnt + iProp->name() + cant + "aaa,bbb\" to " + iProp->type() ) - TS_ASSERT( iProp->operator()().empty() ) - TS_ASSERT( iProp->isDefault() ) - TS_ASSERT_EQUALS( iProp->setValue("1,2,3,4"), "" ) - TS_ASSERT_EQUALS( iProp->operator()().size(), 4 ) - for ( std::size_t i=0; i < 4; ++i ) - { - TS_ASSERT_EQUALS( iProp->operator()()[i], i+1 ) + void testSetValueAndIsDefault() { + std::string couldnt = "Could not set property ", + cant = ". Can not convert \""; + + TS_ASSERT_EQUALS(iProp->setValue("1.1,2,2"), couldnt + iProp->name() + + cant + "1.1,2,2\" to " + + iProp->type()) + TS_ASSERT(iProp->operator()().empty()) + TS_ASSERT(iProp->isDefault()) + TS_ASSERT_EQUALS(iProp->setValue("aaa,bbb"), couldnt + iProp->name() + + cant + "aaa,bbb\" to " + + iProp->type()) + TS_ASSERT(iProp->operator()().empty()) + TS_ASSERT(iProp->isDefault()) + TS_ASSERT_EQUALS(iProp->setValue("1,2,3,4"), "") + TS_ASSERT_EQUALS(iProp->operator()().size(), 4) + for (std::size_t i = 0; i < 4; ++i) { + TS_ASSERT_EQUALS(iProp->operator()()[i], i + 1) } - TS_ASSERT( !iProp->isDefault() ) - TS_ASSERT_EQUALS( iProp->setValue(""), "" ) - TS_ASSERT( iProp->operator()().empty() ) - TS_ASSERT( iProp->isDefault() ) - - TS_ASSERT_EQUALS( dProp->setValue("aaa,bbb"), - couldnt + dProp->name() + cant + "aaa,bbb\" to " + dProp->type() ) - TS_ASSERT( dProp->operator()().empty() ) - TS_ASSERT( dProp->isDefault() ) - TS_ASSERT_EQUALS( dProp->setValue("1,2"), "" ) - TS_ASSERT_EQUALS( dProp->operator()()[1], 2 ) - TS_ASSERT( !dProp->isDefault() ) - TS_ASSERT_EQUALS( dProp->setValue("1.11,2.22,3.33,4.44"), "" ) - TS_ASSERT_EQUALS( dProp->operator()()[0], 1.11 ) - TS_ASSERT( !dProp->isDefault() ) - TS_ASSERT_EQUALS( dProp->setValue(""), "" ) - TS_ASSERT( dProp->operator()().empty() ) - TS_ASSERT( dProp->isDefault() ) - - TS_ASSERT_EQUALS( sProp->setValue("This,is,a,test"), "" ) - TS_ASSERT_EQUALS( sProp->operator()()[2], "a" ) - TS_ASSERT( !sProp->isDefault() ) - TS_ASSERT_EQUALS( sProp->setValue(""), "" ) - TS_ASSERT( sProp->operator()().empty() ) - TS_ASSERT( sProp->isDefault() ) + TS_ASSERT(!iProp->isDefault()) + TS_ASSERT_EQUALS(iProp->setValue(""), "") + TS_ASSERT(iProp->operator()().empty()) + TS_ASSERT(iProp->isDefault()) + + TS_ASSERT_EQUALS(dProp->setValue("aaa,bbb"), couldnt + dProp->name() + + cant + "aaa,bbb\" to " + + dProp->type()) + TS_ASSERT(dProp->operator()().empty()) + TS_ASSERT(dProp->isDefault()) + TS_ASSERT_EQUALS(dProp->setValue("1,2"), "") + TS_ASSERT_EQUALS(dProp->operator()()[1], 2) + TS_ASSERT(!dProp->isDefault()) + TS_ASSERT_EQUALS(dProp->setValue("1.11,2.22,3.33,4.44"), "") + TS_ASSERT_EQUALS(dProp->operator()()[0], 1.11) + TS_ASSERT(!dProp->isDefault()) + TS_ASSERT_EQUALS(dProp->setValue(""), "") + TS_ASSERT(dProp->operator()().empty()) + TS_ASSERT(dProp->isDefault()) + + TS_ASSERT_EQUALS(sProp->setValue("This,is,a,test"), "") + TS_ASSERT_EQUALS(sProp->operator()()[2], "a") + TS_ASSERT(!sProp->isDefault()) + TS_ASSERT_EQUALS(sProp->setValue(""), "") + TS_ASSERT(sProp->operator()().empty()) + TS_ASSERT(sProp->isDefault()) } - void testAssignmentOperator() - { + void testAssignmentOperator() { ArrayProperty<int> i("i"); - TS_ASSERT( i.isDefault() ) - std::vector<int> ii(3,4); - TS_ASSERT_EQUALS( i = ii, ii ) - TS_ASSERT_EQUALS( i.operator()()[1], 4 ) - TS_ASSERT( !i.isDefault() ) + TS_ASSERT(i.isDefault()) + std::vector<int> ii(3, 4); + TS_ASSERT_EQUALS(i = ii, ii) + TS_ASSERT_EQUALS(i.operator()()[1], 4) + TS_ASSERT(!i.isDefault()) ArrayProperty<double> d("d"); - TS_ASSERT( d.isDefault() ) - std::vector<double> dd(5,9.99); - TS_ASSERT_EQUALS( d = dd, dd ) - TS_ASSERT_EQUALS( d.operator()()[3], 9.99 ) - TS_ASSERT( !d.isDefault() ) + TS_ASSERT(d.isDefault()) + std::vector<double> dd(5, 9.99); + TS_ASSERT_EQUALS(d = dd, dd) + TS_ASSERT_EQUALS(d.operator()()[3], 9.99) + TS_ASSERT(!d.isDefault()) ArrayProperty<std::string> s("s"); - TS_ASSERT( s.isDefault() ) - std::vector<std::string> ss(2,"zzz"); - TS_ASSERT_EQUALS( s = ss, ss ) - TS_ASSERT_EQUALS( s.operator()()[0], "zzz" ) - TS_ASSERT( !s.isDefault() ) + TS_ASSERT(s.isDefault()) + std::vector<std::string> ss(2, "zzz"); + TS_ASSERT_EQUALS(s = ss, ss) + TS_ASSERT_EQUALS(s.operator()()[0], "zzz") + TS_ASSERT(!s.isDefault()) } - - void testOperatorBrackets() - { - TS_ASSERT( iProp->operator()().empty() ) - TS_ASSERT( dProp->operator()().empty() ) - TS_ASSERT( sProp->operator()().empty() ) + + void testOperatorBrackets() { + TS_ASSERT(iProp->operator()().empty()) + TS_ASSERT(dProp->operator()().empty()) + TS_ASSERT(sProp->operator()().empty()) } - - void testOperatorNothing() - { + + void testOperatorNothing() { std::vector<int> i = *iProp; - TS_ASSERT( i.empty() ) - - std::vector<double> d(3,8.8); + TS_ASSERT(i.empty()) + + std::vector<double> d(3, 8.8); *dProp = d; std::vector<double> dd = *dProp; - for(std::size_t i = 0; i<3; ++i) - { - TS_ASSERT_EQUALS( dProp->operator()()[i], 8.8 ) + for (std::size_t i = 0; i < 3; ++i) { + TS_ASSERT_EQUALS(dProp->operator()()[i], 8.8) } - + std::vector<std::string> s = *sProp; - TS_ASSERT( s.empty() ) + TS_ASSERT(s.empty()) } - - void testCasting() - { - TS_ASSERT_DIFFERS( dynamic_cast<PropertyWithValue<std::vector<int> >*>(iProp), - static_cast<PropertyWithValue<std::vector<int> >*>(0) ) - TS_ASSERT_DIFFERS( dynamic_cast<PropertyWithValue<std::vector<double> >*>(dProp), - static_cast<PropertyWithValue<std::vector<double> >*>(0) ) - TS_ASSERT_DIFFERS( dynamic_cast<PropertyWithValue<std::vector<std::string> >*>(sProp), - static_cast<PropertyWithValue<std::vector<std::string> >*>(0) ) - - TS_ASSERT_DIFFERS( dynamic_cast<Property*>(iProp), static_cast<Property*>(0) ) - TS_ASSERT_DIFFERS( dynamic_cast<Property*>(dProp), static_cast<Property*>(0) ) - TS_ASSERT_DIFFERS( dynamic_cast<Property*>(sProp), static_cast<Property*>(0) ) + + void testCasting() { + TS_ASSERT_DIFFERS( + dynamic_cast<PropertyWithValue<std::vector<int>> *>(iProp), + static_cast<PropertyWithValue<std::vector<int>> *>(0)) + TS_ASSERT_DIFFERS( + dynamic_cast<PropertyWithValue<std::vector<double>> *>(dProp), + static_cast<PropertyWithValue<std::vector<double>> *>(0)) + TS_ASSERT_DIFFERS( + dynamic_cast<PropertyWithValue<std::vector<std::string>> *>(sProp), + static_cast<PropertyWithValue<std::vector<std::string>> *>(0)) + + TS_ASSERT_DIFFERS(dynamic_cast<Property *>(iProp), + static_cast<Property *>(0)) + TS_ASSERT_DIFFERS(dynamic_cast<Property *>(dProp), + static_cast<Property *>(0)) + TS_ASSERT_DIFFERS(dynamic_cast<Property *>(sProp), + static_cast<Property *>(0)) } - + private: ArrayProperty<int> *iProp; ArrayProperty<double> *dProp; diff --git a/Code/Mantid/Framework/Nexus/src/NexusClasses.cpp b/Code/Mantid/Framework/Nexus/src/NexusClasses.cpp index 83c01c82b57a8ff240e8690f1ca804eab89bc3e1..15cb5abfa4f386753cc17d4417f4c2c8e47b6cff 100644 --- a/Code/Mantid/Framework/Nexus/src/NexusClasses.cpp +++ b/Code/Mantid/Framework/Nexus/src/NexusClasses.cpp @@ -179,8 +179,10 @@ void NXClass::open() { // if (NX_ERROR == NXopengroup(m_fileID,name().c_str(),NX_class().c_str())) //{ if (NX_ERROR == NXopengrouppath(m_fileID, m_path.c_str())) { - throw std::runtime_error("Cannot open group " + m_path + " of class " + - NX_class()); + + throw std::runtime_error("Cannot open group " + name() + " of class " + + NX_class() + " (trying to open path " + m_path + + ")"); } //} m_open = true; @@ -215,8 +217,9 @@ bool NXClass::openLocal(const std::string &nxclass) { void NXClass::close() { if (NX_ERROR == NXclosegroup(m_fileID)) { - throw std::runtime_error("Cannot close group " + m_path + " of class " + - NX_class()); + throw std::runtime_error("Cannot close group " + name() + " of class " + + NX_class() + " (trying to close path " + m_path + + ")"); } m_open = false; } diff --git a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/Algorithm.cpp b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/Algorithm.cpp index c31420c5f18edda2ba6b6733b5092ef294fef23d..2cf3dcd25225a2fff21d7ec3219c47310c006d4e 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/Algorithm.cpp +++ b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/Algorithm.cpp @@ -10,6 +10,7 @@ #include <boost/python/bases.hpp> #include <boost/python/class.hpp> +#include <boost/python/exception_translator.hpp> #include <boost/python/overloads.hpp> #include <boost/python/register_ptr_to_python.hpp> #include <boost/python/scope.hpp> @@ -49,11 +50,22 @@ BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType2_Overload, PythonAlgorithm::declarePyAlgProperty, 3, 6) BOOST_PYTHON_FUNCTION_OVERLOADS(declarePropertyType3_Overload, PythonAlgorithm::declarePyAlgProperty, 4, 5) + +/** + * Map a CancelException to a Python KeyboardInterupt + * @param exc A cancel exception to translate. Unused here as the message is + * ignored + */ +void translateCancel(const Algorithm::CancelException &exc) { + UNUSED_ARG(exc); + PyErr_SetString(PyExc_KeyboardInterrupt, ""); } -void export_leaf_classes() { +} +void export_leaf_classes() { register_ptr_to_python<boost::shared_ptr<Algorithm>>(); + register_exception_translator<Algorithm::CancelException>(&translateCancel); // Export Algorithm but the actual held type in Python is // boost::shared_ptr<AlgorithmAdapter> diff --git a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IAlgorithm.cpp b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IAlgorithm.cpp index 8e818c28e492a9a9fe271607d534b09ae8a87faa..47d942b878559b732f2c12f1e6c4cf211129645c 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IAlgorithm.cpp +++ b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IAlgorithm.cpp @@ -9,12 +9,15 @@ #pragma warning(default : 4250) #endif #include "MantidKernel/Strings.h" +#include "MantidPythonInterface/kernel/IsNone.h" #include "MantidPythonInterface/kernel/Policies/VectorToNumpy.h" #include <Poco/Thread.h> +#include <boost/python/arg_from_python.hpp> #include <boost/python/bases.hpp> #include <boost/python/class.hpp> +#include <boost/python/dict.hpp> #include <boost/python/object.hpp> #include <boost/python/operators.hpp> #include <boost/python/register_ptr_to_python.hpp> @@ -26,15 +29,35 @@ using Mantid::API::AlgorithmID; using Mantid::API::IAlgorithm; using Mantid::API::IAlgorithm_sptr; using Mantid::PythonInterface::AlgorithmIDProxy; +using Mantid::PythonInterface::isNone; using Mantid::PythonInterface::Policies::VectorToNumpy; using namespace boost::python; namespace { -/*********************************************************************** - * - * Useful functions within Python to have on the algorithm interface - * - ***********************************************************************/ + +// Global map of the thread ID to the current algorithm object +dict THREAD_ID_MAP; + +/** + * Private method to add an algorithm reference to the thread id map. + * It replaces any current reference with the same ID + * @param threadID The current Python thread ID + * @param alg A Python reference to the algorithm object + */ +void _trackAlgorithmInThread(long threadID, const object & alg) { + THREAD_ID_MAP[threadID] = alg; +} + +/** + * Return the algorithm object for the given thread ID or None + * if one doesn't exist. The entry is removed from the map + * if it is found + */ +object _algorithmInThread(long threadID) { + auto value = THREAD_ID_MAP.get(threadID); + if(value) api::delitem(THREAD_ID_MAP, threadID); + return value; +} /// Functor for use with sorting algorithm to put the properties that do not /// have valid values first @@ -130,7 +153,6 @@ PyObject *getOutputProperties(IAlgorithm &self) { return names; } -// ---------------------- Documentation ------------------------------------- /** * Create a doc string for the simple API * @param self :: A pointer to the python object wrapping and Algorithm @@ -176,18 +198,36 @@ std::string createDocString(IAlgorithm &self) { return buffer.str(); } +/** + * RAII struct to drop the GIL and reacquire it on destruction. + * If the algorithm is not a child then it added to the map of + * tracked algorithms. See executeProxy for a more + * detailed explanation + */ struct AllowCThreads { - AllowCThreads() : m_tracefunc(NULL), m_tracearg(NULL), m_saved(NULL) { + AllowCThreads(const object & algm) + : m_tracefunc(NULL), m_tracearg(NULL), m_saved(NULL), + m_tracking(false) { PyThreadState *curThreadState = PyThreadState_GET(); - assert(curThreadState != NULL); m_tracefunc = curThreadState->c_tracefunc; m_tracearg = curThreadState->c_traceobj; Py_XINCREF(m_tracearg); PyEval_SetTrace(NULL, NULL); + if(!isNone(algm)) { + _trackAlgorithmInThread(curThreadState->thread_id, algm); + m_tracking = true; + } m_saved = PyEval_SaveThread(); } ~AllowCThreads() { PyEval_RestoreThread(m_saved); + if(m_tracking) { + try { + api::delitem(THREAD_ID_MAP, m_saved->thread_id); + } catch(error_already_set&) { + PyErr_Clear(); + } + } PyEval_SetTrace(m_tracefunc, m_tracearg); Py_XDECREF(m_tracearg); } @@ -196,32 +236,31 @@ private: Py_tracefunc m_tracefunc; PyObject *m_tracearg; PyThreadState *m_saved; + bool m_tracking; }; /** - * Releases the GIL and disables any tracer functions, executes the calling - *algorithm object - * and re-acquires the GIL and resets the tracing functions. - * The trace functions are disabled as they can seriously hamper performance of - *Python algorithms - * - * As an algorithm is a potentially time-consuming operation, this allows other - *threads - * to execute python code while this thread is executing C code + * Execute the algorithm * @param self :: A reference to the calling object */ -bool executeWhileReleasingGIL(IAlgorithm &self) { - bool result(false); - AllowCThreads threadStateHolder; - result = self.execute(); - return result; +bool executeProxy(object &self) { + // We need to do 2 things before we execute the algorthm: + // 1. store a reference to this algorithm mapped to the current thread ID to + // allow it to be looked up when an abort request is received + // 2. release the GIL, drop the Python threadstate and reset anything installed + // via PyEval_SetTrace while we execute the C++ code - AllowCThreads() + // does this for us + + // Extract this before dropping GIL as I'm not sure if it calls any Python + auto & calg = extract<IAlgorithm&>(self)(); + AllowCThreads threadStateHolder((!calg.isChild()) ? self : object()); + return calg.execute(); } /** * @param self A reference to the calling object * @return An AlgorithmID wrapped in a AlgorithmIDProxy container or None if - * there is - * no ID + * there is no ID */ PyObject *getAlgorithmID(IAlgorithm &self) { AlgorithmID id = self.getAlgorithmID(); @@ -258,8 +297,6 @@ std::string getWikiSummary(IAlgorithm &self) { void export_ialgorithm() { class_<AlgorithmIDProxy>("AlgorithmID", no_init).def(self == self); - // --------------------------------- IAlgorithm - // ------------------------------------------------ register_ptr_to_python<boost::shared_ptr<IAlgorithm>>(); class_<IAlgorithm, bases<IPropertyManager>, boost::noncopyable>( @@ -336,8 +373,11 @@ void export_ialgorithm() { .def("initialize", &IAlgorithm::initialize, "Initializes the algorithm") .def("validateInputs", &IAlgorithm::validateInputs, "Cross-check all inputs and return any errors as a dictionary") - .def("execute", &executeWhileReleasingGIL, + .def("execute", &executeProxy, "Runs the algorithm and returns whether it has been successful") + // 'Private' static methods + .def("_algorithmInThread", &_algorithmInThread) + .staticmethod("_algorithmInThread") // Special methods .def("__str__", &IAlgorithm::toString) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggCalibrate.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggCalibrate.py index ce24056a03b73e803f50ae561012795cd146c59b..db0259e8c7fe6a3fb3ac80cf19ba552d55ba9fba 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggCalibrate.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggCalibrate.py @@ -18,9 +18,6 @@ class EnggCalibrate(PythonAlgorithm): self.declareProperty(MatrixWorkspaceProperty("InputWorkspace", "", Direction.Input),\ "Workspace with the calibration run to use.") - self.declareProperty(MatrixWorkspaceProperty("VanadiumWorkspace", "", Direction.Input), - "Workspace with the Vanadium (correction and calibration) run.") - self.declareProperty(FloatArrayProperty("ExpectedPeaks", ""),\ "A list of dSpacing values where peaks are expected.") @@ -30,6 +27,27 @@ class EnggCalibrate(PythonAlgorithm): "find expected peaks. This takes precedence over 'ExpectedPeaks' if both " "options are given.") + self.declareProperty(MatrixWorkspaceProperty("VanadiumWorkspace", "", Direction.Input, + PropertyMode.Optional), + 'Workspace with the Vanadium (correction and calibration) run. ' + 'Alternatively, when the Vanadium run has been already processed, ' + 'the properties can be used') + + self.declareProperty(ITableWorkspaceProperty("VanIntegrationWorkspace", "", + Direction.Input, PropertyMode.Optional), + 'Results of integrating the spectra of a Vanadium run, with one column ' + '(integration result) and one row per spectrum. This can be used in ' + 'combination with OutVanadiumCurveFits from a previous execution and ' + 'VanadiumWorkspace to provide pre-calculated values for Vanadium correction.') + + self.declareProperty(MatrixWorkspaceProperty('VanCurvesWorkspace', '', Direction.Input, + PropertyMode.Optional), + doc = 'A workspace2D with the fitting workspaces corresponding to ' + 'the instrument banks. This workspace has three spectra per bank, as produced ' + 'by the algorithm Fit. This is meant to be used as an alternative input ' + 'VanadiumWorkspace for testing and performance reasons. If not given, no ' + 'workspace is generated.') + import EnggUtils self.declareProperty("Bank", '', StringListValidator(EnggUtils.ENGINX_BANKS), direction=Direction.Input, @@ -53,17 +71,10 @@ class EnggCalibrate(PythonAlgorithm): 'are added as two columns in a single row. If not given, no table is ' 'generated.') - self.declareProperty(ITableWorkspaceProperty("VanadiumIntegWorkspace", "", - Direction.Input, PropertyMode.Optional), - 'Results of integrating the spectra of a Vanadium run, with one column ' - '(integration result) and one row per spectrum. This can be used in ' - 'combination with OutVanadiumCurveFits from a previous execution and ' - 'VanadiumWorkspace to provide pre-calculated values for Vanadium correction.') - - self.declareProperty("Difc", 0.0, direction = Direction.Output,\ + self.declareProperty("Difc", 0.0, direction = Direction.Output, doc = "Calibrated Difc value for the bank or range of pixels/detectors given") - self.declareProperty("Zero", 0.0, direction = Direction.Output,\ + self.declareProperty("Zero", 0.0, direction = Direction.Output, doc = "Calibrated Zero value for the bank or range of pixels/detectors given") def PyExec(self): @@ -124,18 +135,25 @@ class EnggCalibrate(PythonAlgorithm): """ alg = self.createChildAlgorithm('EnggFocus') alg.setProperty('InputWorkspace', ws) - alg.setProperty('VanadiumWorkspace', vanWS) + alg.setProperty('Bank', bank) alg.setProperty(self.INDICES_PROP_NAME, indices) - integWS = self.getProperty('VanadiumIntegWorkspace').value - if integWS: - alg.setProperty('VanadiumIntegWorkspace', integWS) - detPos = self.getProperty('DetectorPositions').value if detPos: alg.setProperty('DetectorPositions', detPos) + if vanWS: + alg.setProperty('VanadiumWorkspace', vanWS) + + integWS = self.getProperty('VanIntegrationWorkspace').value + if integWS: + alg.setProperty('VanIntegrationWorkspace', integWS) + + curvesWS = self.getProperty('VanCurvesWorkspace').value + if curvesWS: + alg.setProperty('VanCurvesWorkspace', curvesWS) + alg.execute() return alg.getProperty('OutputWorkspace').value diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggCalibrateFull.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggCalibrateFull.py index 8f331173035ac98d0a48498bc6a64ce0d66cd7f3..12e30625cbcd963964305250b91fed9fdcf9d66d 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggCalibrateFull.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggCalibrateFull.py @@ -20,9 +20,25 @@ class EnggCalibrateFull(PythonAlgorithm): self.declareProperty(MatrixWorkspaceProperty("Workspace", "", Direction.InOut), "Workspace with the calibration run to use. The calibration will be applied on it.") - self.declareProperty(MatrixWorkspaceProperty("VanadiumWorkspace", "", Direction.Input), + self.declareProperty(MatrixWorkspaceProperty("VanadiumWorkspace", "", Direction.Input, + PropertyMode.Optional), "Workspace with the Vanadium (correction and calibration) run.") + self.declareProperty(ITableWorkspaceProperty('VanIntegrationWorkspace', '', + Direction.Input, PropertyMode.Optional), + doc = 'Results of integrating the spectra of a Vanadium run, with one column ' + '(integration result) and one row per spectrum. This can be used in ' + 'combination with OutVanadiumCurveFits from a previous execution and ' + 'VanadiumWorkspace to provide pre-calculated values for Vanadium correction.') + + self.declareProperty(MatrixWorkspaceProperty('VanCurvesWorkspace', '', Direction.Input, + PropertyMode.Optional), + doc = 'A workspace2D with the fitting workspaces corresponding to ' + 'the instrument banks. This workspace has three spectra per bank, as produced ' + 'by the algorithm Fit. This is meant to be used as an alternative input ' + 'VanadiumWorkspace for testing and performance reasons. If not given, no ' + 'workspace is generated.') + self.declareProperty(ITableWorkspaceProperty("OutDetPosTable", "", Direction.Output),\ "A table with the detector IDs and calibrated detector positions and additional " "calibration information. The table includes: the old positions in V3D format " @@ -55,13 +71,6 @@ class EnggCalibrateFull(PythonAlgorithm): "find expected peaks. This takes precedence over 'ExpectedPeaks' if both " "options are given.") - self.declareProperty('OutVanadiumCurveFits', '', direction=Direction.Input, - doc = 'Name for a workspace2D with the fitting workspaces corresponding to ' - 'the banks processed. This workspace has three spectra per bank, as produced ' - 'by the algorithm Fit. This is meant to be used as an alternative input ' - 'VanadiumWorkspace for testing and performance reasons. If not given, no ' - 'workspace is generated.') - def PyExec(self): # Get peaks in dSpacing from file, and check we have what we need, before doing anything @@ -76,9 +85,11 @@ class EnggCalibrateFull(PythonAlgorithm): self.getProperty(self.INDICES_PROP_NAME).value) vanWS = self.getProperty("VanadiumWorkspace").value + vanIntegWS = self.getProperty('VanIntegrationWorkspace').value + vanCurvesWS = self.getProperty('VanCurvesWorkspace').value # These corrections rely on ToF<->Dspacing conversions, so ideally they'd be done after the # calibration step, which creates a cycle / chicken-and-egg issue. - EnggUtils.applyVanadiumCorrection(self, inWS, vanWS) + EnggUtils.applyVanadiumCorrections(self, inWS, WSIndices, vanWS, vanIntegWS, vanCurvesWS) rebinnedWS = self._prepareWsForFitting(inWS) posTbl = self._calculateCalibPositionsTbl(rebinnedWS, WSIndices, expectedPeaksD) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggFocus.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggFocus.py index e6cdd95c84b4e3827c75870b791dcd1806067f82..f883edbfcb16925084ec771ac9d393433773c894 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggFocus.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggFocus.py @@ -1,7 +1,9 @@ -#pylint: disable=no-init,invalid-name +#pylint: disable=no-init from mantid.kernel import * from mantid.api import * +import EnggUtils + class EnggFocus(PythonAlgorithm): INDICES_PROP_NAME = 'SpectrumNumbers' @@ -15,17 +17,33 @@ class EnggFocus(PythonAlgorithm): return "Focuses a run by summing up all the spectra into a single one." def PyInit(self): - import EnggUtils - self.declareProperty(MatrixWorkspaceProperty("InputWorkspace", "", Direction.Input), "Workspace with the run to focus.") - self.declareProperty(MatrixWorkspaceProperty("VanadiumWorkspace", "", Direction.Input), - "Workspace with the Vanadium (correction and calibration) run.") - - self.declareProperty(WorkspaceProperty("OutputWorkspace", "", Direction.Output),\ + self.declareProperty(MatrixWorkspaceProperty("OutputWorkspace", "", Direction.Output), "A workspace with focussed data") + self.declareProperty(MatrixWorkspaceProperty("VanadiumWorkspace", "", Direction.Input, + PropertyMode.Optional), + doc = 'Workspace with the Vanadium (correction and calibration) run. ' + 'Alternatively, when the Vanadium run has been already processed, ' + 'the properties can be used') + + self.declareProperty(ITableWorkspaceProperty('VanIntegrationWorkspace', '', + Direction.Input, PropertyMode.Optional), + doc = 'Results of integrating the spectra of a Vanadium run, with one column ' + '(integration result) and one row per spectrum. This can be used in ' + 'combination with OutVanadiumCurveFits from a previous execution and ' + 'VanadiumWorkspace to provide pre-calculated values for Vanadium correction.') + + self.declareProperty(MatrixWorkspaceProperty('VanCurvesWorkspace', '', Direction.Input, + PropertyMode.Optional), + doc = 'A workspace2D with the fitting workspaces corresponding to ' + 'the instrument banks. This workspace has three spectra per bank, as produced ' + 'by the algorithm Fit. This is meant to be used as an alternative input ' + 'VanadiumWorkspace for testing and performance reasons. If not given, no ' + 'workspace is generated.') + self.declareProperty("Bank", '', StringListValidator(EnggUtils.ENGINX_BANKS), direction=Direction.Input, doc = "Which bank to focus: It can be specified as 1 or 2, or " @@ -38,94 +56,69 @@ class EnggFocus(PythonAlgorithm): 'ignored). This option cannot be used together with Bank, as they overlap. ' 'You can give multiple ranges, for example: "0-99", or "0-9, 50-59, 100-109".') - self.declareProperty(ITableWorkspaceProperty('DetectorPositions', '', Direction.Input,\ - PropertyMode.Optional),\ + self.declareProperty(ITableWorkspaceProperty('DetectorPositions', '', Direction.Input, + PropertyMode.Optional), "Calibrated detector positions. If not specified, default ones are used.") - self.declareProperty(ITableWorkspaceProperty("VanadiumIntegWorkspace", "", - Direction.Input, PropertyMode.Optional), - 'Results of integrating the spectra of a Vanadium run, with one column ' - '(integration result) and one row per spectrum. This can be used in ' - 'combination with OutVanadiumCurveFits from a previous execution and ' - 'VanadiumWorkspace to provide pre-calculated values for Vanadium correction.') - - self.declareProperty('OutVanadiumCurveFits', '', direction=Direction.Input, - doc = 'Name for a workspace2D with the fitting workspaces corresponding to ' - 'the banks processed. This workspace has three spectra per bank, as produced ' - 'by the algorithm Fit. This is meant to be used as an alternative input ' - 'VanadiumWorkspace for testing and performance reasons. If not given, no ' - 'workspace is generated.') - def PyExec(self): - import EnggUtils - # Get the run workspace - ws = self.getProperty('InputWorkspace').value + wks = self.getProperty('InputWorkspace').value # Get spectra indices either from bank or direct list of indices, checking for errors bank = self.getProperty('Bank').value spectra = self.getProperty(self.INDICES_PROP_NAME).value - indices = EnggUtils.getWsIndicesFromInProperties(ws, bank, spectra) + indices = EnggUtils.getWsIndicesFromInProperties(wks, bank, spectra) # Leave the data for the bank we are interested in only - ws = EnggUtils.cropData(self, ws, indices) + wks = EnggUtils.cropData(self, wks, indices) + + # Leave data for the same bank in the vanadium workspace too + vanWS = self.getProperty('VanadiumWorkspace').value + vanIntegWS = self.getProperty('VanIntegrationWorkspace').value + vanCurvesWS = self.getProperty('VanCurvesWorkspace').value + EnggUtils.applyVanadiumCorrections(self, wks, indices, vanWS, vanIntegWS, vanCurvesWS) # Apply calibration detPos = self.getProperty("DetectorPositions").value if detPos: - self._applyCalibration(ws, detPos) - - # Leave data for the same bank in the vanadium workspace too - vanWS = self.getProperty("VanadiumWorkspace").value - # if it is raw data (not precalculated curve), needs to be cropped - if EnggUtils.vanadiumWorkspaceIsPrecalculated(ws): - vanWS = EnggUtils.cropData(self, vanWS, indices) - - # These corrections rely on ToF<->Dspacing conversions, so they're done after the calibration step - vanFittingWS = EnggUtils.applyVanadiumCorrection(self, ws, vanWS, - self.getProperty('VanadiumIntegWorkspace').value) + self._applyCalibration(wks, detPos) # Convert to dSpacing - ws = EnggUtils.convertToDSpacing(self, ws) + wks = EnggUtils.convertToDSpacing(self, wks) # Sum the values - ws = EnggUtils.sumSpectra(self, ws) + wks = EnggUtils.sumSpectra(self, wks) # Convert back to time of flight - ws = EnggUtils.convertToToF(self, ws) + wks = EnggUtils.convertToToF(self, wks) # OpenGenie displays distributions instead of pure counts (this is done implicitly when # converting units), so I guess that's what users will expect - self._convertToDistr(ws) - - self.setProperty("OutputWorkspace", ws) + self._convertToDistr(wks) - # optinally, generate the workspace with per-bank vanadium fitting curves - outVanWSName = self.getPropertyValue('OutVanadiumCurveFits') - if outVanWSName: - mtd[outVanWSName] = vanFittingWS + self.setProperty("OutputWorkspace", wks) - def _applyCalibration(self, ws, detPos): + def _applyCalibration(self, wks, detPos): """ Refines the detector positions using the result of calibration (if one is specified). - @param ws :: workspace to apply the calibration (on its instrument) + @param wks :: workspace to apply the calibration (on its instrument) @param detPos :: detector positions (as a table of positions, one row per detector) """ alg = self.createChildAlgorithm('ApplyCalibration') - alg.setProperty('Workspace', ws) + alg.setProperty('Workspace', wks) alg.setProperty('PositionTable', detPos) alg.execute() - def _convertToDistr(self, ws): + def _convertToDistr(self, wks): """ Convert workspace to distribution - @param ws :: workspace, which is modified/converted in place + @param wks :: workspace, which is modified/converted in place """ alg = self.createChildAlgorithm('ConvertToDistribution') - alg.setProperty('Workspace', ws) + alg.setProperty('Workspace', wks) alg.execute() AlgorithmFactory.subscribe(EnggFocus) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggVanadiumCorrections.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggVanadiumCorrections.py new file mode 100644 index 0000000000000000000000000000000000000000..a398b8e381146ffbce9bb7459d8e946897962486 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/EnggVanadiumCorrections.py @@ -0,0 +1,426 @@ +#pylint: disable=no-init,invalid-name +from mantid.kernel import * +from mantid.api import * +import mantid.simpleapi as sapi + +import numpy as np + +import EnggUtils + +class EnggVanadiumCorrections(PythonAlgorithm): + # banks (or groups) to which the pixel-by-pixel correction should be applied + _ENGINX_BANKS_FOR_PIXBYPIX_CORR = [1,2] + + + def category(self): + return "Diffraction\\Engineering;PythonAlgorithms" + + def name(self): + return "EnggVanadiumCorrections" + + def summary(self): + return ("Calculates correction features and / or uses them to correct diffraction data " + "with respect to reference Vanadium data.") + + def PyInit(self): + self.declareProperty(MatrixWorkspaceProperty("Workspace", "", Direction.InOut, PropertyMode.Optional), + "Workspace with the diffraction data to correct. The Vanadium corrections " + "will be applied on it.") + + self.declareProperty(MatrixWorkspaceProperty("VanadiumWorkspace", "", Direction.Input, + PropertyMode.Optional), + "Workspace with the reference Vanadium diffraction data.") + + self.declareProperty(ITableWorkspaceProperty("OutIntegrationWorkspace", "", Direction.Output, + PropertyMode.Optional), + 'Output integration workspace produced when given an input Vanadium workspace') + + self.declareProperty(MatrixWorkspaceProperty("OutCurvesWorkspace", "", Direction.Output, + PropertyMode.Optional), + 'Output curves workspace produced when given an input Vanadium workspace') + + self.declareProperty(ITableWorkspaceProperty("IntegrationWorkspace", "", Direction.Input, + PropertyMode.Optional), + "Workspace with the integrated values for every spectra of the reference " + "Vanadium diffraction data. One row per spectrum.") + + self.declareProperty(MatrixWorkspaceProperty("CurvesWorkspace", "", Direction.Input, + PropertyMode.Optional), + 'Workspace with the curves fitted on bank-aggregated Vanadium diffraction ' + 'data, one per bank. This workspace has three spectra per bank, as produced ' + 'by the algorithm Fit. This is meant to be used as an alternative input ' + 'VanadiumWorkspace') + + def PyExec(self): + """ + Vanadium correction as done for the EnginX instrument. This is in principle meant to be used + in EnginXFocus and EnginXCalibrateFull. The process consists of two steps: + 1. sensitivity correction + 2. pixel-by-pixel correction + + 1. is performed for very pixel/detector/spectrum: scale every spectrum/curve by a + scale factor proportional to the number of neutrons in that spectrum + + 2. Correct every pixel by dividing by a curve (spline) fitted on the summed spectra + (summing banks separately). + + The sums and fits are done in d-spacing. + """ + ws = self.getProperty('Workspace').value + vanWS = self.getProperty('VanadiumWorkspace').value + integWS = self.getProperty('IntegrationWorkspace').value + curvesWS = self.getProperty('CurvesWorkspace').value + + # figure out if we are calculating or re-using pre-calculated corrections + if vanWS: + self.log().information("A workspace with reference Vanadium data was passed. Calculating " + "corrections") + integWS, curvesWS = self._calcVanadiumCorrection(vanWS) + self.setProperty('OutIntegrationWorkspace', integWS) + self.setProperty('OutCurvesWorkspace', curvesWS) + + elif not integWS or not curvesWS: + raise ValueError('When a VanadiumWorkspace is not passed, both the IntegrationWorkspace and ' + 'the CurvesWorkspace are required inputs. One or both of them were not given') + + if ws: + self._applyVanadiumCorrections(ws, integWS, curvesWS) + self.setProperty('Workspace', ws) + + def _applyVanadiumCorrections(self, ws, integWS, curvesWS): + """ + Applies the corrections on a workspace. The integration and curves workspaces may have + been calculated from a Vanadium run or may have been passed from a previous calculation. + + @param ws :: workspace to correct (modified in-place) + @param integWS :: table workspace with spectra integration values, one row per spectra + @param curvesWS ::workspace with "Vanadium curves" for every bank + """ + integSpectra = integWS.rowCount() + spectra = ws.getNumberHistograms() + if integSpectra < spectra: + raise ValueError("The number of histograms in the input data workspace (%d) is bigger " + "than the number of spectra (rows) in the integration workspace (%d)"% + (spectra, integSpectra)) + + self._applySensitivityCorrection(ws, integWS, curvesWS) + self._applyPixByPixCorrection(ws, curvesWS) + + def _applySensitivityCorrection(self, ws, integWS, curvesWS): + """ + Applies the first step of the Vanadium corrections on the given workspace. + Operations are done in ToF + + @param ws :: workspace (in/out) + @param vanWS :: workspace with Vanadium data + @param integWS :: pre-calculated integral of every spectrum for the Vanadium data + @param curvesWS :: pre-calculated per-bank curves from the Vanadium data + """ + for i in range(0, ws.getNumberHistograms()): + scaleFactor = integWS.cell(i,0) / curvesWS.blocksize() + ws.setY(i, np.divide(ws.dataY(i), scaleFactor)) + + def _applyPixByPixCorrection(self, ws, curvesWS): + """ + Applies the second step of the Vanadium correction on the given workspace: pixel by pixel + divides by a curve fitted to the sum of the set of spectra of the corresponding bank. + + @param ws :: workspace to work on / correct + @param curvesWS :: a workspace with the per-bank curves for Vanadium data + """ + curvesDict = self._precalcWStoDict(curvesWS) + + self._divideByCurves(ws, curvesDict) + + def _calcVanadiumCorrection(self, vanWS): + """ + Calculates the features that are required to perform vanadium corrections: integration + of the vanadium data spectra, and per-bank curves fitted to the summed spectra + + @param vanWS :: workspace with data from a Vanadium run + + @returns two workspaces: the integration and the curves. The integration workspace is a + matrix workspace as produced by the algotithm 'Integration'. The curves workspace is a + matrix workspace as produced in the outputs of the algorithm 'Fit' + """ + # Integration of every spectra, as a matrix workspace + integWS = self._calcIntegrationSpectra(vanWS) + + # Have to calculate curves. get one curve per bank, in d-spacing + curvesWS = self._fitCurvesPerBank(vanWS, self._ENGINX_BANKS_FOR_PIXBYPIX_CORR) + + return integWS, curvesWS + + def _calcIntegrationSpectra(self, vanWS): + """ + This does the real calculations behind _applySensitivityCorrection(), essentially a call to + the 'Integration' algorithm, for when we are given raw data from a Vanadium run. + + @param vanWS :: workspace with data from a Vanadium run + + @returns Integration workspace with Vanadium spectra integration values, as a table workspace + with one row per spectrum + """ + expectedDim = 'Time-of-flight' + dimType = vanWS.getXDimension().getName() + if expectedDim != dimType: + raise ValueError("This algorithm expects a workspace with %s X dimension, but " + "the X dimension of the input workspace is: '%s'" % (expectedDim, dimType)) + + integWS = self._integrateSpectra(vanWS) + if 1 != integWS.blocksize() or integWS.getNumberHistograms() < vanWS.getNumberHistograms(): + raise RuntimeError("Error while integrating vanadium workspace, the Integration algorithm " + "produced a workspace with %d bins and %d spectra. The workspace " + "being integrated has %d spectra."% + (integWS.blocksize(), integWS.getNumberHistograms(), + vanWS.getNumberHistograms())) + + integTbl = sapi.CreateEmptyTableWorkspace() + integTbl.addColumn('double', 'Spectra Integration') + for i in range(integWS.getNumberHistograms()): + integTbl.addRow([integWS.readY(i)[0]]) + + return integTbl + + def _fitCurvesPerBank(self, vanWS, banks): + """ + Fits one curve to every bank (where for every bank the data fitted is the result of + summing up all the spectra of the bank). The fitting is done in d-spacing. + + @param vanWS :: Vanadium run workspace to fit, expected in TOF units as they are archived + @param banks :: list of banks to consider which is normally all the banks of the instrument + + @returns a workspace with fitting results for all banks (3 spectra per bank). The spectra + are in dSpacing units. + """ + curves = {} + for b in banks: + indices = EnggUtils.getWsIndicesForBank(vanWS, b) + if not indices: + # no indices at all for this bank, not interested in it, don't add it to the dictionary + # (as when doing Calibrate (not-full)) which does CropData() the original workspace + continue + + wsToFit = EnggUtils.cropData(self, vanWS, indices) + wsToFit = EnggUtils.convertToDSpacing(self, wsToFit) + wsToFit = EnggUtils.sumSpectra(self, wsToFit) + + fitWS = self._fitBankCurve(wsToFit, b) + curves.update({b: fitWS}) + + curvesWS = self._prepareCurvesWS(curves) + + return curvesWS + + def _fitBankCurve(self, vanWS, bank): + """ + Fits a spline to a single-spectrum workspace (in d-spacing) + + @param vanWS :: Vanadium workspace to fit (normally this contains spectra for a single bank) + @param bank :: instrument bank this is fitting is done for + + @returns fit workspace (MatrixWorkspace), with the same number of bins as the input + workspace, and the Y values simulated from the fitted curve + """ + expectedDim = 'd-Spacing' + dimType = vanWS.getXDimension().getName() + if expectedDim != dimType: + raise ValueError("This algorithm expects a workspace with %s X dimension, but " + "the X dimension of the input workspace is: '%s'" % (expectedDim, dimType)) + + if 1 != vanWS.getNumberHistograms(): + raise ValueError("The workspace does not have exactly one histogram. Inconsistency found.") + + # without these min/max parameters 'BSpline' would completely misbehave + xvec = vanWS.readX(0) + startX = min(xvec) + endX = max(xvec) + functionDesc = 'name=BSpline, Order=3, StartX=' + str(startX) +', EndX=' + str(endX) + ', NBreak=12' + fitAlg = self.createChildAlgorithm('Fit') + fitAlg.setProperty('Function', functionDesc) + fitAlg.setProperty('InputWorkspace', vanWS) + # WorkspaceIndex is left to default '0' for 1D function fits + # StartX, EndX could in principle be left to default start/end of the spectrum, but apparently + # not safe for 'BSpline' + fitAlg.setProperty('CreateOutput', True) + fitAlg.execute() + + success = fitAlg.getProperty('OutputStatus').value + self.log().information("Fitting Vanadium curve for bank %s, using function '%s', result: %s" % + (bank, functionDesc, success)) + + detailMsg = ("It seems that this algorithm failed to to fit a function to the summed " + "spectra of a bank. The function definiton was: '%s'") % functionDesc + + outParsPropName = 'OutputParameters' + try: + fitAlg.getProperty(outParsPropName).value + except RuntimeError: + raise RuntimeError("Could not find the parameters workspace expected in the output property " + + OutParsPropName + " from the algorithm Fit. It seems that this algorithm failed." + + detailMsg) + + outWSPropName = 'OutputWorkspace' + fitWS = None + try: + fitWS = fitAlg.getProperty(outWSPropName).value + except RuntimeError: + raise RuntimeError("Could not find the data workspace expected in the output property " + + outWSPropName + ". " + detailMsg) + + mtd['engg_van_ws_dsp'] = vanWS + mtd['engg_fit_ws_dsp'] = fitWS + + return fitWS + + def _prepareCurvesWS(self, curvesDict): + """ + Simply concantenates or appends fitting output workspaces as produced by the algorithm Fit (with 3 + spectra each). The groups of 3 spectra are added sorted by the bank ID (number). This could also + produce a workspace group with the individual workspaces in it, but the AppendSpectra solution + seems simpler. + + @param curvesDict :: dictionary with fitting workspaces produced by 'Fit' + + @returns a workspace where all the input workspaces have been concatenated, with 3 spectra per + workspace / bank + """ + if 0 == len(curvesDict): + raise RuntimeError("Expecting a dictionary with fitting workspaces from 'Fit' but got an " + "empty dictionary") + if 1 == len(curvesDict): + return curvesDict.values()[0] + + keys = sorted(curvesDict) + ws = curvesDict[keys[0]] + for idx in range(1, len(keys)): + nextWS = curvesDict[keys[idx]] + ws = self._appendSpec(ws, nextWS) + + return ws + + def _divideByCurves(self, ws, curves): + """ + Expects a workspace in ToF units. All operations are done in-place (the workspace is + input/output). For every bank-curve pair, divides the corresponding spectra in the + workspace by the (simulated) fitted curve. The division is done in d-spacing (the + input workspace is converted to d-spacing inside this method, but results are converted + back to ToF before returning from this method). The curves workspace is expected in + d-spacing units (since it comes from fitting a sum of spectra for a bank or group of + detectors). + + This method is capable of dealing with workspaces with range and bin size different from + the range and bin size of the curves. It will rebin the curves workspace to match the + input 'ws' workspace (using the algorithm RebinToWorkspace). + + @param ws :: workspace with (sample) spectra to divide by curves fitted to Vanadium spectra + + @param curves :: dictionary of fitting workspaces (in d-spacing), one per bank. The keys are + the bank identifier and the values are their fitting workspaces. The fitting workspaces are + expected as returned by the algorithm 'Fit': 3 spectra: original data, simulated data with fit, + difference between original and simulated data. + """ + # Note that this division could use the algorithm 'Divide' + # This is simple and more efficient than using divide workspace, which requires + # cropping separate workspaces, dividing them separately, then appending them + # with AppendSpectra, etc. + ws = EnggUtils.convertToDSpacing(self, ws) + for b in curves: + # process all the spectra (indices) in one bank + fittedCurve = curves[b] + idxs = EnggUtils.getWsIndicesForBank(ws, b) + + if not idxs: + pass + + # This RebinToWorkspace is required here: normal runs will have narrower range of X values, + # and possibly different bin size, as compared to (long) Vanadium runs. Same applies to short + # Ceria runs (for Calibrate -non-full) and even long Ceria runs (for Calibrate-Full). + rebinnedFitCurve = self._rebinToMatchWS(fittedCurve, ws) + + for i in idxs: + # take values of the second spectrum of the workspace (fit simulation - fitted curve) + ws.setY(i, np.divide(ws.dataY(i), rebinnedFitCurve.readY(1))) + + # finally, convert back to ToF + EnggUtils.convertToToF(self, ws) + + def _precalcWStoDict(self, ws): + """ + Turn a workspace with one or more fitting results (3 spectra per curve fitted), that comes + with all the spectra appended, into a dictionary of individual fitting results. + + @param ws workspace with fitting results (3 spectra per curve fitted) as provided by Fit for + all banks. + + @returns dictionary with every individual fitting result (3 spectra) + """ + curves = {} + + if 0 != (ws.getNumberHistograms() % 3): + raise RuntimeError("A workspace without instrument definition has ben passed, so it is " + "expected to have fitting results, but it does not have a number of " + "histograms multiple of 3. Number of hsitograms found: %d"% + ws.getNumberHistograms()) + + for wi in range(0, ws.getNumberHistograms()/3): + indiv = EnggUtils.cropData(self, ws, [wi, wi+2]) + curves.update({wi: indiv}) + + return curves + + def _appendSpec(self, ws1, ws2): + """ + Uses the algorithm 'AppendSpectra' to append the spectra of ws1 and ws2 + + @param ws1 :: first workspace + @param ws2 :: second workspace + + @returns workspace with the concatenation of the spectra ws1+ws2 + """ + alg = self.createChildAlgorithm('AppendSpectra') + alg.setProperty('InputWorkspace1', ws1) + alg.setProperty('InputWorkspace2', ws2) + alg.execute() + + result = alg.getProperty('OutputWorkspace').value + return result + + def _integrateSpectra(self, ws): + """ + Integrates all the spectra or a workspace, and return the result. + Simply uses 'Integration' as a child algorithm. + + @param ws :: workspace (MatrixWorkspace) with the spectra to integrate + + @returns integrated workspace, or result of integrating every spectra in the input workspace + """ + intAlg = self.createChildAlgorithm('Integration') + intAlg.setProperty('InputWorkspace', ws) + intAlg.execute() + ws = intAlg.getProperty('OutputWorkspace').value + + return ws + + def _rebinToMatchWS(self, ws, targetWS): + """ + Rebins a workspace so that its bins match those of a 'target' workspace. This simply uses the + algorithm RebinToWorkspace as a child. In principle this method does not care about the units + of the input workspaces, as long as they are in the same units. + + @param ws :: input workspace (MatrixWorkspace) to rebin (all spectra will be rebinnded) + @param targetWS :: workspace to match against, this fixes the data range, and number and width of the + bins for the workspace returned + + @returns ws rebinned to resemble targetWS + """ + reAlg = self.createChildAlgorithm('RebinToWorkspace') + reAlg.setProperty('WorkspaceToRebin', ws) + reAlg.setProperty('WorkspaceToMatch', targetWS) + reAlg.execute() + reWS = reAlg.getProperty('OutputWorkspace').value + + return reWS + + +AlgorithmFactory.subscribe(EnggVanadiumCorrections) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadDNSLegacy.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadDNSLegacy.py index d50d9f0aa6faf2a6e24f8aae7e70776cbc17ab2b..024490af34c2d85ce9948921f22f9b5c470703ca 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadDNSLegacy.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadDNSLegacy.py @@ -12,11 +12,13 @@ from dnsdata import DNSdata sys.path.pop(0) POLARISATIONS = ['0', 'x', 'y', 'z', '-x', '-y', '-z'] +NORMALIZATIONS = ['duration', 'monitor'] class LoadDNSLegacy(PythonAlgorithm): """ - Load the DNS Legacy data file to the mantid workspace + Load the DNS Legacy data file to the matrix workspace + Monitor/duration data are loaded to the separate workspace """ def category(self): """ @@ -42,98 +44,134 @@ class LoadDNSLegacy(PythonAlgorithm): doc="Name of the workspace to store the experimental data.") self.declareProperty("Polarisation", "0", StringListValidator(POLARISATIONS), - doc="Type of polarisation. Valid values: %s" % str(POLARISATIONS)) + doc="Type of polarisation.") + self.declareProperty("Normalization", "duration", + StringListValidator(NORMALIZATIONS), + doc="Type of data for normalization.") return def PyExec(self): # Input filename = self.getPropertyValue("Filename") - outws = self.getPropertyValue("OutputWorkspace") + outws_name = self.getPropertyValue("OutputWorkspace") + monws_name = outws_name + '_NORM' pol = self.getPropertyValue("Polarisation") + norm = self.getPropertyValue("Normalization") # load data array from the given file data_array = np.loadtxt(filename) + if not data_array.size: + message = "File " + filename + " does not contain any data!" + self.log().error(message) + raise RuntimeError(message) + + # load run information + metadata = DNSdata() + try: + metadata.read_legacy(filename) + except RuntimeError as err: + message = "Error of loading of file " + filename + ": " + err + self.log().error(message) + raise RuntimeError(message) + ndet = 24 - dataX = np.zeros(ndet) + # this needed to be able to use ConvertToMD + dataX = np.zeros(2*ndet) + dataX.fill(metadata.wavelength + 0.00001) + dataX[::2] -= 0.000002 dataY = data_array[0:ndet, 1:] dataE = np.sqrt(dataY) # create workspace - __temporary_workspace__ = api.CreateWorkspace(DataX=dataX, - DataY=dataY, DataE=dataE, NSpec=ndet, UnitX="Wavelength") - api.LoadInstrument(__temporary_workspace__, InstrumentName='DNS') + api.CreateWorkspace(OutputWorkspace=outws_name, DataX=dataX, DataY=dataY, + DataE=dataE, NSpec=ndet, UnitX="Wavelength") + outws = api.mtd[outws_name] + api.LoadInstrument(outws, InstrumentName='DNS') - # load run information - metadata = DNSdata() - metadata.read_legacy(filename) - run = __temporary_workspace__.mutableRun() + run = outws.mutableRun() if metadata.start_time and metadata.end_time: run.setStartAndEndTime(DateAndTime(metadata.start_time), DateAndTime(metadata.end_time)) # add name of file as a run title fname = os.path.splitext(os.path.split(filename)[1])[0] run.addProperty('run_title', fname, True) - # run.addProperty('dur_secs', str(metadata.duration), True) # rotate the detector bank to the proper position - api.RotateInstrumentComponent(__temporary_workspace__, - "bank0", X=0, Y=1, Z=0, Angle=metadata.deterota) + api.RotateInstrumentComponent(outws, "bank0", X=0, Y=1, Z=0, Angle=metadata.deterota) # add sample log Ei and wavelength - api.AddSampleLog(__temporary_workspace__, - 'Ei', LogText=str(metadata.incident_energy), - LogType='Number') - api.AddSampleLog(__temporary_workspace__, - 'wavelength', LogText=str(metadata.wavelength), - LogType='Number') + api.AddSampleLog(outws, LogName='Ei', LogText=str(metadata.incident_energy), + LogType='Number', LogUnit='meV') + api.AddSampleLog(outws, LogName='wavelength', LogText=str(metadata.wavelength), + LogType='Number', LogUnit='Angstrom') # add other sample logs - api.AddSampleLog(__temporary_workspace__, 'deterota', - LogText=str(metadata.deterota), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'mon_sum', - LogText=str(metadata.monitor_counts), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'duration', - LogText=str(metadata.duration), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'huber', - LogText=str(metadata.huber), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'T1', - LogText=str(metadata.t1), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'T2', - LogText=str(metadata.t2), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'Tsp', - LogText=str(metadata.tsp), LogType='Number') + api.AddSampleLog(outws, LogName='deterota', LogText=str(metadata.deterota), + LogType='Number', LogUnit='Degrees') + api.AddSampleLog(outws, 'mon_sum', + LogText=str(float(metadata.monitor_counts)), LogType='Number') + api.AddSampleLog(outws, LogName='duration', LogText=str(metadata.duration), + LogType='Number', LogUnit='Seconds') + api.AddSampleLog(outws, LogName='huber', LogText=str(metadata.huber), + LogType='Number', LogUnit='Degrees') + api.AddSampleLog(outws, LogName='omega', LogText=str(metadata.huber - metadata.deterota), + LogType='Number', LogUnit='Degrees') + api.AddSampleLog(outws, LogName='T1', LogText=str(metadata.t1), + LogType='Number', LogUnit='K') + api.AddSampleLog(outws, LogName='T2', LogText=str(metadata.t2), + LogType='Number', LogUnit='K') + api.AddSampleLog(outws, LogName='Tsp', LogText=str(metadata.tsp), + LogType='Number', LogUnit='K') # flipper - api.AddSampleLog(__temporary_workspace__, 'flipper_precession', - LogText=str(metadata.flipper_precession_current), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'flipper_z_compensation', - LogText=str(metadata.flipper_z_compensation_current), LogType='Number') - flipper_status = 'OFF' + api.AddSampleLog(outws, LogName='flipper_precession', + LogText=str(metadata.flipper_precession_current), + LogType='Number', LogUnit='A') + api.AddSampleLog(outws, LogName='flipper_z_compensation', + LogText=str(metadata.flipper_z_compensation_current), + LogType='Number', LogUnit='A') + flipper_status = 'OFF' # flipper OFF if abs(metadata.flipper_precession_current) > sys.float_info.epsilon: - flipper_status = 'ON' - api.AddSampleLog(__temporary_workspace__, 'flipper', + flipper_status = 'ON' # flipper ON + api.AddSampleLog(outws, LogName='flipper', LogText=flipper_status, LogType='String') # coil currents - api.AddSampleLog(__temporary_workspace__, 'C_a', - LogText=str(metadata.a_coil_current), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'C_b', - LogText=str(metadata.b_coil_current), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'C_c', - LogText=str(metadata.c_coil_current), LogType='Number') - api.AddSampleLog(__temporary_workspace__, 'C_z', - LogText=str(metadata.z_coil_current), LogType='Number') + api.AddSampleLog(outws, LogName='C_a', LogText=str(metadata.a_coil_current), + LogType='Number', LogUnit='A') + api.AddSampleLog(outws, LogName='C_b', LogText=str(metadata.b_coil_current), + LogType='Number', LogUnit='A') + api.AddSampleLog(outws, LogName='C_c', LogText=str(metadata.c_coil_current), + LogType='Number', LogUnit='A') + api.AddSampleLog(outws, LogName='C_z', LogText=str(metadata.z_coil_current), + LogType='Number', LogUnit='A') # type of polarisation - api.AddSampleLog(__temporary_workspace__, 'polarisation', + api.AddSampleLog(outws, 'polarisation', LogText=pol, LogType='String') # slits - api.AddSampleLog(__temporary_workspace__, 'slit_i_upper_blade_position', - LogText=str(metadata.slit_i_upper_blade_position), LogType='String') - api.AddSampleLog(__temporary_workspace__, 'slit_i_lower_blade_position', - LogText=str(metadata.slit_i_lower_blade_position), LogType='String') - api.AddSampleLog(__temporary_workspace__, 'slit_i_left_blade_position', - LogText=str(metadata.slit_i_left_blade_position), LogType='String') - api.AddSampleLog(__temporary_workspace__, 'slit_i_right_blade_position', - LogText=str(metadata.slit_i_right_blade_position), LogType='String') - - self.setProperty("OutputWorkspace", __temporary_workspace__) - self.log().debug('LoadDNSLegacy: data are loaded to the workspace ' + outws) - api.DeleteWorkspace(__temporary_workspace__) + api.AddSampleLog(outws, LogName='slit_i_upper_blade_position', + LogText=str(metadata.slit_i_upper_blade_position), + LogType='Number', LogUnit='mm') + api.AddSampleLog(outws, LogName='slit_i_lower_blade_position', + LogText=str(metadata.slit_i_lower_blade_position), + LogType='Number', LogUnit='mm') + api.AddSampleLog(outws, LogName='slit_i_left_blade_position', + LogText=str(metadata.slit_i_left_blade_position), + LogType='Number', LogUnit='mm') + api.AddSampleLog(outws, 'slit_i_right_blade_position', + LogText=str(metadata.slit_i_right_blade_position), + LogType='Number', LogUnit='mm') + + # create workspace with normalization data (monitor or duration) + if norm == 'duration': + dataY.fill(metadata.duration) + dataE.fill(0.001) + else: + dataY.fill(metadata.monitor_counts) + dataE = np.sqrt(dataY) + api.CreateWorkspace(OutputWorkspace=monws_name, DataX=dataX, DataY=dataY, + DataE=dataE, NSpec=ndet, UnitX="Wavelength") + monws = api.mtd[monws_name] + api.LoadInstrument(monws, InstrumentName='DNS') + api.CopyLogs(InputWorkspace=outws_name, OutputWorkspace=monws_name, MergeStrategy='MergeReplaceExisting') + + self.setProperty("OutputWorkspace", outws) + self.log().debug('LoadDNSLegacy: data are loaded to the workspace ' + outws_name) return diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadEmptyVesuvio.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadEmptyVesuvio.py new file mode 100644 index 0000000000000000000000000000000000000000..71f89f722ec92c77768badb7f35d20b1515e35ff --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadEmptyVesuvio.py @@ -0,0 +1,109 @@ +#pylint: disable=no-init +from mantid.kernel import * +from mantid.api import * +import mantid.simpleapi as ms + +import os + +WKSP_PROP = "OutputWorkspace" +INST_PAR_PROP = "InstrumentParFile" + +# Instrument parameter headers. Key in the dictionary is the number of columns in the file +IP_HEADERS = {5:"spectrum,theta,t0,-,R", 6:"spectrum,-,theta,t0,-,R"} + +# Child Algorithm logging +_LOGGING_ = False + +#---------------------------------------------------------------------------------------- + +class LoadEmptyVesuvio(PythonAlgorithm): + + def summary(self): + return "Loads an empty workspace containing the Vesuvio instrument at ISIS." + +#---------------------------------------------------------------------------------------- + + def PyInit(self): + self.declareProperty(FileProperty(INST_PAR_PROP, "", action=FileAction.OptionalLoad, + extensions=["dat"]), + 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(WorkspaceProperty(WKSP_PROP, "", Direction.Output), + doc="The name of the output workspace.") + +#---------------------------------------------------------------------------------------- + + def PyExec(self): + vesuvio_ws = self._load_empty_evs() + + # Load the IP file if it is provided + ip_filename = self.getPropertyValue(INST_PAR_PROP) + if ip_filename != "": + vesuvio_ws = self._load_ip_file(vesuvio_ws, ip_filename) + + self.setProperty(WKSP_PROP, vesuvio_ws) + +#---------------------------------------------------------------------------------------- + + def _load_empty_evs(self): + """ + Load en empty VESUVIO workspace with default IDF detector psotiions. + @return VESUVIO workspace + """ + # Get IDF + inst_dir = config.getInstrumentDirectory() + inst_file = os.path.join(inst_dir, "VESUVIO_Definition.xml") + + # Load the instrument + vesuvio_ws = ms.LoadEmptyInstrument(Filename=inst_file, + OutputWorkspace=self.getPropertyValue(WKSP_PROP), + EnableLogging=_LOGGING_) + + return vesuvio_ws + +#---------------------------------------------------------------------------------------- + + def _load_ip_file(self, workspace, 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 + @return Updated workspace + """ + ip_header = self._get_header_format(ip_file) + + # More verbose until the child algorithm stuff is sorted + update_inst = self.createChildAlgorithm("UpdateInstrumentFromFile") + update_inst.setLogging(_LOGGING_) + update_inst.setProperty("Workspace", workspace) + update_inst.setProperty("Filename", ip_file) + update_inst.setProperty("MoveMonitors", False) + update_inst.setProperty("IgnorePhi", True) + update_inst.setProperty("AsciiHeader", ip_header) + update_inst.execute() + + return update_inst.getProperty("Workspace").value + +#---------------------------------------------------------------------------------------- + + def _get_header_format(self, ip_filename): + """ + Returns the header format to be used for the given IP file. + Currently supports 5/6 column files. + Raises ValueError if anything other than a 5/6 column file is found. + @filename ip_filename :: Full path to the IP file. + @return The header format string for use with UpdateInstrumentFromFile + """ + ipfile = open(ip_filename, "r") + first_line = ipfile.readline() + columns = first_line.split() # splits on whitespace characters + try: + return IP_HEADERS[len(columns)] + except KeyError: + raise ValueError("Unknown format for IP file. Currently support 5/6 column " + "variants. ncols=%d" % (len(columns))) + +#---------------------------------------------------------------------------------------- + +AlgorithmFactory.subscribe(LoadEmptyVesuvio) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadVesuvio.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadVesuvio.py index cf465f8fa9418900a94baac4225647f9e44364f6..e587a1bfdef0c883ffd6d29d8e7955c34d6b0dfe 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadVesuvio.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/LoadVesuvio.py @@ -5,9 +5,10 @@ from mantid.kernel import * from mantid.api import * import mantid.simpleapi as ms +from LoadEmptyVesuvio import LoadEmptyVesuvio + import copy import numpy as np -import os RUN_PROP = "Filename" WKSP_PROP = "OutputWorkspace" @@ -27,14 +28,11 @@ ITHICK = 2 BACKWARD = 0 FORWARD = 1 -# Instrument parameter headers. Key in the dictionary is the number of columns in the file -IP_HEADERS = {5:"spectrum,theta,t0,-,R", 6:"spectrum,-,theta,t0,-,R"} - # Child Algorithm logging _LOGGING_ = False #pylint: disable=too-many-instance-attributes -class LoadVesuvio(PythonAlgorithm): +class LoadVesuvio(LoadEmptyVesuvio): _ws_index = None _spectrum_no = None @@ -88,9 +86,13 @@ class LoadVesuvio(PythonAlgorithm): mon_thick = None foil_out = None +#---------------------------------------------------------------------------------------- + def summary(self): return "Loads raw data produced by the Vesuvio instrument at ISIS." +#---------------------------------------------------------------------------------------- + def PyInit(self): self.declareProperty(RUN_PROP, "", StringMandatoryValidator(), doc="The run numbers that should be loaded. E.g." @@ -105,8 +107,8 @@ class LoadVesuvio(PythonAlgorithm): self.declareProperty(MODE_PROP, "DoubleDifference", StringListValidator(MODES), doc="The difference option. Valid values: %s" % str(MODES)) - self.declareProperty(FileProperty(INST_PAR_PROP,"",action=FileAction.OptionalLoad, - extensions=["dat"]), + self.declareProperty(FileProperty(INST_PAR_PROP, "", action=FileAction.OptionalLoad, + extensions=["dat", "par"]), 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") @@ -155,7 +157,7 @@ class LoadVesuvio(PythonAlgorithm): ip_file = self.getPropertyValue(INST_PAR_PROP) if len(ip_file) > 0: - self._load_ip_file(ip_file) + self.foil_out = self._load_ip_file(self.foil_out, ip_file) if self._sumspectra: self._sum_all_spectra() @@ -219,7 +221,7 @@ class LoadVesuvio(PythonAlgorithm): ip_file = self.getPropertyValue(INST_PAR_PROP) if len(ip_file) > 0: - self._load_ip_file(ip_file) + self.foil_out = self._load_ip_file(self.foil_out, ip_file) if self._sumspectra: self._sum_all_spectra() @@ -235,11 +237,8 @@ class LoadVesuvio(PythonAlgorithm): parameters as attributes """ isis = config.getFacility("ISIS") - inst_name = "VESUVIO" - inst_dir = config.getInstrumentDirectory() - inst_file = os.path.join(inst_dir, inst_name + "_Definition.xml") - __empty_vesuvio_ws = ms.LoadEmptyInstrument(Filename=inst_file, EnableLogging=_LOGGING_) - empty_vesuvio = __empty_vesuvio_ws.getInstrument() + empty_vesuvio_ws = self._load_empty_evs() + empty_vesuvio = empty_vesuvio_ws.getInstrument() def to_int_list(str_param): """Return the list of numbers described by the string range""" @@ -247,7 +246,7 @@ class LoadVesuvio(PythonAlgorithm): return range(int(elements[0]),int(elements[1]) + 1) # range goes x_l,x_h-1 # Attach parameters as attributes - self._inst_prefix = isis.instrument(inst_name).shortName() + self._inst_prefix = isis.instrument("VESUVIO").shortName() parnames = empty_vesuvio.getParameterNames(False) for name in parnames: # Irritating parameter access doesn't let you query the type @@ -284,8 +283,6 @@ class LoadVesuvio(PythonAlgorithm): self._forw_period_sum2 = to_range_tuple(self.forward_period_sum2) self._forw_foil_out_norm = to_range_tuple(self.forward_foil_out_norm) - ms.DeleteWorkspace(__empty_vesuvio_ws,EnableLogging=_LOGGING_) - #---------------------------------------------------------------------------------------- def _retrieve_input(self): @@ -716,31 +713,6 @@ class LoadVesuvio(PythonAlgorithm): ethick = self.foil_thick.readE(ws_index) np.sqrt((eout**2 + ethick**2), eout) # The second argument makes it happen in place -#---------------------------------------------------------------------------------------- - - 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 - """ - if ip_file == "": - raise ValueError("Empty filename string for IP file") - - ip_header = self._get_header_format(ip_file) - - # More verbose until the child algorithm stuff is sorted - update_inst = self.createChildAlgorithm("UpdateInstrumentFromFile") - update_inst.setLogging(_LOGGING_) - update_inst.setProperty("Workspace", self.foil_out) - update_inst.setProperty("Filename", ip_file) - update_inst.setProperty("MoveMonitors", False) - update_inst.setProperty("IgnorePhi", True) - update_inst.setProperty("AsciiHeader", ip_header) - update_inst.execute() - - self.foil_out = update_inst.getProperty("Workspace").value - #---------------------------------------------------------------------------------------- def _sum_all_spectra(self): @@ -772,26 +744,6 @@ class LoadVesuvio(PythonAlgorithm): #endfor self.foil_out = ws_out -#---------------------------------------------------------------------------------------- - - def _get_header_format(self, ip_filename): - """ - Returns the header format to be used for the given - IP file. Currently supports 5/6 column files. - Raises ValueError if anything other than a 5/6 column - file is found. - @filename ip_filename :: Full path to the IP file. - @returns The header format string for use with UpdateInstrumentFromFile - """ - ipfile = open(ip_filename, "r") - first_line = ipfile.readline() - columns = first_line.split() # splits on whitespace characters - try: - return IP_HEADERS[len(columns)] - except KeyError: - raise ValueError("Unknown format for IP file. Currently support 5/6 column " - "variants. ncols=%d" % (len(columns))) - #---------------------------------------------------------------------------------------- def _store_results(self): diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/PDDetermineCharacterizations.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/PDDetermineCharacterizations.py deleted file mode 100644 index 911d57adf1fe81cf0f79a6499d74351c88fcd6c0..0000000000000000000000000000000000000000 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/PDDetermineCharacterizations.py +++ /dev/null @@ -1,267 +0,0 @@ -#pylint: disable=no-init -from mantid.api import * -from mantid.kernel import * - -# this should match those in LoadPDCharacterizations -COL_NAMES = [ - "frequency", # double - "wavelength", # double - "bank", # integer - "vanadium", # integer - "container", # integer - "empty", # integer - "d_min", # string - "d_max", # string - "tof_min", # double - "tof_max" # double - ] -DEF_INFO = { - "frequency":0., - "wavelength":0., - "bank":1, - "vanadium":0, - "container":0, - "empty":0, - "d_min":"", - "d_max":"", - "tof_min":0., - "tof_max":0. - } - -class PDDetermineCharacterizations(PythonAlgorithm): - def category(self): - return "Workflow\\Diffraction\\UsesPropertyManager" - - def name(self): - return "PDDetermineCharacterizations" - - def summary(self): - return "Determines the characterizations of a workspace." - - def PyInit(self): - # input parameters - self.declareProperty(WorkspaceProperty("InputWorkspace", "", - Direction.Input), - "Workspace with logs to help identify frequency and wavelength") - - self.declareProperty(ITableWorkspaceProperty("Characterizations", "", - Direction.Input), - "Table of characterization information") - - self.declareProperty("ReductionProperties", - "__pd_reduction_properties", - validator=StringMandatoryValidator(), - doc="Property manager name for the reduction") - - defaultMsg = " run to use. 0 to use value in table, -1 to not use." - self.declareProperty("BackRun", 0, - doc="The background" + defaultMsg) - self.declareProperty("NormRun", 0, - doc="The background" + defaultMsg) - self.declareProperty("NormBackRun", 0, - doc="The background" + defaultMsg) - - self.declareProperty(StringArrayProperty("FrequencyLogNames", ["SpeedRequest1", "Speed1", "frequency"],\ - direction=Direction.Input),\ - "Possible log names for frequency.") - - self.declareProperty(StringArrayProperty("WaveLengthLogNames", ["LambdaRequest", "lambda"],\ - direction=Direction.Input),\ - "Candidate log names for wave length.") - - return - - def validateInputs(self): - # correct workspace type - char = self.getProperty("Characterizations").value - if char.id() != "TableWorkspace": - msg = "Characterizations should be a TableWorkspace" - return {"Characterizations":msg} - - # correct number of columns - colNames = char.getColumnNames() - if len(colNames) != len(COL_NAMES): - msg = "Encountered invalid number of columns in " \ - + "Characterizations. Found %d, expected 10" % len(a) - return {"Characterizations":msg} - - # correct column names - for (left, right) in zip(colNames, COL_NAMES): - if left != right: - msg = "Encountered column \"%s\" when expected \"%s\"" \ - % (left, right) - return {"Characterizations":msg} - - # everything is fine - return {} - - def PyExec(self): - # setup property manager to return - manager_name = self.getProperty("ReductionProperties").value - if PropertyManagerDataService.doesExist(manager_name): - manager = PropertyManagerDataService.retrieve(manager_name) - else: - manager = PropertyManager() - - # empty characterizations table means return the default values - char = self.getProperty("Characterizations").value - if char.rowCount() <= 0: - for key in COL_NAMES: - if not manager.existsProperty(key): - manager[key] = DEF_INFO[key] - PropertyManagerDataService.addOrReplace(manager_name, manager) - return - wksp = self.getProperty("InputWorkspace").value - - # determine wavelength and frequency - frequency = self.getFrequency(wksp.getRun(), str(wksp)) - wavelength = self.getWavelength(wksp.getRun(), str(wksp)) - self.log().information("Determined frequency: " + str(frequency) \ - + " Hz, center wavelength:" \ - + str(wavelength) + " Angstrom") - - # get a row of the table - info = self.getLine(char, frequency, wavelength) - - # update the characterization runs as necessary - propNames = ("BackRun", "NormRun", "NormBackRun") - dictNames = ("container", "vanadium", "empty") - for (propName, dictName) in zip(propNames, dictNames): - runNum = self.getProperty(propName).value - if runNum < 0: # reset value - info[dictName] = 0 - elif runNum > 0: # override value - info[dictName] = runNum - - # convert to a property manager - self.processInformation(manager, info) - PropertyManagerDataService.addOrReplace(manager_name, manager) - - def processInformation(self, prop_man, info_dict): - for key in COL_NAMES: - val = info_dict[key] - # Convert comma-delimited list to array, else return the original - # value. - if type("") == type(val): - if (len(val)==0) and (key in DEF_INFO.keys()): - val = DEF_INFO[key] - else: - try: - val = [float(x) for x in val.split(',')] - except ValueError, err: - self.log().error("Error to parse key: '%s' value = '%s'. " % (str(key), str(val))) - raise NotImplementedError(str(err)) - - try: - prop_man[key] = val - except TypeError: - # Converter error, so remove old value first - del prop_man[key] - prop_man[key] = val - - def closeEnough(self, left, right): - left = float(left) - right = float(right) - if abs(left-right) == 0.: - return True - if 100. * abs(left-right)/left < 5.: - return True - return False - - def getLine(self, char, frequency, wavelength): - """ Get line in the characterization file with given frequency and wavelength - """ - # empty dictionary if things are wrong - if frequency is None or wavelength is None: - return dict(DEF_INFO) - - # go through every row looking for a match - result = dict(DEF_INFO) - icount = 0 - for i in xrange(char.rowCount()): - row = char.row(i) - if not self.closeEnough(frequency, row['frequency']): - continue - if not self.closeEnough(wavelength, row['wavelength']): - continue - result = dict(row) - icount += 1 - - self.log().information("Total %d rows are parsed for frequency = %f, wavelength = %f" % (icount, frequency, wavelength)) - return result - - def getFrequency(self, logs, wkspName): - """ Get frequency from log - """ - frequencynames = self.getProperty("FrequencyLogNames").value - self.log().notice("Type of frequency names is %s" % (str(type(frequencynames)))) - for name in frequencynames: - if name in logs.keys(): - frequency = logs[name] - if frequency.units != "Hz": - msg = "When looking at " + name \ - + " log encountered unknown units for frequency. " \ - + "Only know how to deal with " \ - + "frequency in Hz, not " + frequency.units - self.log().information(msg) - else: - frequency = frequency.getStatistics().mean - if frequency == 0.: - self.log().information("'%s' mean value is zero" % name) - else: - self.log().information("Found frequency in %s log" \ - % name) - return frequency - self.log().warning("Failed to determine frequency in \"%s\"" \ - % wkspName) - return None - - def getWavelength(self, logs, dummy_wkspName): - """ Get wave length - Wavelength can be given by 2 sample logs, either LambdaRequest or lambda. - And its unit can be either Angstrom or A. - """ - wavelengthnames = self.getProperty("WaveLengthLogNames").value - - for name in wavelengthnames: - # skip if not exists - if name not in logs.keys(): - continue - - # get value - wavelength = logs[name] - - # unit - if name == "LambdaRequest": - if wavelength.units != "Angstrom": - msg = "Only know how to deal with LambdaRequest in Angstrom, not %s" % (wavelength.units) - self.log().warning(msg) - break - - elif name == "lambda": - if wavelength.units != "A": - msg = "Only know how to deal with lambda in A, not %s" % (wavelength.units) - self.log().warning(msg) - break - - else: - if wavelength.units != "Angstrom" and wavelength.units != "A": - msg = "Only know how to deal with %s in Angstrom (A) but not %s" % (name,\ - wavelength.units) - self.log().warning(msg) - break - - # return - wavelength = wavelength.getStatistics().mean - if wavelength == 0.: - self.log().warning("'%s' mean value is zero" % name) - break - else: - return wavelength - - # ENDFOR - - return None - -# Register algorthm with Mantid. -AlgorithmFactory.subscribe(PDDetermineCharacterizations) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SNSPowderReduction.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SNSPowderReduction.py index a591cef0cb129bcded78e09773ed18951297efef..036305f969b6dd6da64e1ebe4d5c8ee6bfbae825 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SNSPowderReduction.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/SNSPowderReduction.py @@ -260,14 +260,14 @@ class SNSPowderReduction(DataProcessorAlgorithm): samRun = temp info = tempinfo else: - if (tempinfo["frequency"] is not None) and (info["frequency"] is not None) \ - and (abs(tempinfo["frequency"] - info["frequency"])/info["frequency"] > .05): + if (tempinfo["frequency"].value is not None) and (info["frequency"].value is not None) \ + and (abs(tempinfo["frequency"].value - info["frequency"].value)/info["frequency"].value > .05): raise RuntimeError("Cannot add incompatible frequencies (%f!=%f)" \ - % (tempinfo["frequency"], info["frequency"])) - if (tempinfo["wavelength"] is not None) and (info["wavelength"] is not None) \ - and abs(tempinfo["wavelength"] - info["wavelength"])/info["wavelength"] > .05: + % (tempinfo["frequency"].value, info["frequency"].value)) + if (tempinfo["wavelength"].value is not None) and (info["wavelength"].value is not None) \ + and abs(tempinfo["wavelength"].value - info["wavelength"].value)/info["wavelength"].value > .05: raise RuntimeError("Cannot add incompatible wavelengths (%f != %f)" \ - % (tempinfo["wavelength"], info["wavelength"])) + % (tempinfo["wavelength"].value, info["wavelength"].value)) samRun = api.Plus(LHSWorkspace=samRun, RHSWorkspace=temp, OutputWorkspace=samRun) if samRun.id() == EVENT_WORKSPACE_ID: samRun = api.CompressEvents(InputWorkspace=samRun, OutputWorkspace=samRun,\ @@ -329,7 +329,7 @@ class SNSPowderReduction(DataProcessorAlgorithm): self._info = self._getinfo(samRun) # process the container - canRun = self._info["container"] + canRun = self._info["container"].value if canRun > 0: if self.getProperty("FilterCharacterizations").value: canFilterWall = timeFilterWall @@ -352,7 +352,7 @@ class SNSPowderReduction(DataProcessorAlgorithm): canRun = None # process the vanadium run - vanRun = self._info["vanadium"] + vanRun = self._info["vanadium"].value if vanRun > 0: if self.getProperty("FilterCharacterizations").value: vanFilterWall = timeFilterWall @@ -376,7 +376,7 @@ class SNSPowderReduction(DataProcessorAlgorithm): # load the vanadium background (if appropriate) - vbackRun = self._info["empty"] + vbackRun = self._info["empty"].value if vbackRun > 0: vbackRun = self._loadData(vbackRun, SUFFIX, vanFilterWall, outname="vbackRun") try: @@ -406,14 +406,13 @@ class SNSPowderReduction(DataProcessorAlgorithm): # focus the data vanRun = api.AlignAndFocusPowder(InputWorkspace=vanRun, OutputWorkspace=vanRun, CalFileName=calib, Params=self._binning, ResampleX=self._resampleX, Dspacing=self._bin_in_dspace, - DMin=self._info["d_min"], DMax=self._info["d_max"], - TMin=self._info["tof_min"], TMax=self._info["tof_max"], RemovePromptPulseWidth=self._removePromptPulseWidth, CompressTolerance=self.COMPRESS_TOL_TOF, UnwrapRef=self._LRef, LowResRef=self._DIFCref, LowResSpectrumOffset=self._lowResTOFoffset, CropWavelengthMin=self._wavelengthMin, - CropWavelengthMax=self._wavelengthMax, **(focuspos)) + CropWavelengthMax=self._wavelengthMax, + ReductionProperties="__snspowderreduction", **(focuspos)) # strip peaks @@ -710,11 +709,11 @@ class SNSPowderReduction(DataProcessorAlgorithm): focuspos = self._focusPos temp = api.AlignAndFocusPowder(InputWorkspace=temp, OutputWorkspace=temp, CalFileName=calib,\ Params=self._binning, ResampleX=self._resampleX, Dspacing=self._bin_in_dspace,\ - DMin=self._info["d_min"], DMax=self._info["d_max"], TMin=self._info["tof_min"], TMax=self._info["tof_max"],\ PreserveEvents=preserveEvents,\ RemovePromptPulseWidth=self._removePromptPulseWidth, CompressTolerance=self.COMPRESS_TOL_TOF,\ UnwrapRef=self._LRef, LowResRef=self._DIFCref, LowResSpectrumOffset=self._lowResTOFoffset,\ - CropWavelengthMin=self._wavelengthMin, CropWavelengthMax=self._wavelengthMax, **(focuspos)) + CropWavelengthMin=self._wavelengthMin, CropWavelengthMax=self._wavelengthMax, + ReductionProperties="__snspowderreduction", **(focuspos)) for iws in xrange(temp.getNumberHistograms()): spec = temp.getSpectrum(iws) self.log().debug("[DBx131] ws %d: spectrum ID = %d. " % (iws, spec.getSpectrumNo())) @@ -785,8 +784,6 @@ class SNSPowderReduction(DataProcessorAlgorithm): return wksplist def _getinfo(self, wksp): - rowValues = {} - if mtd.doesExist("characterizations"): # get the correct row of the table @@ -799,23 +796,17 @@ class SNSPowderReduction(DataProcessorAlgorithm): NormBackRun=self.getProperty("VanadiumBackgroundNumber").value, FrequencyLogNames = self.getProperty("FrequencyLogNames").value, WaveLengthLogNames = self.getProperty("WaveLengthLogNames").value) - # convert the result into a dict - manager = PropertyManagerDataService.retrieve("__snspowderreduction") - for name in ["frequency", "wavelength", "bank", "vanadium", "container", - "empty", "d_min", "d_max", "tof_min", "tof_max"]: - rowValues[name] = manager.getProperty(name).value else: - # "frequency", "wavelength" - rowValues["bank"] = 0 - rowValues["container"] = self.getProperty("BackgroundNumber").value - rowValues["vanadium"] = self.getProperty("VanadiumNumber").value - rowValues["empty"] = self.getProperty("VanadiumBackgroundNumber").value - rowValues["d_min"] = 0. - rowValues["d_max"] = 0. - rowValues["tof_min"] = 0. - rowValues["tof_max"] = 0. - - return rowValues + charac = api.PDDetermineCharacterizations(InputWorkspace=wksp, + ReductionProperties="__snspowderreduction", + BackRun=self.getProperty("BackgroundNumber").value, + NormRun=self.getProperty("VanadiumNumber").value, + NormBackRun=self.getProperty("VanadiumBackgroundNumber").value, + FrequencyLogNames = self.getProperty("FrequencyLogNames").value, + WaveLengthLogNames = self.getProperty("WaveLengthLogNames").value) + + # convert the result into a dict + return PropertyManagerDataService.retrieve("__snspowderreduction") def _save(self, wksp, info, normalized, pdfgetn): prefix = str(wksp) @@ -827,16 +818,16 @@ class SNSPowderReduction(DataProcessorAlgorithm): pdfwksp = str(wksp)+"_norm" pdfwksp = api.SetUncertainties(InputWorkspace=wksp, OutputWorkspace=pdfwksp, SetError="sqrt") api.SaveGSS(InputWorkspace=pdfwksp, Filename=filename+".getn", SplitFiles=False, Append=False,\ - MultiplyByBinWidth=False, Bank=info["bank"], Format="SLOG", ExtendedHeader=True) + MultiplyByBinWidth=False, Bank=info["bank"].value, Format="SLOG", ExtendedHeader=True) api.DeleteWorkspace(pdfwksp) return # don't do the other bits of saving if "gsas" in self._outTypes: api.SaveGSS(InputWorkspace=wksp, Filename=filename+".gsa", SplitFiles=False, Append=False,\ - MultiplyByBinWidth=normalized, Bank=info["bank"], Format="SLOG", ExtendedHeader=True) + MultiplyByBinWidth=normalized, Bank=info["bank"].value, Format="SLOG", ExtendedHeader=True) if "fullprof" in self._outTypes: - api.SaveFocusedXYE(InputWorkspace=wksp, StartAtBankNumber=info["bank"], Filename=filename+".dat") + api.SaveFocusedXYE(InputWorkspace=wksp, StartAtBankNumber=info["bank"].value, Filename=filename+".dat") if "topas" in self._outTypes: - api.SaveFocusedXYE(InputWorkspace=wksp, StartAtBankNumber=info["bank"], Filename=filename+".xye", + api.SaveFocusedXYE(InputWorkspace=wksp, StartAtBankNumber=info["bank"].value, Filename=filename+".xye", Format="TOPAS") if "nexus" in self._outTypes: api.ConvertUnits(InputWorkspace=wksp, OutputWorkspace=wksp, Target=self.getProperty("FinalDataUnits").value) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ISISIndirectEnergyTransfer.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ISISIndirectEnergyTransfer.py index d3ea3fb88063236b71f1958991a3047c97ee53fa..4b3a005b7c4f9beaceb239f4ae6c20c0ce741957 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ISISIndirectEnergyTransfer.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ISISIndirectEnergyTransfer.py @@ -1,4 +1,4 @@ -#pylint: disable=invalid-name,attribute-defined-outside-init,too-many-instance-attributes,too-many-branches,no-init,deprecated-module +#pylint: disable=invalid-name,too-many-instance-attributes,too-many-branches,no-init,deprecated-module from mantid.kernel import * from mantid.api import * from mantid.simpleapi import * @@ -14,6 +14,32 @@ _elems_or_none = lambda l: l if len(l) != 0 else None class ISISIndirectEnergyTransfer(DataProcessorAlgorithm): + _chopped_data = None + _data_files = None + _load_logs = None + _calibration_ws = None + _instrument_name = None + _analyser = None + _reflection = None + _efixed = None + _spectra_range = None + _background_range = None + _rebin_string = None + _detailed_balance = None + _scale_factor = None + _fold_multiple_frames = None + _grouping_method = None + _grouping_ws = None + _grouping_map_file = None + _output_x_units = None + _plot_type = None + _save_formats = None + _output_ws = None + _sum_files = None + _ipf_filename = None + _workspace_names = None + + def category(self): return 'Workflow\\Inelastic;PythonAlgorithms;Inelastic' @@ -39,22 +65,31 @@ class ISISIndirectEnergyTransfer(DataProcessorAlgorithm): doc='Workspace contining calibration data') # Instrument configuration properties - self.declareProperty(name='Instrument', defaultValue='', doc='Instrument used during run.', - validator=StringListValidator(['IRIS', 'OSIRIS', 'TOSCA', 'TFXA'])) - self.declareProperty(name='Analyser', defaultValue='', doc='Analyser bank used during run.', - validator=StringListValidator(['graphite', 'mica', 'fmica'])) - self.declareProperty(name='Reflection', defaultValue='', doc='Reflection number for instrument setup during run.', - validator=StringListValidator(['002', '004', '006'])) + self.declareProperty(name='Instrument', defaultValue='', + validator=StringListValidator(['IRIS', 'OSIRIS', 'TOSCA', 'TFXA']), + doc='Instrument used during run.') + self.declareProperty(name='Analyser', defaultValue='', + validator=StringListValidator(['graphite', 'mica', 'fmica']), + doc='Analyser bank used during run.') + self.declareProperty(name='Reflection', defaultValue='', + validator=StringListValidator(['002', '004', '006']), + doc='Reflection number for instrument setup during run.') + + self.declareProperty(name='Efixed', defaultValue=Property.EMPTY_DBL, + validator=FloatBoundedValidator(0.0), + doc='Overrides the default Efixed value for the analyser/reflection selection.') self.declareProperty(IntArrayProperty(name='SpectraRange', values=[0, 1], validator=IntArrayMandatoryValidator()), doc='Comma separated range of spectra number to use.') self.declareProperty(FloatArrayProperty(name='BackgroundRange'), doc='Range of background to subtact from raw data in time of flight.') - self.declareProperty(name='RebinString', defaultValue='', doc='Rebin string parameters.') + self.declareProperty(name='RebinString', defaultValue='', + doc='Rebin string parameters.') self.declareProperty(name='DetailedBalance', defaultValue=Property.EMPTY_DBL, doc='') - self.declareProperty(name='ScaleFactor', defaultValue=1.0, doc='Factor by which to scale result.') + self.declareProperty(name='ScaleFactor', defaultValue=1.0, + doc='Factor by which to scale result.') self.declareProperty(name='FoldMultipleFrames', defaultValue=True, doc='Folds multiple framed data sets into a single workspace.') @@ -66,15 +101,20 @@ class ISISIndirectEnergyTransfer(DataProcessorAlgorithm): direction=Direction.Input, optional=PropertyMode.Optional), doc='Workspace containing spectra grouping.') - self.declareProperty(FileProperty('MapFile', '', action=FileAction.OptionalLoad, extensions=['.map']), + self.declareProperty(FileProperty('MapFile', '', + action=FileAction.OptionalLoad, + extensions=['.map']), doc='Workspace containing spectra grouping.') # Output properties - self.declareProperty(name='UnitX', defaultValue='DeltaE', doc='X axis units for the result workspace.', - validator=StringListValidator(['DeltaE', 'DeltaE_inWavenumber'])) - self.declareProperty(StringArrayProperty(name='SaveFormats'), doc='Comma seperated list of save formats') - self.declareProperty(name='Plot', defaultValue='None', doc='Type of plot to output after reduction.', - validator=StringListValidator(['None', 'Spectra', 'Contour', 'Both'])) + self.declareProperty(name='UnitX', defaultValue='DeltaE', + validator=StringListValidator(['DeltaE', 'DeltaE_inWavenumber']), + doc='X axis units for the result workspace.') + self.declareProperty(StringArrayProperty(name='SaveFormats'), + doc='Comma seperated list of save formats') + self.declareProperty(name='Plot', defaultValue='None', + validator=StringListValidator(['None', 'Spectra', 'Contour', 'Both']), + doc='Type of plot to output after reduction.') self.declareProperty(WorkspaceGroupProperty('OutputWorkspace', '', direction=Direction.Output), @@ -99,11 +139,11 @@ class ISISIndirectEnergyTransfer(DataProcessorAlgorithm): self._setup() self._workspace_names, self._chopped_data = load_files(self._data_files, - self._ipf_filename, - self._spectra_range[0], - self._spectra_range[1], - self._sum_files, - self._load_logs) + self._ipf_filename, + self._spectra_range[0], + self._spectra_range[1], + self._sum_files, + self._load_logs) for c_ws_name in self._workspace_names: is_multi_frame = isinstance(mtd[c_ws_name], WorkspaceGroup) @@ -122,6 +162,14 @@ class ISISIndirectEnergyTransfer(DataProcessorAlgorithm): # Process workspaces for ws_name in workspaces: + # Set Efixed if given to algorithm + if self._efixed != Property.EMPTY_DBL: + SetInstrumentParameter(Workspace=ws_name, + ComponentName=self._analyser, + ParameterName='Efixed', + ParameterType='Number', + Value=str(self._efixed)) + monitor_ws_name = ws_name + '_mon' # Process monitor @@ -283,6 +331,10 @@ class ISISIndirectEnergyTransfer(DataProcessorAlgorithm): issues['SaveFormats'] = '%s is not a valid save format' % format_name break + efixed = self.getProperty('Efixed').value + if efixed != Property.EMPTY_DBL and instrument_name not in ['IRIS', 'OSIRIS']: + issues['Efixed'] = 'Can only override Efixed on IRIS and OSIRIS' + return issues @@ -300,6 +352,7 @@ class ISISIndirectEnergyTransfer(DataProcessorAlgorithm): self._instrument_name = self.getPropertyValue('Instrument') self._analyser = self.getPropertyValue('Analyser') self._reflection = self.getPropertyValue('Reflection') + self._efixed = self.getProperty('Efixed').value self._spectra_range = self.getProperty('SpectraRange').value self._background_range = _elems_or_none(self.getProperty('BackgroundRange').value) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/MolDyn.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/MolDyn.py index fefcac653037759fedc1aeaa9a130c72d7d2b9e0..8b6db2241b2d7e72f0ceda0a3bc106590064cc67 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/MolDyn.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/MolDyn.py @@ -139,21 +139,16 @@ class MolDyn(PythonAlgorithm): # Do setup self._setup() - try: - # Load data file - data, name, ext = self._load_file() - - # Run nMOLDYN import - if ext == 'cdl': - self._cdl_import(data, name) - elif ext == 'dat': - self._ascii_import(data, name) - else: - raise RuntimeError('Unrecognised file format: %s' % ext) - - except Exception as ex: - logger.error('Error parsing file %s' % (self._sam_path)) - logger.debug('Error is: %s' % (str(ex))) + # Load data file + data, name, ext = self._load_file() + + # Run nMOLDYN import + if ext == 'cdl': + self._cdl_import(data, name) + elif ext == 'dat': + self._ascii_import(data, name) + else: + raise RuntimeError('Unrecognised file format: %s' % ext) # Do processing specific to workspaces in energy if isinstance(mtd[self._out_ws], WorkspaceGroup): @@ -270,18 +265,14 @@ class MolDyn(PythonAlgorithm): logger.information('Got file path for %s: %s' % (self._sam_path, path)) # Open file and get data - try: - handle = open(path, 'r') - data = [] - for line in handle: - line = line.rstrip() - data.append(line) - handle.close() + handle = open(path, 'r') + data = [] + for line in handle: + line = line.rstrip() + data.append(line) + handle.close() - return data, name, ext - - except: - raise RuntimeError('Could not load file: %s' % path) + return data, name, ext def _find_dimensions(self, data): @@ -308,6 +299,7 @@ class MolDyn(PythonAlgorithm): return num_q, num_t, num_f + #pylint: disable=too-many-locals,too-many-branches def _cdl_import(self, data, name): """ @@ -424,11 +416,21 @@ class MolDyn(PythonAlgorithm): yDat = np.append(yDat, np.array(S)) eDat = np.append(eDat, eZero) outWS = name + '_' + func - CreateWorkspace(OutputWorkspace=outWS, DataX=xDat, DataY=yDat, DataE=eDat, - Nspec=nQ, UnitX=xUnit, VerticalAxisUnit='MomentumTransfer', VerticalAxisValues=Qaxis) + + CreateWorkspace(OutputWorkspace=outWS, + DataX=xDat, + DataY=yDat, + DataE=eDat, + Nspec=nQ, + UnitX=xUnit, + VerticalAxisUnit='MomentumTransfer', + VerticalAxisValues=Qaxis) + output_ws_list.append(outWS) - GroupWorkspaces(InputWorkspaces=output_ws_list, OutputWorkspace=self._out_ws) + GroupWorkspaces(InputWorkspaces=output_ws_list, + OutputWorkspace=self._out_ws) + #pylint: disable=too-many-locals def _ascii_import(self, data, name): @@ -439,7 +441,8 @@ class MolDyn(PythonAlgorithm): @param name Name of data file """ - from IndirectNeutron import ChangeAngles, InstrParas, RunParas + from IndirectCommon import getEfixed + from IndirectNeutron import ChangeAngles, InstrParas logger.notice('Loading ASCII data: %s' % name) @@ -482,18 +485,22 @@ class MolDyn(PythonAlgorithm): yDat = np.append(yDat, np.array(S)) eDat = np.append(eDat, eZero) - CreateWorkspace(OutputWorkspace=self._out_ws, DataX=xDat, DataY=yDat, DataE=eDat, + CreateWorkspace(OutputWorkspace=self._out_ws, + DataX=xDat, + DataY=yDat, + DataE=eDat, Nspec=nQ, UnitX='TOF') + Qmax = Q[nQ - 1] instr = 'MolDyn' - ana = 'qmax' + ana = 'simul' if Qmax <= 2.0: refl = '2' else: refl = '4' InstrParas(self._out_ws, instr, ana, refl) - efixed = RunParas(self._out_ws, instr, name, name) + efixed = getEfixed(self._out_ws)# RunParas(self._out_ws, instr, name, name) logger.information('Qmax = ' + str(Qmax) + ' ; efixed = ' + str(efixed)) pi4 = 4.0 * math.pi wave = 1.8 * math.sqrt(25.2429 / efixed) @@ -537,7 +544,8 @@ class MolDyn(PythonAlgorithm): elif num_sample_hist < num_res_hist: logger.information('Cropping resolution workspace to sample') - resolution_ws = CropWorkspace(InputWorkspace=self._res_ws, StartWorkspaceIndex=0, + resolution_ws = CropWorkspace(InputWorkspace=self._res_ws, + StartWorkspaceIndex=0, EndWorkspaceIndex=num_sample_hist) # If the spectra counts match then just use the resolution as it is @@ -562,7 +570,8 @@ class MolDyn(PythonAlgorithm): # Convolve the symmetrised sample with the resolution ConvolveWorkspaces(OutputWorkspace=function_ws_name, - Workspace1=function_ws_name, Workspace2=resolution_ws) + Workspace1=function_ws_name, + Workspace2=resolution_ws) def _plot_spectra(self, ws_name): diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/dnsdata.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/dnsdata.py index 9117cf9c724b3939c5cdfb027cc1f852a8b49981..6468125642e0842c37914ba8000b2eeae7bda22e 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/dnsdata.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/dnsdata.py @@ -69,6 +69,10 @@ class DNSdata(object): unparsed = fhandler.read() blocks = unparsed.split(splitsymbol) + # check whether the file is complete + if len(blocks) < 9: + raise RuntimeError("The file %s is not complete!" % filename) + # parse each block # parse block 0 (header) res = parse_header(blocks[0]) @@ -79,7 +83,7 @@ class DNSdata(object): self.sample_name = res['sample'] self.userid = res['userid'] except: - raise ValueError("The file %s does not contain valid DNS data format." % filename) + raise RuntimeError("The file %s does not contain valid DNS data format." % filename) # parse block 1 (general information) b1splitted = [s.strip() for s in blocks[1].split('#')] b1rest = [el for el in b1splitted] 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 b5c5aba1a1c19e3d6618600a27d1ef5da4fcdb86..de20c3b1338580fe65312b5b74d9c29ff1d6a9f4 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt @@ -21,6 +21,7 @@ set ( TEST_PY_FILES EnggCalibrateTest.py EnggFitPeaksTest.py EnggFocusTest.py + EnggVanadiumCorrectionsTest.py FilterLogByTimeTest.py FindReflectometryLinesTest.py FlatPlatePaalmanPingsCorrectionTest.py @@ -35,6 +36,7 @@ set ( TEST_PY_FILES ISISIndirectDiffractionReductionTest.py ISISIndirectEnergyTransferTest.py LoadDNSLegacyTest.py + LoadEmptyVesuvioTest.py LoadFullprofFileTest.py LoadLiveDataTest.py LoadLogPropertyTableTest.py @@ -48,7 +50,6 @@ set ( TEST_PY_FILES MSDFitTest.py MuscatSofQWTest.py OSIRISDiffractionReductionTest.py - PDDetermineCharacterizationsTest.py ResNorm2Test.py RetrieveRunInfoTest.py SANSWideAngleCorrectionTest.py diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggCalibrateTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggCalibrateTest.py index 8538f39bb1742a3ac9905b74404dfd84468bc19b..adea57775481e7bf94d7dff692596fdb57e2374c 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggCalibrateTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggCalibrateTest.py @@ -1,11 +1,11 @@ import unittest -from mantid.simpleapi import * from mantid.api import * +import mantid.simpleapi as sapi class EnggCalibrateTest(unittest.TestCase): _data_ws = None - _van_ws = None + _van_curves_ws = None _van_integ_tbl = None # Note not using @classmethod setUpClass / tearDownClass because that's not supported in the old @@ -15,17 +15,17 @@ class EnggCalibrateTest(unittest.TestCase): Set up dependencies for one or more of the tests below. """ if not self.__class__._data_ws: - self.__class__._data_ws = LoadNexus("ENGINX00228061.nxs", OutputWorkspace='ENGIN-X_test_ws') + self.__class__._data_ws = sapi.LoadNexus("ENGINX00228061.nxs", OutputWorkspace='ENGIN-X_test_ws') - if not self.__class__._van_ws: + if not self.__class__._van_curves_ws: # Note the pre-calculated file instead of the too big vanadium run # self.__class__._van_ws = LoadNexus("ENGINX00236516.nxs", OutputWorkspace='ENGIN-X_test_vanadium_ws') - self.__class__._van_ws = LoadNexus(Filename= - 'ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs', - OutputWorkspace='ENGIN-X_vanadium_curves_test_ws') - self.__class__._van_integ_tbl = LoadNexus(Filename= - 'ENGINX_precalculated_vanadium_run000236516_integration.nxs', - OutputWorkspace='ENGIN-X_vanadium_integ_test_ws') + self.__class__._van_curves_ws = sapi.LoadNexus(Filename= + 'ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs', + OutputWorkspace='ENGIN-X_vanadium_curves_test_ws') + self.__class__._van_integ_tbl = sapi.LoadNexus(Filename= + 'ENGINX_precalculated_vanadium_run000236516_integration.nxs', + OutputWorkspace='ENGIN-X_vanadium_integ_test_ws') def test_issues_with_properties(self): @@ -36,28 +36,28 @@ class EnggCalibrateTest(unittest.TestCase): # No InputWorkspace property (required) self.assertRaises(RuntimeError, - EnggCalibrate, + sapi.EnggCalibrate, File='foo', Bank='1') # Wrong (mispelled) InputWorkspace property self.assertRaises(RuntimeError, - EnggCalibrate, + sapi.EnggCalibrate, InputWorkpace='anything_goes', Bank='2') # mispelled ExpectedPeaks - tbl = CreateEmptyTableWorkspace(OutputWorkspace='test_table') + tbl = sapi.CreateEmptyTableWorkspace(OutputWorkspace='test_table') self.assertRaises(RuntimeError, - EnggCalibrate, + sapi.EnggCalibrate, Inputworkspace=self.__class__._data_ws, DetectorPositions=tbl, Bank='2', Peaks='2') # mispelled DetectorPositions self.assertRaises(RuntimeError, - EnggCalibrate, + sapi.EnggCalibrate, InputWorkspace=self.__class__._data_ws, Detectors=tbl, Bank='2', Peaks='2') # There's no output workspace self.assertRaises(RuntimeError, - EnggCalibrate, + sapi.EnggCalibrate, InputWorkspace=self.__class__._data_ws, Bank='1') @@ -69,7 +69,7 @@ class EnggCalibrateTest(unittest.TestCase): # This should produce 'given peak center ... is outside of data range' warnings # and finally raise after a 'some peaks not found' error self.assertRaises(RuntimeError, - EnggCalibrate, + sapi.EnggCalibrate, InputWorkspace=self.__class__._data_ws, ExpectedPeaks=[0.2, 0.4], Bank='2') def test_runs_ok(self): @@ -77,10 +77,10 @@ class EnggCalibrateTest(unittest.TestCase): Checks normal operation. """ - difc, zero = EnggCalibrate(InputWorkspace=self.__class__._data_ws, - VanadiumWorkspace=self.__class__._van_ws, - VanadiumIntegWorkspace=self.__class__._van_integ_tbl, - ExpectedPeaks=[1.6, 1.1, 1.8], Bank='2') + difc, zero = sapi.EnggCalibrate(InputWorkspace=self.__class__._data_ws, + VanIntegrationWorkspace=self.__class__._van_integ_tbl, + VanCurvesWorkspace=self.__class__._van_curves_ws, + ExpectedPeaks=[1.6, 1.1, 1.8], Bank='2') self.check_3peaks_values(difc, zero) @@ -91,23 +91,37 @@ class EnggCalibrateTest(unittest.TestCase): """ # This file has: 1.6, 1.1, 1.8 (as the test above) filename = 'EnginX_3_expected_peaks_unittest.csv' - difc, zero = EnggCalibrate(InputWorkspace=self.__class__._data_ws, - VanadiumWorkspace=self.__class__._van_ws, - VanadiumIntegWorkspace=self.__class__._van_integ_tbl, - ExpectedPeaks=[-4, 40, 323], # nonsense, but FromFile should prevail - ExpectedPeaksFromFile=filename, - Bank='2') + difc, zero = sapi.EnggCalibrate(InputWorkspace=self.__class__._data_ws, + VanIntegrationWorkspace=self.__class__._van_integ_tbl, + VanCurvesWorkspace=self.__class__._van_curves_ws, + ExpectedPeaks=[-4, 40, 323], # nonsense, but FromFile should prevail + ExpectedPeaksFromFile=filename, + Bank='2') self.check_3peaks_values(difc, zero) def check_3peaks_values(self, difc, zero): # There are platform specific differences in final parameter values # For example in earlier versions, debian: 369367.57492582797; win7: 369242.28850305633 - expected_difc = -1910618.2889 - # assertLess would be nices, but only available in unittest >= 2.7 - self.assertTrue(abs((expected_difc-difc)/expected_difc) < 5e-3) - expected_zero = 2120458.99303 - self.assertTrue(abs((expected_zero-zero)/expected_zero) < 5e-3) + expected_difc = 19110.7598121 + # win7 results were ~0.831% different (19269.451153) from linux expected values, + # osx were ~0.995% different (18920.539474) + difc_err_epsilon = 1e-2 + + # assertLess would be nice, but only available in unittest >= 2.7 + self.assertTrue(abs((expected_difc-difc)/expected_difc) < difc_err_epsilon, + "Difc (%f) is too far from its expected value (%f)" %(difc, expected_difc)) + + expected_zero = -724.337353801 + # especially this zero parameter is extremely platform dependent/sensitive + # ubuntu: -724.337354; osx: -396.628396; win7: -995.879786 + + # this is obviously a ridiculous threshold to do just a very rough test that results/funcionality + # do not change too much + zero_err_epsilon = 0.5 + + self.assertTrue(abs((expected_zero-zero)/expected_zero) < zero_err_epsilon, + "Zero (%f) is too far from its expected value (%f)" %(zero, expected_zero)) if __name__ == '__main__': diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggFocusTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggFocusTest.py index c44b92194bb32b6e2196cfad9a2a26a97028f36e..0748244f3701b99099bc7be0db9de414d931deaa 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggFocusTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggFocusTest.py @@ -5,14 +5,14 @@ from mantid.api import * class EnggFocusTest(unittest.TestCase): _data_ws = None - _van_ws = None + _van_curves_ws = None _van_integ_tbl = None _expected_yvals_bank1 = [0.016676032919961604, 0.015995344072536975, 0.047449159145519233, 0.15629648148139513, 0.11018845452876322, 0.017291707350351286] - _expected_yvals_bank2 = [0.0, 0.024244737019873813, 0.075157734070604859, - 0.064284432802742111, 0.16711309433387569, 0.0015212255190929613] + _expected_yvals_bank2 = [0.0, 0.018310873111703541, 0.071387646720910913, + 0.061783574337739511, 0.13102948781549345, 0.001766956921862095] # Note not using @classmethod setUpClass / tearDownClass because that's not supported in the old # unittest of rhel6 @@ -24,12 +24,13 @@ class EnggFocusTest(unittest.TestCase): self.__class__._data_ws = LoadNexus(Filename='ENGINX00228061.nxs', OutputWorkspace='ENGIN-X_test_ws') - if not self.__class__._van_ws: + if not self.__class__._van_curves_ws: # Note the pre-calculated file instead of the too big vanadium run # self.__class__._van_ws = LoadNexus("ENGINX00236516.nxs", OutputWorkspace='ENGIN-X_test_vanadium_ws') - self.__class__._van_ws = LoadNexus(Filename= + self.__class__._van_curves_ws = LoadNexus(Filename= 'ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs', OutputWorkspace='ENGIN-X_vanadium_curves_test_ws') + if not self.__class__._van_integ_tbl: self.__class__._van_integ_tbl = LoadNexus(Filename= 'ENGINX_precalculated_vanadium_run000236516_integration.nxs', OutputWorkspace='ENGIN-X_vanadium_integ_test_ws') @@ -119,21 +120,21 @@ class EnggFocusTest(unittest.TestCase): out_name = 'out' out = EnggFocus(InputWorkspace=self.__class__._data_ws, - VanadiumWorkspace=self.__class__._van_ws, - VanadiumIntegWorkspace=self.__class__._van_integ_tbl, + VanIntegrationWorkspace=self.__class__._van_integ_tbl, + VanCurvesWorkspace=self.__class__._van_curves_ws, Bank='1', OutputWorkspace=out_name) self._check_output_ok(ws=out, ws_name=out_name, y_dim_max=1, yvalues=self._expected_yvals_bank1) - def test_runs_ok_south(self): + def test_runs_ok_north(self): """ - Same as before but with Bank='South' - equivalent to Bank='1' + Same as before but with Bank='North' - equivalent to Bank='1' """ out_name = 'out' out = EnggFocus(InputWorkspace=self.__class__._data_ws, - VanadiumWorkspace=self.__class__._van_ws, - VanadiumIntegWorkspace=self.__class__._van_integ_tbl, + VanIntegrationWorkspace=self.__class__._van_integ_tbl, + VanCurvesWorkspace=self.__class__._van_curves_ws, Bank='North', OutputWorkspace=out_name) self._check_output_ok(ws=out, ws_name=out_name, y_dim_max=1, yvalues=self._expected_yvals_bank1) @@ -144,8 +145,8 @@ class EnggFocusTest(unittest.TestCase): """ out_idx_name = 'out_idx' out_idx = EnggFocus(InputWorkspace=self.__class__._data_ws, - VanadiumWorkspace=self.__class__._van_ws, - VanadiumIntegWorkspace=self.__class__._van_integ_tbl, + VanIntegrationWorkspace=self.__class__._van_integ_tbl, + VanCurvesWorkspace=self.__class__._van_curves_ws, SpectrumNumbers='1-1200', OutputWorkspace=out_idx_name) @@ -158,8 +159,8 @@ class EnggFocusTest(unittest.TestCase): """ out_idx_name = 'out_idx' out_idx = EnggFocus(InputWorkspace=self.__class__._data_ws, - VanadiumWorkspace=self.__class__._van_ws, - VanadiumIntegWorkspace=self.__class__._van_integ_tbl, + VanIntegrationWorkspace=self.__class__._van_integ_tbl, + VanCurvesWorkspace=self.__class__._van_curves_ws, SpectrumNumbers='1-100, 101-500, 400-1200', OutputWorkspace=out_idx_name) self._check_output_ok(ws=out_idx, ws_name=out_idx_name, y_dim_max=1, @@ -172,8 +173,8 @@ class EnggFocusTest(unittest.TestCase): out_name = 'out_bank2' out_bank2 = EnggFocus(InputWorkspace=self.__class__._data_ws, Bank='2', - VanadiumWorkspace=self.__class__._van_ws, - VanadiumIntegWorkspace=self.__class__._van_integ_tbl, + VanCurvesWorkspace=self.__class__._van_curves_ws, + VanIntegrationWorkspace=self.__class__._van_integ_tbl, OutputWorkspace=out_name) self._check_output_ok(ws=out_bank2, ws_name=out_name, y_dim_max=1201, @@ -185,8 +186,8 @@ class EnggFocusTest(unittest.TestCase): """ out_name = 'out_bank_south' out_bank_south = EnggFocus(InputWorkspace=self.__class__._data_ws, - VanadiumWorkspace=self.__class__._van_ws, - VanadiumIntegWorkspace=self.__class__._van_integ_tbl, + VanIntegrationWorkspace=self.__class__._van_integ_tbl, + VanCurvesWorkspace=self.__class__._van_curves_ws, Bank='South', OutputWorkspace=out_name) self._check_output_ok(ws=out_bank_south, ws_name=out_name, y_dim_max=1201, diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggVanadiumCorrectionsTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggVanadiumCorrectionsTest.py new file mode 100644 index 0000000000000000000000000000000000000000..c051117e67b9a141cbd3df51933428d07db2e7ec --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/EnggVanadiumCorrectionsTest.py @@ -0,0 +1,121 @@ +import unittest +from mantid.api import * +import mantid.simpleapi as sapi + +class EnggVanadiumCorrectionsTest(unittest.TestCase): + + _data_ws = None + _van_integ_tbl = None + _van_curves_ws = None + + NUM_SPEC = 2513 + + # Note not using @classmethod setUpClass / tearDownClass because that's not supported in the old + # unittest of rhel6 + def setUp(self): + """ + Set up dependencies (big files load) for one or more of the tests below. + """ + if not self.__class__._data_ws: + self.__class__._data_ws = sapi.LoadNexus("ENGINX00228061.nxs", OutputWorkspace='ENGIN-X_test_ws') + + if not self.__class__._van_curves_ws: + # Note the pre-calculated file instead of the too big vanadium run + # self.__class__._van_ws = LoadNexus("ENGINX00236516.nxs", OutputWorkspace='ENGIN-X_test_vanadium_ws') + self.__class__._van_curves_ws = sapi.LoadNexus(Filename= + 'ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs', + OutputWorkspace='ENGIN-X_vanadium_curves_test_ws') + + if not self.__class__._van_integ_tbl: + self.__class__._van_integ_tbl = sapi.LoadNexus(Filename= + 'ENGINX_precalculated_vanadium_run000236516_integration.nxs', + OutputWorkspace='ENGIN-X_vanadium_integ_test_ws') + + def test_issues_with_properties(self): + """ + Tests proper error handling when passing wrong properties or not passing required + ones. + """ + + # absolutely wrong properties passed + self.assertRaises(RuntimeError, + sapi.EnggVanadiumCorrections, + File='foo', Bank='1') + + # Wrong (mispelled) Workspace property + self.assertRaises(RuntimeError, + sapi.EnggVanadiumCorrections, + InputWorkspace='anything_goes') + + # mispelled VanadiumWorkspace + self.assertRaises(RuntimeError, + sapi.EnggVanadiumCorrections, + VanWorkspace=self.__class__._data_ws, + IntegrationWorkspace=self.__class__._van_integ_tbl, + CurvesWorkspace=self.__class__._van_curves_ws) + + # mispelled CurvesWorkspace + self.assertRaises(RuntimeError, + sapi.EnggVanadiumCorrections, + IntegrationWorkspace=self.__class__._van_integ_tbl, + CurveWorkspace=self.__class__._van_curves_ws) + + # mispelled IntegrationWorkspace + self.assertRaises(RuntimeError, + sapi.EnggVanadiumCorrections, + IntegWorkspace=self.__class__._van_integ_tbl, + CurvesWorkspace=self.__class__._van_curves_ws) + + def _check_corrected_ws(self, ws): + self.assertEqual(ws.getAxis(0).getUnit().unitID(), 'TOF') + self.assertEqual(ws.getAxis(1).getUnit().unitID(), 'Label') + self.assertEqual(ws.getNumberHistograms(), self.NUM_SPEC) + + def _check_integ_ws(self, ws): + self.assertTrue(isinstance(ws, ITableWorkspace), + 'The integration workspace should be a table workspace.') + self.assertEqual(ws.columnCount(), 1) + self.assertEqual(ws.rowCount(), self.NUM_SPEC) + + def _check_curves_ws(self, ws): + self.assertTrue(0 == ws.getNumberHistograms() % 3) + self.assertTrue(isinstance(ws, MatrixWorkspace), + 'The integration workspace should be a matrix workspace.') + + def test_runs_ok_when_reusing_precalculated(self): + """ + Checks normal operation, re-using previously calculated integrations and curves from + Vanadium run data + """ + sample_ws = self.__class__._data_ws + int_ws = self.__class__._van_integ_tbl + curves_ws = self.__class__._van_curves_ws + sapi.EnggVanadiumCorrections(Workspace=sample_ws, + IntegrationWorkspace=int_ws, + CurvesWorkspace=curves_ws) + + self._check_corrected_ws(sample_ws) + self._check_integ_ws(int_ws) + self._check_curves_ws(curves_ws) + + # This is disabled because it would require loading the big vanadium run file. This is tested + # in the EnggCalibration system test + def disabled_test_runs_ok_when_calculating(self): + """ + Checks normal operation, when calculating integrations and curves from Vanadium run data + """ + sample_ws = self.__class__._data_ws + integ_ws_name = 'calc_integ_ws' + curves_ws_name = 'calc_curves_ws' + out = sapi.EnggVanadiumCorrections(Workspace=sample_ws, + VanadiumWorkspace=self.__class__._van_ws, + IntegrationWorkspace=integ_ws_name, + CurvesWorkspace=curves_ws_name) + + self._check_corrected_ws(sample_ws) + self._check_integ_ws(integ_ws_name) + self._check_curves_ws(curves_ws_name) + + +if __name__ == '__main__': + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectDiffractionReductionTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectDiffractionReductionTest.py index c3dac5c2496a682e63fdaa8a4eb414c25bcb183d..f5093a666743b8fca0967ed509bec401baee3ad8 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectDiffractionReductionTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectDiffractionReductionTest.py @@ -1,3 +1,5 @@ +#pylint: disable=too-many-public-methods,invalid-name + import unittest from mantid.simpleapi import * from mantid.api import * @@ -10,13 +12,106 @@ class ISISIndirectDiffractionReductionTest(unittest.TestCase): Sanity test to ensure the most basic reduction actually completes. """ - ws = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW'], - Instrument='IRIS', - Mode='diffspec', - SpectraRange=[105, 112]) + wks = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Mode='diffspec', + SpectraRange=[105, 112]) + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(len(wks), 1) + self.assertEqual(wks.getNames()[0], 'IRS26176_diffspec_red') + + red_ws = wks[0] + self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(red_ws.getNumberHistograms(), 1) + + + def test_rebin_param(self): + """ + Tests rebinning. + """ + + wks = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Mode='diffspec', + SpectraRange=[105, 112], + RebinParam='3,0.1,4') + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(len(wks), 1) + self.assertEqual(wks.getNames()[0], 'IRS26176_diffspec_red') + + red_ws = wks[0] + self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(red_ws.getNumberHistograms(), 1) + + self.assertEqual(red_ws.blocksize(), 10) + data_x = red_ws.dataX(0) + self.assertAlmostEqual(data_x[0], 3.0) + self.assertAlmostEqual(data_x[-1], 4.0) + + + def test_multi_files(self): + """ + Test reducing multiple files. + """ + + wks = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW', 'IRS26173.RAW'], + Instrument='IRIS', + Mode='diffspec', + SpectraRange=[105, 112]) + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(len(wks), 2) + self.assertEqual(wks.getNames()[0], 'IRS26176_diffspec_red') + self.assertEqual(wks.getNames()[1], 'IRS26173_diffspec_red') + + red_ws = wks[0] + self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(red_ws.getNumberHistograms(), 1) + + + def test_sum_files(self): + """ + Test summing multiple runs. + """ + + wks = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW', 'IRS26173.RAW'], + SumFiles=True, + Instrument='IRIS', + Mode='diffspec', + SpectraRange=[105, 112]) + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(len(wks), 1) + self.assertEqual(wks.getNames()[0], 'IRS26176_multi_diffspec_red') - self.assertTrue(isinstance(ws, WorkspaceGroup), 'Result workspace should be a workspace group.') - self.assertEqual(ws.getNames()[0], 'IRS26176_diffspec_red') + red_ws = wks[0] + self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(red_ws.getNumberHistograms(), 1) + + self.assertTrue('multi_run_numbers' in red_ws.getRun()) + self.assertEqual(red_ws.getRun().get('multi_run_numbers').value, '26176,26173') + + + def test_grouping_individual(self): + """ + Test setting individual grouping, one spectrum per detector. + """ + + wks = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Mode='diffspec', + SpectraRange=[105, 112], + GroupingPolicy='Individual') + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(len(wks), 1) + self.assertEqual(wks.getNames()[0], 'IRS26176_diffspec_red') + + red_ws = wks[0] + self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(red_ws.getNumberHistograms(), 8) def test_reduction_with_container_completes(self): @@ -24,14 +119,19 @@ class ISISIndirectDiffractionReductionTest(unittest.TestCase): Test to ensure that reduction with container subtraction works. """ - ws = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW'], - ContainerFiles=['IRS26173.RAW'], - Instrument='IRIS', - Mode='diffspec', - SpectraRange=[105, 112]) + wks = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW'], + ContainerFiles=['IRS26173.RAW'], + Instrument='IRIS', + Mode='diffspec', + SpectraRange=[105, 112]) - self.assertTrue(isinstance(ws, WorkspaceGroup), 'Result workspace should be a workspace group.') - self.assertEqual(ws.getNames()[0], 'IRS26176_diffspec_red') + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(len(wks), 1) + self.assertEqual(wks.getNames()[0], 'IRS26176_diffspec_red') + + red_ws = wks[0] + self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(red_ws.getNumberHistograms(), 1) def test_reduction_with_container_and_scale_completes(self): @@ -39,15 +139,20 @@ class ISISIndirectDiffractionReductionTest(unittest.TestCase): Test to ensure that reduction with container subtraction works. """ - ws = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW'], - ContainerFiles=['IRS26173.RAW'], - ContainerScaleFactor=0.5, - Instrument='IRIS', - Mode='diffspec', - SpectraRange=[105, 112]) + wks = ISISIndirectDiffractionReduction(InputFiles=['IRS26176.RAW'], + ContainerFiles=['IRS26173.RAW'], + ContainerScaleFactor=0.1, + Instrument='IRIS', + Mode='diffspec', + SpectraRange=[105, 112]) + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(len(wks), 1) + self.assertEqual(wks.getNames()[0], 'IRS26176_diffspec_red') - self.assertTrue(isinstance(ws, WorkspaceGroup), 'Result workspace should be a workspace group.') - self.assertEqual(ws.getNames()[0], 'IRS26176_diffspec_red') + red_ws = wks[0] + self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(red_ws.getNumberHistograms(), 1) if __name__ == '__main__': diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectEnergyTransferTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectEnergyTransferTest.py index a6d4b219aa822ffc76cd72addb9dc0c4c76e99c6..b721ff5ad75c50aadf6d2002f72006abe34fd7dc 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectEnergyTransferTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectEnergyTransferTest.py @@ -41,6 +41,7 @@ class ISISIndirectEnergyTransferTest(unittest.TestCase): red_ws = wks.getItem(0) self.assertEqual(red_ws.getNumberHistograms(), 51) + self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'DeltaE') def test_reduction_with_range(self): @@ -61,6 +62,80 @@ class ISISIndirectEnergyTransferTest(unittest.TestCase): self.assertEqual(red_ws.getNumberHistograms(), 6) + def test_grouping_all(self): + """ + Tests setting the grouping policy to all. + """ + + wks = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53], + GroupingMethod='All') + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(wks.getNames()[0], 'IRS26176_graphite002_red') + + red_ws = wks.getItem(0) + self.assertEqual(red_ws.getNumberHistograms(), 1) + + + def test_grouping_individual(self): + """ + Tests setting the grouping policy to individual. + """ + + wks = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53], + GroupingMethod='Individual') + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(wks.getNames()[0], 'IRS26176_graphite002_red') + + red_ws = wks.getItem(0) + self.assertEqual(red_ws.getNumberHistograms(), 51) + + + def test_reduction_with_background_subtraction(self): + """ + Tests running a reduction with a background subtraction. + """ + + wks = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53], + BackgroundRange=[70000, 75000]) + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(wks.getNames()[0], 'IRS26176_graphite002_red') + + + def test_reduction_with_output_unit(self): + """ + Tests creating reduction in different X units. + """ + + wks = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53], + UnitX='DeltaE_inWavenumber') + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(wks.getNames()[0], 'IRS26176_graphite002_red') + + red_ws = wks.getItem(0) + self.assertEqual(red_ws.getNumberHistograms(), 51) + self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'DeltaE_inWavenumber') + + def test_reduction_with_detailed_balance(self): """ Sanity test to ensure a reduction using detailed balance option @@ -142,6 +217,44 @@ class ISISIndirectEnergyTransferTest(unittest.TestCase): self.assertEqual(red_ws.getNumberHistograms(), 6) + def test_multi_files(self): + """ + Test reducing multiple files. + """ + + wks = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW', 'IRS26173.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53]) + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(len(wks), 2) + self.assertEqual(wks.getNames()[0], 'IRS26176_graphite002_red') + self.assertEqual(wks.getNames()[1], 'IRS26173_graphite002_red') + + + def test_sum_files(self): + """ + Test summing multiple runs. + """ + + wks = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW', 'IRS26173.RAW'], + SumFIles=True, + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53]) + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(len(wks), 1) + self.assertEqual(wks.getNames()[0], 'IRS26176_multi_graphite002_red') + + red_ws = wks[0] + self.assertTrue('multi_run_numbers' in red_ws.getRun()) + self.assertEqual(red_ws.getRun().get('multi_run_numbers').value, '26176,26173') + + def test_instrument_validation_failure(self): """ Tests that an invalid instrument configuration causes the validation to @@ -175,5 +288,46 @@ class ISISIndirectEnergyTransferTest(unittest.TestCase): GroupingMethod='Workspace') + def test_reduction_with_manual_efixed(self): + """ + Sanity test to ensure a reduction with a manual Efixed value completes. + """ + + wks = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53], + Efixed=1.9) + + self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(wks.getNames()[0], 'IRS26176_graphite002_red') + + red_ws = wks.getItem(0) + self.assertEqual(red_ws.getNumberHistograms(), 51) + + + def test_reduction_with_manual_efixed_same_as_default(self): + """ + Sanity test to ensure that manually setting the default Efixed value + gives the same results as not providing it. + """ + + ref = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53]) + + wks = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53], + Efixed=1.845) + + self.assertTrue(CheckWorkspacesMatch(ref, wks), 'Success!') + + if __name__ == '__main__': unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/LoadDNSLegacyTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/LoadDNSLegacyTest.py index 8f67f05cb03bb1f2b31165527d16dc122a024387..43e2359a9ff9e0d1673611063d9694331a067ab7 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/LoadDNSLegacyTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/LoadDNSLegacyTest.py @@ -2,6 +2,8 @@ import unittest from testhelpers import run_algorithm from mantid.api import AnalysisDataService from math import pi +import os +from mantid.simpleapi import LoadDNSLegacy class LoadDNSLegacyTest(unittest.TestCase): @@ -27,10 +29,31 @@ class LoadDNSLegacyTest(unittest.TestCase): self.assertEqual(8332872, run.getProperty('mon_sum').value) self.assertEqual('y', run.getProperty('polarisation').value) # check whether detector bank is rotated - det = ws.getDetector(1) + det = ws.getDetector(0) self.assertAlmostEqual(8.54, ws.detectorSignedTwoTheta(det)*180/pi) run_algorithm("DeleteWorkspace", Workspace=outputWorkspaceName) return + def _createIncompleteFile(self, filename): + """ + creates an incomplete data file + """ + with open(filename, "w") as f: + f.write("# DNS Data userid=sa,exp=961,file=988,sample=run2") + f.write("#--------------------------------------------------------------------------") + f.write("# 9") + f.write("# User: Some User") + f.close() + return + + def test_LoadInvalidData(self): + outputWorkspaceName = "LoadDNSLegacyTest_Test2" + filename = "dns-incomplete.d_dat" + self._createIncompleteFile(filename) + self.assertRaises(RuntimeError, LoadDNSLegacy, Filename=filename, + OutputWorkspace=outputWorkspaceName, Polarisation='y') + os.remove(filename) + + if __name__ == '__main__': unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/LoadEmptyVesuvioTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/LoadEmptyVesuvioTest.py new file mode 100644 index 0000000000000000000000000000000000000000..04f9a20333b5b1f379b16ed51bf37c175d3500b8 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/LoadEmptyVesuvioTest.py @@ -0,0 +1,43 @@ +import unittest +from mantid.simpleapi import * +from mantid.api import * + +class LoadEmptyVesuvioTest(unittest.TestCase): + + def test_load_empty_no_par_file(self): + """ + Tests loading the instrument with no PAR file. + """ + evs_ws = LoadEmptyVesuvio() + + self.assertEqual(evs_ws.getNumberHistograms(), 198) + self.assertEqual(evs_ws.blocksize(), 1) + + evs = evs_ws.getInstrument() + sample_pos = evs.getSample().getPos() + + bs_det_1 = evs_ws.getDetector(2) + bs_det_1_l1 = sample_pos.distance(bs_det_1.getPos()) + + self.assertAlmostEqual(bs_det_1_l1, 0.6747705, places=7) + + def test_load_empty_with_par_file(self): + """ + Tests loading the instrument with no PAR file. + """ + evs_ws = LoadEmptyVesuvio(InstrumentParFile='IP0005.dat') + + self.assertEqual(evs_ws.getNumberHistograms(), 198) + self.assertEqual(evs_ws.blocksize(), 1) + + evs = evs_ws.getInstrument() + sample_pos = evs.getSample().getPos() + + bs_det_1 = evs_ws.getDetector(2) + bs_det_1_l1 = sample_pos.distance(bs_det_1.getPos()) + + self.assertAlmostEqual(bs_det_1_l1, 0.6707999706268, places=7) + + +if __name__ == '__main__': + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/OSIRISDiffractionReductionTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/OSIRISDiffractionReductionTest.py index f8c932cc9b195c98ced8033e772a4a61a36c504b..95e486e9ecfb9d3152fa199eaa770132973e4736 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/OSIRISDiffractionReductionTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/OSIRISDiffractionReductionTest.py @@ -1,3 +1,5 @@ +#pylint: disable=too-many-public-methods,invalid-name + import unittest from mantid.simpleapi import * from mantid.api import * @@ -10,12 +12,13 @@ class OSIRISDiffractionReductionTest(unittest.TestCase): Sanity test to ensure the most basic reduction actually completes. """ - ws = OSIRISDiffractionReduction(Sample=['OSI89813.raw'], - CalFile='osiris_041_RES10.cal', - Vanadium=['osi89757.raw']) + wks = OSIRISDiffractionReduction(Sample=['OSI89813.raw'], + CalFile='osiris_041_RES10.cal', + Vanadium=['osi89757.raw']) - self.assertTrue(isinstance(ws, MatrixWorkspace), 'Result workspace should be a matrix workspace.') - self.assertEqual(ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertTrue(isinstance(wks, MatrixWorkspace), 'Result workspace should be a matrix workspace.') + self.assertEqual(wks.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(wks.getNumberHistograms(), 1) def test_reduction_with_manual_drange_completes(self): @@ -24,14 +27,15 @@ class OSIRISDiffractionReductionTest(unittest.TestCase): The run here is for dRange 4. """ - ws = OSIRISDiffractionReduction(Sample=['OSI10203.raw'], - CalFile='osiris_041_RES10.cal', - Vanadium=['OSI10156.raw'], - DetectDRange=False, - DRange=4) + wks = OSIRISDiffractionReduction(Sample=['OSI10203.raw'], + CalFile='osiris_041_RES10.cal', + Vanadium=['OSI10156.raw'], + DetectDRange=False, + DRange=4) - self.assertTrue(isinstance(ws, MatrixWorkspace), 'Result workspace should be a matrix workspace.') - self.assertEqual(ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertTrue(isinstance(wks, MatrixWorkspace), 'Result workspace should be a matrix workspace.') + self.assertEqual(wks.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(wks.getNumberHistograms(), 1) def test_reduction_with_can_subtraction(self): @@ -39,15 +43,16 @@ class OSIRISDiffractionReductionTest(unittest.TestCase): Tests reduction after subtraction of an empty can. """ - ws = OSIRISDiffractionReduction(Sample=['OSI10203.raw'], - CalFile='osiris_041_RES10.cal', - Vanadium=['OSI10156.raw'], - Container='OSI10241.raw', - DetectDRange=False, - DRange=4) + wks = OSIRISDiffractionReduction(Sample=['OSI10203.raw'], + CalFile='osiris_041_RES10.cal', + Vanadium=['OSI10156.raw'], + Container='OSI10241.raw', + DetectDRange=False, + DRange=4) - self.assertTrue(isinstance(ws, MatrixWorkspace), 'Result workspace should be a matrix workspace.') - self.assertEqual(ws.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertTrue(isinstance(wks, MatrixWorkspace), 'Result workspace should be a matrix workspace.') + self.assertEqual(wks.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(wks.getNumberHistograms(), 1) def test_reduction_with_can_subtraction_and_scale(self): @@ -55,16 +60,28 @@ class OSIRISDiffractionReductionTest(unittest.TestCase): Tests reduction after subtraction of an empty can. """ - ws = OSIRISDiffractionReduction(Sample=['OSI10203.raw'], - CalFile='osiris_041_RES10.cal', - Vanadium=['OSI10156.raw'], - Container='OSI10241.raw', - ContainerScaleFactor=0.5, - DetectDRange=False, - DRange=4) + wks = OSIRISDiffractionReduction(Sample=['OSI10203.raw'], + CalFile='osiris_041_RES10.cal', + Vanadium=['OSI10156.raw'], + Container='OSI10241.raw', + ContainerScaleFactor=0.5, + DetectDRange=False, + DRange=4) + + self.assertTrue(isinstance(wks, MatrixWorkspace), 'Result workspace should be a matrix workspace.') + self.assertEqual(wks.getAxis(0).getUnit().unitID(), 'dSpacing') + self.assertEqual(wks.getNumberHistograms(), 1) + - self.assertTrue(isinstance(ws, MatrixWorkspace), 'Result workspace should be a matrix workspace.') - self.assertEqual(ws.getAxis(0).getUnit().unitID(), 'dSpacing') + def test_failure_no_vanadium(self): + """ + Tests error handling when failed to obtain a vanadium run. + """ + self.assertRaises(RuntimeError, + OSIRISDiffractionReduction, + Sample=['OSI89813.raw'], + CalFile='osiris_041_RES10.cal', + Vanadium=['osi89757.raw']) if __name__ == '__main__': diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/PDDetermineCharacterizationsTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/PDDetermineCharacterizationsTest.py deleted file mode 100644 index 7c903af430304ff2890bfb908e85dee894e5cde1..0000000000000000000000000000000000000000 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/PDDetermineCharacterizationsTest.py +++ /dev/null @@ -1,160 +0,0 @@ -import unittest -import numpy -from mantid.simpleapi import * -from mantid.kernel import * -from mantid.api import * - -class PDDetermineCharacterizationsTest(unittest.TestCase): - def createLogWksp(self): - if self.logWksp is not None: - return - - # create the log workspace - self.logWksp = CreateWorkspace(DataX=numpy.arange(-1, 1.2, 0.2), - DataY=numpy.arange(-1, 1, 0.2), - OutputWorkspace="_det_char_logs") - - # add a frequency log - freq = FloatTimeSeriesProperty("frequency") - freq.units="Hz" - freq.addValue(DateAndTime("2008-12-18T17:58:38"), 60.) - self.logWksp.run().addProperty(freq.name, freq, True) - - # add a wavelength log - wave = FloatTimeSeriesProperty("LambdaRequest") - wave.units="Angstrom" - wave.addValue(DateAndTime("2008-12-18T17:58:38"), 0.533) - self.logWksp.run().addProperty(wave.name, wave, True) - - def createTableWksp(self, full): - # pick the right name - if full: - if self.charWksp is not None: - return - name = "_det_char_table_full" - else: - if self.charWkspEmpty is not None: - return - name = "_det_char_table_empty" - - # table columns - labels = ["frequency", "wavelength", "bank", "vanadium", "container", - "empty", "d_min", "d_max", "tof_min", "tof_max"] - types = ["double", "double", "int", "int", "int", - "int", "str", "str", "double", "double"] - - # create the table - table = CreateEmptyTableWorkspace(OutputWorkspace=name) - for (label, typ) in zip(labels, types): - table.addColumn(typ, label) - - if full: - rows = [[60., 0.533, 1, 17702, 17711, 0, "0.05", "2.20", 0000.00, 16666.67], - [60., 1.333, 3, 17703, 17712, 0, "0.43", "5.40", 12500.00, 29166.67], - [60., 2.665, 4, 17704, 17713, 0, "1.15", "9.20", 33333.33, 50000.00], - [60., 4.797, 5, 17705, 17714, 0, "2.00", "15.35", 66666.67, 83333.67]] - for row in rows: - table.addRow(row) - self.charWksp = table - else: - self.charWkspEmpty = table - - def setUp(self): - self.mgrName = "__pd_reduction_properties" - self.logWksp = None - self.charWkspEmpty = None - self.charWksp = None - self.defaultInfo = { - "frequency":0., - "wavelength":0., - "bank":1, - "vanadium":0, - "container":0, - "empty":0, - "d_min":"", - "d_max":"", - "tof_min":0., - "tof_max":0. - } - - def tearDown(self): - if self.logWksp is not None: - DeleteWorkspace(self.logWksp) - if self.charWksp is not None: - DeleteWorkspace(self.charWksp) - - def test_exception(self): - self.createLogWksp() - self.assertRaises(RuntimeError, PDDetermineCharacterizations) - self.assertRaises(RuntimeError, PDDetermineCharacterizations, - InputWorkspace=self.logWksp) - - def compareResult(self, expect, manager): - for key in expect.keys(): - if type([]) == type(expect[key]): - self.checkSequence(key, expect[key], - manager.getProperty(key).value) - else: - self.assertEqual(expect[key], manager.getProperty(key).value, - "'%s' doesn't have expected value" % key) - - def checkSequence(self, k, tarr, rarr): - try: - self.assertSequenceEqual(tarr, rarr) - except AttributeError: - # RHEL6 doesn't have assertSequenceEqual since it runs python 2.6 - self.assertEquals(len(tarr), len(rarr), - "'%s' doesn't have same length" % k) - import itertools - for (i, (tval, rval)) in enumerate(itertools.izip(tarr, rarr)): - try: - self.assertEqual(tval, rval) - except AssertionError as detail: - message = "'%s' doesn't have expected value (%s) at index %d" - raise AssertionError(message % (k, detail, i)) - - def test_emptyChar(self): - self.createLogWksp() - self.createTableWksp(False) - PDDetermineCharacterizations(InputWorkspace=self.logWksp, - Characterizations=self.charWkspEmpty, - ReductionProperties=self.mgrName) - - self.compareResult(self.defaultInfo, - PropertyManagerDataService.retrieve(self.mgrName)) - - def test_fullChar(self): - self.createLogWksp() - self.createTableWksp(True) - PDDetermineCharacterizations(InputWorkspace=self.logWksp, - Characterizations=self.charWksp, - ReductionProperties=self.mgrName) - - result = {"frequency":60., - "wavelength":0.533, - "bank":1, - "vanadium":17702, - "container":17711, - "empty":0, - "d_min":[0.05], - "d_max":[2.20], - "tof_min":0000.00, - "tof_max":16666.67} - self.compareResult(result, - PropertyManagerDataService.retrieve(self.mgrName)) - - PDDetermineCharacterizations(InputWorkspace=self.logWksp, - Characterizations=self.charWksp, - ReductionProperties=self.mgrName, - BackRun=-1, - NormRun=-1, - NormBackRun=-1) - result["vanadium"] = 0 - result["container"] = 0 - result["empty"] = 0 - self.compareResult(result, - PropertyManagerDataService.retrieve(self.mgrName)) - - -if __name__ == "__main__": - unittest.main() diff --git a/Code/Mantid/Framework/ScriptRepository/inc/MantidScriptRepository/ScriptRepositoryImpl.h b/Code/Mantid/Framework/ScriptRepository/inc/MantidScriptRepository/ScriptRepositoryImpl.h index 02ab5aa294ecacab94b11194d0cb01164c277be3..ad23b61e30b8c4154d3fd12187913efcf53aa5dc 100644 --- a/Code/Mantid/Framework/ScriptRepository/inc/MantidScriptRepository/ScriptRepositoryImpl.h +++ b/Code/Mantid/Framework/ScriptRepository/inc/MantidScriptRepository/ScriptRepositoryImpl.h @@ -161,6 +161,7 @@ private: void download_directory(const std::string &); void download_file(const std::string &, RepositoryEntry &); void updateLocalJson(const std::string &, const RepositoryEntry &); + void updateRepositoryJson(const std::string &, const RepositoryEntry &); /// flag that indicate a valid repository bool valid; diff --git a/Code/Mantid/Framework/ScriptRepository/src/ScriptRepositoryImpl.cpp b/Code/Mantid/Framework/ScriptRepository/src/ScriptRepositoryImpl.cpp index 60de05488bc5e65673260c8c4aba20ef1edbf420..bdb5e8d45e01f4cfcd9bbef064f849b007255db5 100644 --- a/Code/Mantid/Framework/ScriptRepository/src/ScriptRepositoryImpl.cpp +++ b/Code/Mantid/Framework/ScriptRepository/src/ScriptRepositoryImpl.cpp @@ -858,6 +858,18 @@ void ScriptRepositoryImpl::upload(const std::string &file_path, g_log.information() << "ScriptRepository update local json " << std::endl; updateLocalJson(file_path, entry); /// FIXME: performance! + // add the entry to the repository.json. The + // repository.json should change at the + // remote repository, and we could just download the new one, but + // we can not rely on the server updating it fast enough. + // So add to the file locally to avoid race condition. + RepositoryEntry &remote_entry = repo.at(file_path); + if (!published_date.empty()) + remote_entry.pub_date = DateAndTime(published_date); + remote_entry.status = BOTH_UNCHANGED; + g_log.debug() << "ScriptRepository updating repository json " << std::endl; + updateRepositoryJson(file_path, remote_entry); + } else throw ScriptRepoException(info, detail); @@ -866,6 +878,53 @@ void ScriptRepositoryImpl::upload(const std::string &file_path, } } +/* +* Adds an entry to .repository.json +* This is necessary when uploading a file to keep .repository.json and +* .local.json in sync, and thus display correct file status in the GUI. +* Requesting an updated .repository.json from the server is not viable +* at such a time as it would create a race condition. +* @param path: relative path of uploaded file +* @param entry: the entry to add to the json file +*/ +void ScriptRepositoryImpl::updateRepositoryJson(const std::string &path, + const RepositoryEntry &entry) { + + ptree repository_json; + std::string filename = std::string(local_repository).append(".repository.json"); + read_json(filename, repository_json); + + ptree::const_assoc_iterator it = repository_json.find(path); + if (it == repository_json.not_found()) { + boost::property_tree::ptree array; + array.put(std::string("author"), + entry.author); + array.put(std::string("description"), + entry.description); + std::string directory = + (const char *)((entry.directory) ? "true" : "false"); + array.put(std::string("directory"), + directory); + array.put(std::string("pub_date"), + entry.pub_date.toFormattedString()); + repository_json.push_back( + std::pair<std::string, + boost::property_tree::basic_ptree<std::string, std::string>>( + path, array)); + } + + g_log.debug() << "Update LOCAL JSON FILE" << std::endl; + #if defined(_WIN32) || defined(_WIN64) + // set the .repository.json and .local.json not hidden to be able to edit it + SetFileAttributes(filename.c_str(), FILE_ATTRIBUTE_NORMAL); + #endif + write_json(filename, repository_json); + #if defined(_WIN32) || defined(_WIN64) + // set the .repository.json and .local.json hidden + SetFileAttributes(filename.c_str(), FILE_ATTRIBUTE_HIDDEN); + #endif +} + /** * Delete one file from the local and the central ScriptRepository * It will send in a POST method, with the file path to find the path : diff --git a/Code/Mantid/MantidPlot/CMakeLists.txt b/Code/Mantid/MantidPlot/CMakeLists.txt index ca4b2a04ac4e3b19668b2df53d39f1ba9d0a8a0b..fba0e989736961c4035d7b60929dcfba50664314 100644 --- a/Code/Mantid/MantidPlot/CMakeLists.txt +++ b/Code/Mantid/MantidPlot/CMakeLists.txt @@ -438,6 +438,7 @@ set ( MANTID_HDRS src/Mantid/AlgorithmMonitor.h src/Mantid/InstrumentWidget/InstrumentActor.h src/Mantid/InstrumentWidget/InstrumentTreeModel.h src/Mantid/InstrumentWidget/InstrumentTreeWidget.h + src/Mantid/InstrumentWidget/InstrumentWindowTypes.h src/Mantid/InstrumentWidget/InstrumentWindow.h src/Mantid/InstrumentWidget/InstrumentWindowTab.h src/Mantid/InstrumentWidget/InstrumentWindowRenderTab.h diff --git a/Code/Mantid/MantidPlot/pymantidplot/__init__.py b/Code/Mantid/MantidPlot/pymantidplot/__init__.py index 4e624105398350cd560133150e97c367fbf5a396..d23d6ff962e0ffdfb3dfb0aef1e4ecc48ded258e 100644 --- a/Code/Mantid/MantidPlot/pymantidplot/__init__.py +++ b/Code/Mantid/MantidPlot/pymantidplot/__init__.py @@ -20,7 +20,7 @@ import mantid.api # Import into the global namespace qti classes that: # (a) don't need a proxy & (b) can be constructed from python or (c) have enumerations within them from _qti import (PlotSymbol, ImageSymbol, ArrowMarker, ImageMarker, - GraphOptions, InstrumentWindow, InstrumentWindowPickTab, InstrumentWindowMaskTab) + GraphOptions, InstrumentWindow, InstrumentWindowRenderTab, InstrumentWindowPickTab, InstrumentWindowMaskTab) # Make the ApplicationWindow instance accessible from the mantidplot namespace from _qti import app diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.h index edc617cb9349a03b778419fa399d7b6639d18ed1..7623840a9398b36c6814f9bc264a87f1a7bd667e 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindow.h @@ -6,6 +6,7 @@ #include "MantidGLWidget.h" #include "BinDialog.h" +#include "InstrumentWindowTypes.h" #include "MantidQtAPI/GraphOptions.h" #include "MantidQtAPI/WorkspaceObserver.h" @@ -58,7 +59,7 @@ class QSettings; and needs to be updated whenever the instrument view functionality changes. */ -class InstrumentWindow : public MdiSubWindow, public MantidQt::API::WorkspaceObserver, public Mantid::API::AlgorithmObserver, public Mantid::IProjectSerialisable +class InstrumentWindow : public MdiSubWindow, public MantidQt::API::WorkspaceObserver, public Mantid::API::AlgorithmObserver, public Mantid::IProjectSerialisable, public InstrumentWindowTypes { Q_OBJECT diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowTab.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowTab.h index 37df6b1cc3e375d47f34697f2db78fb173849162..53ca85b651f8036ceea9f3a4041803595fb02e73 100644 --- a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowTab.h +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowTab.h @@ -1,6 +1,8 @@ #ifndef INSTRUMENTWINDOWTAB_H #define INSTRUMENTWINDOWTAB_H +#include "InstrumentWindowTypes.h" + #include <QFrame> #include <boost/shared_ptr.hpp> @@ -13,7 +15,7 @@ class ProjectionSurface; class QSettings; class QMenu; -class InstrumentWindowTab : public QFrame +class InstrumentWindowTab : public QFrame, public InstrumentWindowTypes { Q_OBJECT public: diff --git a/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowTypes.h b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowTypes.h new file mode 100644 index 0000000000000000000000000000000000000000..e4fcb2e5e44883a311f9defc2206e9fa91499e6f --- /dev/null +++ b/Code/Mantid/MantidPlot/src/Mantid/InstrumentWidget/InstrumentWindowTypes.h @@ -0,0 +1,19 @@ +#ifndef INSTRUMENTWINDOWTYPES_H_ +#define INSTRUMENTWINDOWTYPES_H_ + +class InstrumentWindowTypes { + +public: + enum SurfaceType { + FULL3D = 0, + CYLINDRICAL_X, + CYLINDRICAL_Y, + CYLINDRICAL_Z, + SPHERICAL_X, + SPHERICAL_Y, + SPHERICAL_Z, + SIDE_BY_SIDE, + RENDERMODE_SIZE + }; +}; +#endif /*INSTRUMENTWINDOWTYPES_H_*/ diff --git a/Code/Mantid/MantidPlot/src/MultiTabScriptInterpreter.cpp b/Code/Mantid/MantidPlot/src/MultiTabScriptInterpreter.cpp index 4d4936c46f5d9c90253bcc75e7f3afc89f8efc34..b86e38449d3b17a9d442f0d38002056477752db8 100644 --- a/Code/Mantid/MantidPlot/src/MultiTabScriptInterpreter.cpp +++ b/Code/Mantid/MantidPlot/src/MultiTabScriptInterpreter.cpp @@ -46,6 +46,9 @@ MultiTabScriptInterpreter::MultiTabScriptInterpreter(ScriptingEnv *env, m_tabWhitespaceCount(4), m_fontFamily(), m_codeFolding(false) { connect(this, SIGNAL(currentChanged(int)), this, SLOT(tabSelectionChanged(int))); + setTabsClosable(true); + connect(this, SIGNAL(tabCloseRequested(int)), + this, SLOT(closeTabAtIndex(int))); } /** @@ -271,7 +274,7 @@ void MultiTabScriptInterpreter::executeAll(const Script::ExecutionMode mode) { m_current->executeAll(mode); } -/* Execute the highlighted code from the current tab using the +/** Execute the highlighted code from the current tab using the * given execution mode * @param mode :: The mode used to execute */ @@ -280,6 +283,12 @@ void MultiTabScriptInterpreter::executeSelection( m_current->executeSelection(mode); } +/** + * Requests that the current script be aborted. The environment has + * to support this. + */ +void MultiTabScriptInterpreter::abortCurrentScript() { m_current->abort(); } + /** * Evaluate */ @@ -502,6 +511,22 @@ void MultiTabScriptInterpreter::showSelectFont() { // Private slots //-------------------------------------------- +/** + * Close a given tab + * @param index :: The tab index + */ +void MultiTabScriptInterpreter::closeTabAtIndex(int index) { + ScriptFileInterpreter *interpreter = interpreterAt(index); + if(!interpreter->shouldClose()) return; + emit tabClosing(index); + removeTab(index); + emit tabClosed(index); + const int nTabs = count(); + emit tabCountChanged(nTabs); + if (nTabs == 0) + emit lastTabClosed(); +} + /** * Close clicked tab. Qt cannot give the position where an action is clicked so * this just gets @@ -516,13 +541,15 @@ void MultiTabScriptInterpreter::closeClickedTab() { * modifications */ void MultiTabScriptInterpreter::currentEditorModified(bool state) { - static const QString modifiedLabel("*"); + static const QChar modifiedLabel('*'); const int index = currentIndex(); QString tabLabel = tabText(index); if (state) { - tabLabel += modifiedLabel; + tabLabel = modifiedLabel + tabLabel; } else { - tabLabel.chop(modifiedLabel.length()); + if(tabLabel.at(0) == modifiedLabel) { + tabLabel.remove(0, 1); + } } setTabText(index, tabLabel); } @@ -663,29 +690,13 @@ QString MultiTabScriptInterpreter::createTabTitle(const QString &filename) const { QString title; if (filename.isEmpty()) { - title = "New script"; + title = "Untitled"; } else { title = QFileInfo(filename).fileName(); } return title; } -/** - * Close a given tab - * @param index :: The tab index - */ -void MultiTabScriptInterpreter::closeTabAtIndex(int index) { - ScriptFileInterpreter *interpreter = interpreterAt(index); - interpreter->prepareToClose(); - emit tabClosing(index); - removeTab(index); - emit tabClosed(index); - const int nTabs = count(); - emit tabCountChanged(nTabs); - if (nTabs == 0) - emit lastTabClosed(); -} - /** * Close a tab at a given position * @param pos :: The tab at the given position diff --git a/Code/Mantid/MantidPlot/src/MultiTabScriptInterpreter.h b/Code/Mantid/MantidPlot/src/MultiTabScriptInterpreter.h index 504aa07ac46d173fa93e2f6f2285d71e6bcc619d..2056786b0c370760428285065b644b5ab5d56a07 100644 --- a/Code/Mantid/MantidPlot/src/MultiTabScriptInterpreter.h +++ b/Code/Mantid/MantidPlot/src/MultiTabScriptInterpreter.h @@ -152,6 +152,8 @@ public slots: void executeAll(const Script::ExecutionMode mode); /// Execute selection using the given mode void executeSelection(const Script::ExecutionMode mode); + /// Abort the current script + void abortCurrentScript(); /// Evaluate void evaluate(); /// Clear out any previous variable definitions in the current script @@ -189,6 +191,8 @@ public slots: void showSelectFont(); private slots: + /// Close a tab with a given index + void closeTabAtIndex(int index); /// Close clicked tab void closeClickedTab(); /// Current editor's modification status has changed @@ -211,8 +215,6 @@ private: void setTabTitle(QWidget *widget, const QString &filename); /// Returns the tab title for the given filename QString createTabTitle(const QString &filename) const; - /// Close a tab with a given index - void closeTabAtIndex(int index); /// Close a tab at a given position void closeTabAtPosition(const QPoint &pos); diff --git a/Code/Mantid/MantidPlot/src/PythonScript.cpp b/Code/Mantid/MantidPlot/src/PythonScript.cpp index 6396aef2af399c082fa1611402faaeb6306965c2..0d85107620fdbe0da8ba905c762713b412014ab9 100644 --- a/Code/Mantid/MantidPlot/src/PythonScript.cpp +++ b/Code/Mantid/MantidPlot/src/PythonScript.cpp @@ -45,6 +45,9 @@ namespace { + // Avoids a compiler warning about implicit 'const char *'->'char*' conversion under clang + #define STR_LITERAL(str) const_cast<char*>(str) + /** * A callback, set using PyEval_SetTrace, that is called by Python * to allow inspection into the current execution frame. It is currently @@ -59,9 +62,8 @@ namespace Q_UNUSED(arg); int retcode(0); if(event != PyTrace_LINE) return retcode; - std::string str1 = "lineNumberChanged"; - std::string str2 = "O i"; - PyObject_CallMethod(scriptObj,&str1[0],&str2[0], frame->f_code->co_filename, frame->f_lineno); + PyObject_CallMethod(scriptObj, STR_LITERAL("lineNumberChanged"), STR_LITERAL("O i"), + frame->f_code->co_filename, frame->f_lineno); return retcode; } @@ -78,8 +80,8 @@ namespace PythonScript::PythonScript(PythonScripting *env, const QString &name, const InteractionType interact, QObject * context) : Script(env, name, interact, context), m_pythonEnv(env), localDict(NULL), - stdoutSave(NULL), stderrSave(NULL), m_CodeFileObject(NULL), isFunction(false), m_isInitialized(false), - m_pathHolder(name), m_workspaceHandles() + stdoutSave(NULL), stderrSave(NULL), m_codeFileObject(NULL), m_threadID(-1), isFunction(false), + m_isInitialized(false), m_pathHolder(name), m_workspaceHandles() { initialize(name, context); } @@ -89,13 +91,15 @@ PythonScript::PythonScript(PythonScripting *env, const QString &name, const Inte */ PythonScript::~PythonScript() { - GlobalInterpreterLock pythonLock; + GlobalInterpreterLock lock; + this->abort(); observeAdd(false); observeAfterReplace(false); observePostDelete(false); observeADSClear(false); this->disconnect(); + Py_XDECREF(m_algorithmInThread); Py_XDECREF(localDict); } @@ -180,7 +184,7 @@ bool PythonScript::compilesToCompleteStatement(const QString & code) const */ void PythonScript::lineNumberChanged(PyObject *codeObject, int lineNo) { - if(codeObject == m_CodeFileObject) + if(codeObject == m_codeFileObject) { sendLineChangeSignal(getRealLineNo(lineNo), false); } @@ -342,7 +346,7 @@ QString PythonScript::constructSyntaxErrorStr(PyObject *syntaxError) QString text = m_pythonEnv->toString(textObject, true).trimmed(); int offset = static_cast<int>(m_pythonEnv->toLong(PyObject_GetAttrString(syntaxError, "offset"))); QString offsetMarker = QString(offset-1, ' ') + "^"; - msg = + msg = "File \"%1\", line %2\n" " %3\n" " %4\n" @@ -353,7 +357,7 @@ QString PythonScript::constructSyntaxErrorStr(PyObject *syntaxError) } else { - msg = + msg = "File \"%1\", line %2\n" "SyntaxError: %3"; msg = msg.arg(filename); @@ -373,15 +377,15 @@ QString PythonScript::constructSyntaxErrorStr(PyObject *syntaxError) * @param traceback A traceback object * @param root If true then this is the root of the traceback */ -void PythonScript::tracebackToMsg(QTextStream &msgStream, - PyTracebackObject* traceback, +void PythonScript::tracebackToMsg(QTextStream &msgStream, + PyTracebackObject* traceback, bool root) { if(traceback == NULL) return; msgStream << "\n "; if (root) msgStream << "at"; else msgStream << "caused by"; - + int lineno = traceback->tb_lineno; QString filename = QString::fromAscii(PyString_AsString(traceback->tb_frame->f_code->co_filename)); if(filename == identifier().c_str()) @@ -390,7 +394,7 @@ void PythonScript::tracebackToMsg(QTextStream &msgStream, sendLineChangeSignal(lineno, true); } - + msgStream << " line " << lineno << " in \'" << filename << "\'"; tracebackToMsg(msgStream, traceback->tb_next, false); } @@ -457,6 +461,10 @@ void PythonScript::initialize(const QString & name, QObject *context) GlobalInterpreterLock pythonlock; PythonScript::setIdentifier(name); setContext(context); + + PyObject *ialgorithm = PyObject_GetAttrString(PyImport_AddModule("mantid.api"), "IAlgorithm"); + m_algorithmInThread = PyObject_GetAttrString(ialgorithm, "_algorithmInThread"); + Py_INCREF(m_algorithmInThread); } @@ -619,11 +627,58 @@ QVariant PythonScript::evaluateImpl() return qret; } +/** + * On construction set a reference to a given value and at destruction reset it + * to its original + */ +struct TemporaryValue { + TemporaryValue(long & refVal, long tmp) : initial(refVal), ref(refVal){ + ref = tmp; + } + ~TemporaryValue() { + ref = initial; + } +private: + long initial; + long & ref; +}; + bool PythonScript::executeImpl() { + TemporaryValue holder(m_threadID, getThreadID()); return executeString(); } +void PythonScript::abortImpl() +{ + // The current thread for this script could be + // in one of two states: + // 1. A C++ algorithm is being executed so it must be + // interrupted using algorithm.cancel() + // 2. Pure Python is executing and can be interrupted + // with a KeyboardInterrupt exception + // In both situations we issue a KeyboardInterrupt just in case the algorithm + // hasn't implemented cancel() checking so that when control returns the Python the + // interrupt should be picked up. + GlobalInterpreterLock lock; + m_pythonEnv->raiseAsyncException(m_threadID, PyExc_KeyboardInterrupt); + PyObject *curAlg = PyObject_CallFunction(m_algorithmInThread, STR_LITERAL("l"), m_threadID); + if(curAlg && curAlg != Py_None){ + PyObject_CallMethod(curAlg, STR_LITERAL("cancel"), STR_LITERAL("")); + } +} + +/** + * The value returned here is only valid when called by a valid + * thread that has a valid Python threadstate + * @return A long int giving a unique ID for the thread + */ +long PythonScript::getThreadID() +{ + GlobalInterpreterLock lock; + return PyThreadState_Get()->thread_id; +} + /// Performs the call to Python bool PythonScript::executeString() @@ -644,6 +699,11 @@ bool PythonScript::executeString() if(!result) { emit_error(); + // If a script was aborted we both raise a KeyboardInterrupt and + // call Algorithm::cancel to make sure we capture it. The doubling + // can leave an interrupt in the pipeline so we clear it was we've + // got the error info out + m_pythonEnv->raiseAsyncException(m_threadID, NULL); } else { @@ -817,14 +877,14 @@ PyObject *PythonScript::compileToByteCode(bool for_eval) { } - if(success) + if(success) { - m_CodeFileObject = ((PyCodeObject*)(compiledCode))->co_filename; + m_codeFileObject = ((PyCodeObject*)(compiledCode))->co_filename; } else { compiledCode = NULL; - m_CodeFileObject = NULL; + m_codeFileObject = NULL; } return compiledCode; } @@ -876,7 +936,7 @@ void PythonScript::clearADSHandle() // i.e. the postfix operator this->deletePythonReference(*(itr++)); } - + assert(m_workspaceHandles.empty()); } diff --git a/Code/Mantid/MantidPlot/src/PythonScript.h b/Code/Mantid/MantidPlot/src/PythonScript.h index b81df7bb91ad6a4c053a6792c8aedf792a7694ac..460dbe9e9faa9edb93c59fa762ef3f9f8e7dda08 100644 --- a/Code/Mantid/MantidPlot/src/PythonScript.h +++ b/Code/Mantid/MantidPlot/src/PythonScript.h @@ -163,6 +163,10 @@ private: QVariant evaluateImpl(); /// Execute the current code and return a boolean indicating success/failure bool executeImpl(); + /// Request that this script be aborted + void abortImpl(); + /// Get the value of the Python thread ID when a script is executed + long getThreadID(); /// Performs the call to Python from a string bool executeString(); @@ -173,6 +177,7 @@ private: /// Compile to bytecode PyObject * compileToByteCode(bool for_eval=true); + // ---------------------------- Variable reference --------------------------------------------- /// Listen to add notifications from the ADS void addHandle(const std::string& wsName, const Mantid::API::Workspace_sptr ws); @@ -192,7 +197,10 @@ private: PythonScripting * m_pythonEnv; PyObject *localDict, *stdoutSave, *stderrSave; - PyObject *m_CodeFileObject; + PyObject *m_codeFileObject; + long m_threadID; ///< Python thread id + /// A reference to the IAlgorithm._algorithmInThread static method + PyObject * m_algorithmInThread; bool isFunction; QString fileName; bool m_isInitialized; diff --git a/Code/Mantid/MantidPlot/src/PythonScripting.cpp b/Code/Mantid/MantidPlot/src/PythonScripting.cpp index c8b3622df2ae5e72a450e22b47d4ced26b25d37b..f99aa4fa39d43e6615e701196317283c9bc60406 100644 --- a/Code/Mantid/MantidPlot/src/PythonScripting.cpp +++ b/Code/Mantid/MantidPlot/src/PythonScripting.cpp @@ -50,6 +50,9 @@ #include "sipAPI_qti.h" +// Avoids a compiler warning about implicit 'const char *'->'char*' conversion under clang +#define STR_LITERAL(str) const_cast<char*>(str) + // Function is defined in a sip object file that is linked in later. There is no header file // so this is necessary extern "C" void init_qti(); @@ -157,13 +160,13 @@ bool PythonScripting::start() // Assume this is called at startup by the the main thread so no GIL required...yet //Keep a hold of the globals, math and sys dictionary objects - PyObject *pymodule = PyImport_AddModule("__main__"); - if( !pymodule ) + PyObject *mainmod = PyImport_AddModule("__main__"); + if( !mainmod ) { finalize(); return false; } - m_globals = PyModule_GetDict(pymodule); + m_globals = PyModule_GetDict(mainmod); if( !m_globals ) { finalize(); @@ -172,14 +175,18 @@ bool PythonScripting::start() //Create a new dictionary for the math functions m_math = PyDict_New(); - - pymodule = PyImport_ImportModule("sys"); - m_sys = PyModule_GetDict(pymodule); + // Keep a hold of the sys dictionary for accessing stdout/stderr + PyObject *sysmod = PyImport_ImportModule("sys"); + m_sys = PyModule_GetDict(sysmod); if( !m_sys ) { finalize(); return false; } + // Set a smaller check interval so that it takes fewer 'ticks' to respond to a KeyboardInterrupt + // The choice of 5 is really quite arbitrary + PyObject_CallMethod(sysmod, STR_LITERAL("setcheckinterval"), STR_LITERAL("i"), 5); + Py_DECREF(sysmod); // Our use of the IPython console requires that we use the v2 api for these PyQt types // This has to be set before the very first import of PyQt (which happens in init_qti) @@ -187,14 +194,14 @@ bool PythonScripting::start() //Embedded qti module needs sip definitions initializing before it can be used init_qti(); - pymodule = PyImport_ImportModule("_qti"); - if( pymodule ) + PyObject *qtimod = PyImport_ImportModule("_qti"); + if( qtimod ) { - PyDict_SetItemString(m_globals, "_qti", pymodule); - PyObject *qti_dict = PyModule_GetDict(pymodule); + PyDict_SetItemString(m_globals, "_qti", qtimod); + PyObject *qti_dict = PyModule_GetDict(qtimod); setQObject(d_parent, "app", qti_dict); PyDict_SetItemString(qti_dict, "mathFunctions", m_math); - Py_DECREF(pymodule); + Py_DECREF(qtimod); } else { @@ -332,6 +339,16 @@ long PythonScripting::toLong(PyObject *object, bool decref) return cvalue; } +/** + * @brief Raise an exception in the target thread. The GIL must be held + * @param id The associated Python thread id + * @param exc The Python exception type + */ +void PythonScripting::raiseAsyncException(long id, PyObject *exc) +{ + PyThreadState_SetAsyncExc(id, exc); +} + bool PythonScripting::setQObject(QObject *val, const char *name, PyObject *dict) { diff --git a/Code/Mantid/MantidPlot/src/PythonScripting.h b/Code/Mantid/MantidPlot/src/PythonScripting.h index bddc4d55bc19beef3c804df854d76bdf5ac1481f..f34b0ae5c5f77d31dba99f367870135f97d00c4f 100644 --- a/Code/Mantid/MantidPlot/src/PythonScripting.h +++ b/Code/Mantid/MantidPlot/src/PythonScripting.h @@ -44,7 +44,7 @@ class PythonScripting: public ScriptingEnv { Q_OBJECT - + public: /// Factory function static ScriptingEnv *constructor(ApplicationWindow *parent); @@ -69,6 +69,8 @@ public: // Python supports progress monitoring bool supportsProgressReporting() const { return true; } + /// Does this support abort requests? + bool supportsAbortRequests() const { return true; } /// Return a string represenation of the given object QString toString(PyObject *object, bool decref = false); @@ -78,6 +80,8 @@ public: PyObject * toPyList(const QStringList & items); /// Returns an integer representation of the object. No check is performed to see if it is an integer long toLong(PyObject *object, bool decref = false); + /// Raise an asynchronous exception in the given thread + void raiseAsyncException(long id, PyObject *exc); ///Return a list of file extensions for Python const QStringList fileExtensions() const; @@ -113,7 +117,7 @@ private: void shutdown(); /// Run execfile on a given file bool loadInitFile(const QString &path); - + private: /// The global dictionary PyObject *m_globals; diff --git a/Code/Mantid/MantidPlot/src/Script.cpp b/Code/Mantid/MantidPlot/src/Script.cpp index f2c76de403dba31759ee622fe130ca30b83a5e49..7a0a4376d36396d0638e1b871c8e3e9378e275e5 100644 --- a/Code/Mantid/MantidPlot/src/Script.cpp +++ b/Code/Mantid/MantidPlot/src/Script.cpp @@ -5,7 +5,7 @@ Copyright : (C) 2006 by Knut Franke Email (use @ for *) : knut.franke*gmx.de Description : Implementations of generic scripting classes - + ***************************************************************************/ /*************************************************************************** @@ -93,7 +93,7 @@ Script::Script(ScriptingEnv *env, const QString &name, m_env->incref(); connect(this, SIGNAL(started(const QString &)), this, SLOT(setIsRunning())); - /** On some systems it has been observed that + /** On some systems it has been observed that * after a script has run that has created & deleted * workspaces the OS does not report all of the * memory as freed. In actual fact the next allocation will @@ -117,7 +117,7 @@ Script::~Script() * Sets a new name for the script */ void Script::setIdentifier(const QString & name) -{ +{ m_name = name.toStdString(); } @@ -158,9 +158,15 @@ QFuture<bool> Script::executeAsync(const ScriptCode & code) return asyncScript->start(); } +/// Request that this script be aborted +void Script::abort() +{ + if(isExecuting()) this->abortImpl(); +} + /** * Asks Mantid to release all free memory. - */ + */ void Script::releaseFreeMemory() { Mantid::API::MemoryManager::Instance().releaseFreeMemory(); @@ -196,4 +202,3 @@ QString Script::normaliseLineEndings(QString text) const text = text.replace(QRegExp("\\r"), QString("\n")); return text; } - diff --git a/Code/Mantid/MantidPlot/src/Script.h b/Code/Mantid/MantidPlot/src/Script.h index 94593f6b8e9aba7803c890febe07359efe4e22fd..a934a7ac0df638a36bb4ffff0220312289a90db2 100644 --- a/Code/Mantid/MantidPlot/src/Script.h +++ b/Code/Mantid/MantidPlot/src/Script.h @@ -93,7 +93,7 @@ class Script : public QObject void redirectStdOut(bool on) { m_redirectOutput = on; } /// Create a list of keywords for the code completion API - virtual void generateAutoCompleteList() {}; + virtual void generateAutoCompleteList() {} // Does the code compile to a complete statement, i.e no more input is required virtual bool compilesToCompleteStatement(const QString & code) const = 0; @@ -106,6 +106,8 @@ public slots: bool execute(const ScriptCode & code); /// Execute the code asynchronously, returning immediately after the execution has started QFuture<bool> executeAsync(const ScriptCode & code); + /// Request that execution of this script be aborted + void abort(); /// Asks Mantid to release all free memory void releaseFreeMemory(); @@ -113,13 +115,13 @@ public slots: void setNotExecuting(); /// Sets the execution mode to Running to indicate something is running void setIsRunning(); - + // local variables virtual bool setQObject(QObject*, const char*) { return false; } virtual bool setInt(int, const char*) { return false; } virtual bool setDouble(double, const char*) { return false; } virtual void clearLocals() {} - + signals: /// A signal defining when this script has started executing void started(const QString & message); @@ -133,7 +135,7 @@ signals: void currentLineChanged(int lineno, bool error); // Signal that new keywords are available void autoCompleteListGenerated(const QStringList & keywords); - + protected: /// Return the true line number by adding the offset inline int getRealLineNo(const int codeLine) const { return codeLine + m_code.offset(); } @@ -147,6 +149,8 @@ protected: virtual QVariant evaluateImpl() = 0; /// Execute the Code, returning false on an error / exception. virtual bool executeImpl() = 0; + /// Implementation of the abort request + virtual void abortImpl() = 0; private: /** diff --git a/Code/Mantid/MantidPlot/src/ScriptFileInterpreter.cpp b/Code/Mantid/MantidPlot/src/ScriptFileInterpreter.cpp index 43ee625b55745e7bd2c3a23e6b38251262147963..56cf2550086dc5d5436534f53a4e6bd955e8e569 100644 --- a/Code/Mantid/MantidPlot/src/ScriptFileInterpreter.cpp +++ b/Code/Mantid/MantidPlot/src/ScriptFileInterpreter.cpp @@ -2,18 +2,19 @@ #include "ScriptOutputDisplay.h" #include "ScriptingEnv.h" #include "MantidQtMantidWidgets/ScriptEditor.h" -#include <iostream> #include <QAction> #include <QFileInfo> -#include <QMessageBox> #include <QMenu> +#include <QMessageBox> #include <QPushButton> #include <QVBoxLayout> -#include <q3cstring.h> #include <qscilexer.h> #include <stdexcept> +//----------------------------------------------------------------------------- +// ScriptFileInterpreter +//----------------------------------------------------------------------------- /** * Construct a widget @@ -38,43 +39,13 @@ ScriptFileInterpreter::~ScriptFileInterpreter() } /** - * Make sure the widget is ready to be deleted, i.e. saved etc + * Check if the interpreter is running and the script is saved + * @return True if the interpreter should be closed */ -void ScriptFileInterpreter::prepareToClose() +bool ScriptFileInterpreter::shouldClose() { - if( !isScriptModified() ) return; - - QMessageBox msgBox(this); - msgBox.setModal(true); - msgBox.setWindowTitle("MantidPlot"); - msgBox.setText(tr("The current script has been modified.")); - msgBox.setInformativeText(tr("Save changes?")); - msgBox.addButton(QMessageBox::Save); - QPushButton *saveAsButton = msgBox.addButton("Save As...", QMessageBox::AcceptRole); - msgBox.addButton(QMessageBox::Discard); - int ret = msgBox.exec(); - - try - { - if( msgBox.clickedButton() == saveAsButton ) - { - m_editor->saveAs(); - } - else if( ret == QMessageBox::Save ) - { - m_editor->saveToCurrentFile(); - } - else - { - m_editor->setModified(false); - } - } - //Catch cancelling save dialogue - catch( ScriptEditor::SaveCancelledException& sce ) - { - UNUSED_ARG(sce); - m_editor->setModified(false); - } + ScriptCloseDialog dialog(*this, this); + return dialog.shouldScriptClose(); } /// Convert tabs in selection to spaces @@ -82,10 +53,10 @@ void ScriptFileInterpreter::tabsToSpaces() { int selFromLine, selFromInd, selToLine, selToInd; - m_editor->getSelection(&selFromLine, &selFromInd, &selToLine, &selToInd); + m_editor->getSelection(&selFromLine, &selFromInd, &selToLine, &selToInd); if(selFromLine == -1) m_editor->selectAll(); - + QString text = m_editor->selectedText(); // Use the tab space count for each converted character @@ -103,10 +74,10 @@ void ScriptFileInterpreter::spacesToTabs() { int selFromLine, selFromInd, selToLine, selToInd; - m_editor->getSelection(&selFromLine, &selFromInd, &selToLine, &selToInd); + m_editor->getSelection(&selFromLine, &selFromInd, &selToLine, &selToInd); if(selFromLine == -1) m_editor->selectAll(); - + QString text = m_editor->selectedText(); // Use the tab space count for each converted characters @@ -124,10 +95,10 @@ void ScriptFileInterpreter::setFont(const QString &fontFamily) { // This needs to check if the font exists and use default if not QFontDatabase database; - + // Select saved choice. If not available, use current font QString fontToUse = m_editor->lexer()->defaultFont().family(); - + if(database.families().contains(fontFamily)) fontToUse = fontFamily; @@ -143,9 +114,9 @@ void ScriptFileInterpreter::setFont(const QString &fontFamily) QFont font = m_editor->lexer()->font(count); font.setFamily(fontToUse); m_editor->lexer()->setFont(font,count); - + count++; - } + } } /// Toggle replacing tabs with whitespace @@ -164,11 +135,11 @@ void ScriptFileInterpreter::setTabWhitespaceCount(int count) void ScriptFileInterpreter::toggleWhitespace(bool state) { m_editor->setEolVisibility(state); - - if(state) + + if(state) m_editor->setWhitespaceVisibility(QsciScintilla::WhitespaceVisibility::WsVisible); else - m_editor->setWhitespaceVisibility(QsciScintilla::WhitespaceVisibility::WsInvisible); + m_editor->setWhitespaceVisibility(QsciScintilla::WhitespaceVisibility::WsInvisible); } /// Comment a block of code @@ -185,11 +156,11 @@ void ScriptFileInterpreter::uncomment() void ScriptFileInterpreter::toggleComment(bool addComment) { - // Get selected text + // Get selected text std::string whitespaces (" \t\f\r"); int selFromLine, selFromInd, selToLine, selToInd; - - m_editor->getSelection(&selFromLine, &selFromInd, &selToLine, &selToInd); + + m_editor->getSelection(&selFromLine, &selFromInd, &selToLine, &selToInd); // Expand selection to first character on start line to the end char on second line. if(selFromLine == -1) @@ -197,20 +168,20 @@ void ScriptFileInterpreter::toggleComment(bool addComment) m_editor->getCursorPosition(&selFromLine, &selFromInd); selToLine = selFromLine; } - + // For each line, select it, copy the line into a new string minus the comment QString replacementText = ""; // If it's adding comment, check all lines to find the lowest index for a non space character int minInd = -1; if(addComment) - { + { for(int i=selFromLine;i<=selToLine;++i) { - std::string thisLine = m_editor->text(i).toUtf8().constData(); + std::string thisLine = m_editor->text(i).toUtf8().constData(); int lineFirstChar = static_cast<int>(thisLine.find_first_not_of(whitespaces + "\n")); - - if(minInd == -1 || (lineFirstChar != -1 && lineFirstChar < minInd)) + + if(minInd == -1 || (lineFirstChar != -1 && lineFirstChar < minInd)) { minInd = lineFirstChar; } @@ -220,21 +191,21 @@ void ScriptFileInterpreter::toggleComment(bool addComment) for(int i=selFromLine;i<=selToLine;++i) { std::string thisLine = m_editor->text(i).toUtf8().constData(); - + int lineFirstChar = static_cast<int>(thisLine.find_first_not_of(whitespaces + "\n")); - + if(lineFirstChar != -1) - { - if(addComment) - { + { + if(addComment) + { thisLine.insert(minInd,"#"); } else - { + { // Check that the first character is a # if(thisLine[lineFirstChar] == '#') // Remove the comment, or ignore to add as is { - thisLine = thisLine.erase(lineFirstChar,1); + thisLine = thisLine.erase(lineFirstChar,1); } } } @@ -242,7 +213,7 @@ void ScriptFileInterpreter::toggleComment(bool addComment) replacementText = replacementText + QString::fromStdString(thisLine); } - m_editor->setSelection(selFromLine,0,selToLine, m_editor->lineLength(selToLine)); + m_editor->setSelection(selFromLine,0,selToLine, m_editor->lineLength(selToLine)); replaceSelectedText(m_editor, replacementText); m_editor->setCursorPosition(selFromLine,selFromInd); } @@ -252,8 +223,8 @@ void ScriptFileInterpreter::toggleComment(bool addComment) // using an older version(2.4.6) of the library missing the method, and too close to code freeze to update. inline void ScriptFileInterpreter::replaceSelectedText(const ScriptEditor *editor, const QString &text) { - int UTF8_CodePage = 65001; - const char *b = (( editor->SCI_GETCODEPAGE == UTF8_CodePage) ? text.utf8().constData() : text.latin1()); + int UTF8_CodePage = 65001; + const char *b = (( editor->SCI_GETCODEPAGE == UTF8_CodePage) ? text.utf8().constData() : text.latin1()); editor->SendScintilla( editor->SCI_REPLACESEL, b ); } @@ -444,6 +415,13 @@ void ScriptFileInterpreter::executeSelection(const Script::ExecutionMode mode) } } +/** + * The environment has to support this behaviour or is does nothing + */ +void ScriptFileInterpreter::abort() { + m_runner->abort(); +} + /** */ void ScriptFileInterpreter::clearVariables() @@ -593,3 +571,88 @@ void ScriptFileInterpreter::executeCode(const ScriptCode & code, const Script::E } } +//----------------------------------------------------------------------------- +// ScriptCloseDialog +//----------------------------------------------------------------------------- +ScriptCloseDialog::ScriptCloseDialog(ScriptFileInterpreter &interpreter, + QWidget *parent) + : QWidget(parent), m_msgBox(new QMessageBox(this)), m_interpreter(interpreter) { + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(m_msgBox); + setLayout(layout); +} + +/** + * Raise the the dialog as modal if necessary and ask the user + * if the script should close + * @return True or False depending on whether the script should be closed + */ +bool ScriptCloseDialog::shouldScriptClose() { + const bool executing(m_interpreter.isExecuting()), + modified(m_interpreter.isScriptModified()); + // Is the dialog even necessary? + if(!modified && !executing) return true; + + m_msgBox->setModal(true); + m_msgBox->setWindowTitle("MantidPlot"); + m_msgBox->setIcon(QMessageBox::Question); + + QString filename = m_interpreter.filename(); + bool close(true); + if(modified) { + m_msgBox->addButton(QMessageBox::Save); + m_msgBox->addButton(QMessageBox::Cancel); + m_msgBox->addButton(QMessageBox::Discard); + m_msgBox->setDefaultButton(QMessageBox::Save); + if(filename.isEmpty()) { + m_msgBox->setText(QString("Save changes before closing?")); + m_msgBox->button(QMessageBox::Save)->setText("Save As"); + } else { + m_msgBox->setText(QString("Save changes to '%1' before closing?").arg(filename)); + } + if(executing) { + m_msgBox->setInformativeText(tr("The script will be aborted.")); + } + // show dialog + int ret = m_msgBox->exec(); + try { + switch(ret) + { + case QMessageBox::Save: + m_interpreter.saveToCurrentFile(); + close = true; + break; + case QMessageBox::Discard: + close = true; + break; + default: + close = false; + break; + } + } + catch(ScriptEditor::SaveCancelledException &) { + close = false; + } + } + else if(executing) { + if(filename.isEmpty()) { + m_msgBox->setText(tr("Abort and close?")); + } else { + m_msgBox->setText(tr("Abort '%1' and close?").arg(m_interpreter.filename())); + } + m_msgBox->addButton(QMessageBox::Abort); + m_msgBox->addButton(QMessageBox::Cancel); + m_msgBox->setDefaultButton(QMessageBox::Abort); + int ret = m_msgBox->exec(); + switch(ret) { + case QMessageBox::Abort: + m_interpreter.m_runner->abort(); + close = true; + break; + default: + close = false; + break; + } + } + return close; +} diff --git a/Code/Mantid/MantidPlot/src/ScriptFileInterpreter.h b/Code/Mantid/MantidPlot/src/ScriptFileInterpreter.h index 705efe9a4ebe730cd9e051e4285ef079cbc0ee3c..ffe90efcc0cdb0fb7a010d596dad4b6569477378 100644 --- a/Code/Mantid/MantidPlot/src/ScriptFileInterpreter.h +++ b/Code/Mantid/MantidPlot/src/ScriptFileInterpreter.h @@ -1,21 +1,25 @@ -#ifndef SCRIPTRUNNERWIDGET_H_ -#define SCRIPTRUNNERWIDGET_H_ +#ifndef SCRIPTFILEINTERPRETER_H_ +#define SCRIPTFILEINTERPRETER_H_ #include "Script.h" -#include <QWidget> -#include <QTextEdit> #include <QPoint> #include <QSplitter> #include <QStatusBar> +#include <QTextEdit> //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- +class QMessageBox; +class ScriptCloseDialog; class ScriptingEnv; class ScriptEditor; class ScriptOutputDisplay; +//----------------------------------------------------------------------------- +// ScriptFileInterpreter +//----------------------------------------------------------------------------- /** * Defines a widget that uses a ScriptEditor, a Script object and a * text display widget to give a single widget that can @@ -31,8 +35,8 @@ public: ScriptFileInterpreter(QWidget *parent = NULL, const QString & settingsGroup = ""); /// Destroy the object ~ScriptFileInterpreter(); - /// Make sure we are in a safe state to delete the widget - virtual void prepareToClose(); + /// Determine if the script is ready to be closed + virtual bool shouldClose(); /// Setup from a script environment virtual void setup(const ScriptingEnv & environ, const QString & identifier); @@ -84,13 +88,15 @@ public slots: virtual void executeAll(const Script::ExecutionMode mode = Script::Asynchronous); /// Execute the current selection virtual void executeSelection(const Script::ExecutionMode mode = Script::Asynchronous); + /// Request that the script execution be aborted + virtual void abort(); /// Clear the script variable cache virtual void clearVariables(); /// Toggles the progress reports on/off virtual void toggleProgressReporting(bool state); /// Toggles the code folding on/off - virtual void toggleCodeFolding(bool state); + virtual void toggleCodeFolding(bool state); /// Toggles the whitespace visibility virtual void toggleWhitespace(bool state); /// Toggle replacing tabs with whitespace @@ -123,12 +129,14 @@ private slots: void setStoppedStatus(); private: + friend class ScriptCloseDialog; + Q_DISABLE_COPY(ScriptFileInterpreter) void setupChildWidgets(); void setupEditor(const ScriptingEnv & environ, const QString & identifier); void setupScriptRunner(const ScriptingEnv & environ, const QString & identifier); - + bool readFileIntoEditor(const QString & filename); void executeCode(const ScriptCode & code, const Script::ExecutionMode mode); @@ -157,60 +165,78 @@ public: NullScriptFileInterpreter() : ScriptFileInterpreter(NULL) {} - /// Make sure we are in a safe state to delete the widget - void prepareToClose() {}; - /// Setup from a script environment - void setup(const ScriptingEnv &, const QString &) {}; + /// Does nothing + bool shouldClose() { return false; } + /// Does nothing + void setup(const ScriptingEnv &, const QString &) {} - /// Return the filename of the script in the editor + /// Does nothing QString filename() const { return QString(); } - /// Has the script text been modified + /// Does nothing bool isScriptModified() const { return false; } private slots: - /// Undo + /// Does nothing void undo() {} - /// Redo + /// Does nothing void redo() {} - /// Copy from the editor + /// Does nothing void copy() {} - /// Cut from the editor + /// Does nothing void cut() {} - /// Paste into the editor + /// Does nothing void paste() {} - /// Find in editor - void showFindReplaceDialog() {}; + /// Does nothing + void showFindReplaceDialog() {} - /// Execute the whole script. + /// Does nothing virtual void executeAll(const Script::ExecutionMode) {} - /// Execute the current selection + /// Does nothing virtual void executeSelection(const Script::ExecutionMode) {} - /// Clear the script variable cache + /// Does nothing + virtual void abort() {} + /// Does nothing virtual void clearVariables() {} - /// Zoom in on script + /// Does nothing virtual void zoomInOnScript() {} - /// Zoom out on script + /// Does nothing virtual void zoomOutOnScript() {} - /// Toggles the progress reports on/off + /// Does nothing virtual void toggleProgressReporting(bool) {} - /// Toggles the code folding on/off + /// Does nothing virtual void toggleCodeFolding(bool) {} - - /// Save to the currently stored name + /// Does nothing virtual void saveToCurrentFile() {} - /// Save to a different name + /// Does nothing virtual void saveAs() {} - /// Save to the given filename - virtual void saveScript(const QString &) {}; - /// Save the current output - virtual void saveOutput(const QString &) {}; - /// Print the script + /// Does nothing + virtual void saveScript(const QString &) {} + /// Does nothing + virtual void saveOutput(const QString &) {} + /// Does nothing virtual void printScript() {} - /// Print the script + /// Does nothing virtual void printOutput() {} +}; +//----------------------------------------------------------------------------- +// ScriptCloseDialog - Specific message for closing the widget +//----------------------------------------------------------------------------- +class ScriptCloseDialog : public QWidget +{ + Q_OBJECT + +public: + ScriptCloseDialog(ScriptFileInterpreter &interpreter, + QWidget *parent = NULL); + + bool shouldScriptClose(); + +private: + QMessageBox *m_msgBox; + ScriptFileInterpreter &m_interpreter; }; -#endif /* SCRIPTRUNNERWIDGET_H_ */ +#endif /* SCRIPTFILEINTERPRETER_H_ */ diff --git a/Code/Mantid/MantidPlot/src/ScriptingEnv.h b/Code/Mantid/MantidPlot/src/ScriptingEnv.h index 9499be40f0d92ee7e3ddc474315fd0f3e61b6b16..87f97b31ae119edb0d11fcadcf3594f9e20187ad 100644 --- a/Code/Mantid/MantidPlot/src/ScriptingEnv.h +++ b/Code/Mantid/MantidPlot/src/ScriptingEnv.h @@ -2,12 +2,12 @@ File : ScriptingEnv.h Project : QtiPlot -------------------------------------------------------------------- - Copyright : (C) 2006 by Ion Vasilief, + Copyright : (C) 2006 by Ion Vasilief, Tilman Hoener zu Siederdissen, Knut Franke Email (use @ for *) : ion_vasilief*yahoo.fr, thzs*gmx.net Description : Scripting abstraction layer - + ***************************************************************************/ /*************************************************************************** @@ -86,7 +86,9 @@ class ScriptingEnv : public QObject virtual bool supportsEvaluation() { return false; } /// Is progress reporting supported virtual bool supportsProgressReporting() const { return false; } - /// Create a code lexer for this environment, can be NULL. Ownership of a created object + /// Does this support abort requests? + virtual bool supportsAbortRequests() const { return false; } + /// Create a code lexer for this environment, can be NULL. Ownership of a created object /// is transferred to the caller. virtual QsciLexer * createCodeLexer() const { return NULL; } @@ -99,14 +101,14 @@ public slots: virtual bool setInt(int, const char*) { return false; } /// Set a reference to a double in the global scope virtual bool setDouble(double, const char*) { return false; } - + /// Clear the global environment. What exactly happens depends on the implementation. virtual void clear() {} /// Increase the reference count. This should only be called by Scripted and Script to avoid memory leaks. void incref(); /// Decrease the reference count. This should only be called by Scripted and Script to avoid segfaults. void decref(); - + signals: /// Starting void starting(); @@ -156,7 +158,7 @@ public: static QStringList languages(); /// Return the number of available implementations. static int numLanguages(); - + private: typedef ScriptingEnv*(*ScriptingEnvConstructor)(ApplicationWindow*); typedef struct { diff --git a/Code/Mantid/MantidPlot/src/ScriptingWindow.cpp b/Code/Mantid/MantidPlot/src/ScriptingWindow.cpp index 90c4d5a4943d4906dbc121917aad8e77e8b213be..4e93909856b8660dd43060dca27e9470a3a9b79a 100755 --- a/Code/Mantid/MantidPlot/src/ScriptingWindow.cpp +++ b/Code/Mantid/MantidPlot/src/ScriptingWindow.cpp @@ -123,7 +123,7 @@ void ScriptingWindow::readSettings() { m_manager->m_replaceTabs = settings.value("ReplaceTabs", true).toBool(); m_manager->m_tabWhitespaceCount = settings.value("TabWhitespaceCount", 4).toInt(); - m_manager->m_fontFamily = settings.value("ScriptFontFamily", "").toString(); + m_manager->m_fontFamily = settings.value("ScriptFontFamily", "").toString(); openPreviousTabs(settings.value("/PreviousFiles", "").toStringList()); settings.endGroup(); @@ -245,11 +245,10 @@ void ScriptingWindow::populateExecMenu() { m_runMenu->clear(); m_runMenu->addAction(m_execSelect); m_runMenu->addAction(m_execAll); - m_runMenu->addSeparator(); - + m_runMenu->addAction(m_abortCurrent); + m_runMenu->addSeparator(); m_runMenu->addAction(m_clearScriptVars); - m_runMenu->addSeparator(); m_execModeMenu->clear(); @@ -315,23 +314,39 @@ void ScriptingWindow::setMenuStates(int ntabs) { m_runMenu->setEnabled(tabsOpen); } +/** + * Set the state of the execution actions depending on the flag + * @param off :: If the true the items are disabled, otherwise the are enabled + */ +void ScriptingWindow::setEditActionsDisabled(bool off) { + auto actions = m_editMenu->actions(); + foreach (QAction *action, actions) { + if(strcmp("Find", action->name()) != 0) { + action->setDisabled(off); + } + } +} + /** * Set the state of the execution actions/menu depending on the flag - * @param state :: If the true the items are enabled, otherwise the are disabled + * @param off :: If the true the items are disabled, otherwise the are enabled */ -void ScriptingWindow::setEditActionsDisabled(bool state) { - m_editMenu->setDisabled(state); +void ScriptingWindow::setExecutionActionsDisabled(bool off) { + m_execSelect->setDisabled(off); + m_execAll->setDisabled(off); + m_execModeMenu->setDisabled(off); + m_clearScriptVars->setDisabled(off); + // Abort should be opposite + setAbortActionsDisabled(!off); } /** * Set the state of the execution actions/menu depending on the flag - * @param state :: If the true the items are enabled, otherwise the are disabled + * @param off :: If the true the items are disabled else they are enabled */ -void ScriptingWindow::setExecutionActionsDisabled(bool state) { - m_execSelect->setDisabled(state); - m_execAll->setDisabled(state); - m_execModeMenu->setDisabled(state); - m_runMenu->setDisabled(state); +void ScriptingWindow::setAbortActionsDisabled(bool off) { + if(!shouldEnableAbort()) off = true; + m_abortCurrent->setDisabled(off); } /** @@ -360,6 +375,13 @@ void ScriptingWindow::executeSelection() { m_manager->executeSelection(this->getExecutionMode()); } +/** + * Ask the manager to abort the script execution for the current script. + */ +void ScriptingWindow::abortCurrent() { + m_manager->abortCurrentScript(); +} + /** */ void ScriptingWindow::clearScriptVariables() { @@ -397,6 +419,63 @@ void ScriptingWindow::acceptCloseEvent(const bool value) { m_acceptClose = value; } +//------------------------------------------- +// Protected non-slot member functions +//------------------------------------------- +/** + * Accept a custom event and in this case test if it is a ScriptingChangeEvent + * @param event :: The custom event + */ +void ScriptingWindow::customEvent(QEvent *event) { + if (!m_manager->isExecuting() && event->type() == SCRIPTING_CHANGE_EVENT) { + ScriptingChangeEvent *sce = static_cast<ScriptingChangeEvent *>(event); + setWindowTitle("MantidPlot: " + sce->scriptingEnv()->languageName() + + " Window"); + } +} + +/** + * Accept a drag enter event and selects whether to accept the action + * @param de :: The drag enter event + */ +void ScriptingWindow::dragEnterEvent(QDragEnterEvent *de) { + const QMimeData *mimeData = de->mimeData(); + if (mimeData->hasUrls()) { + if (extractPyFiles(mimeData->urls()).size() > 0) { + de->acceptProposedAction(); + } + } +} + +/** + * Accept a drag move event and selects whether to accept the action + * @param de :: The drag move event + */ +void ScriptingWindow::dragMoveEvent(QDragMoveEvent *de) { + const QMimeData *mimeData = de->mimeData(); + if (mimeData->hasUrls()) { + if (extractPyFiles(mimeData->urls()).size() > 0) { + de->accept(); + } + } +} + +/** + * Accept a drag drop event and process the data appropriately + * @param de :: The drag drop event + */ +void ScriptingWindow::dropEvent(QDropEvent *de) { + const QMimeData *mimeData = de->mimeData(); + if (mimeData->hasUrls()) { + QStringList filenames = extractPyFiles(mimeData->urls()); + de->acceptProposedAction(); + + for (int i = 0; i < filenames.size(); ++i) { + m_manager->openInNewTab(filenames[i]); + } + } +} + //------------------------------------------- // Private non-slot member functions //------------------------------------------- @@ -570,6 +649,13 @@ void ScriptingWindow::initExecMenuActions() { << Qt::CTRL + Qt::SHIFT + Qt::Key_Enter; m_execAll->setShortcuts(shortcuts); + m_abortCurrent = new QAction(tr("A&bort"), this); + connect(m_abortCurrent, SIGNAL(triggered()), this, SLOT(abortCurrent())); + shortcuts.clear(); + shortcuts << Qt::CTRL + Qt::Key_D; + m_abortCurrent->setShortcuts(shortcuts); + setAbortActionsDisabled(false); + m_clearScriptVars = new QAction(tr("&Clear Variables"), this); connect(m_clearScriptVars, SIGNAL(triggered()), this, SLOT(clearScriptVariables())); @@ -666,69 +752,36 @@ void ScriptingWindow::initHelpMenuActions() { connect(m_showPythonHelp, SIGNAL(triggered()), this, SLOT(showPythonHelp())); } -/** - * Returns the current execution mode set in the menu - */ -Script::ExecutionMode ScriptingWindow::getExecutionMode() const { - if (m_execParallel->isChecked()) - return Script::Asynchronous; - else - return Script::Serialised; +/// Should we enable abort functionality +bool ScriptingWindow::shouldEnableAbort() const { + return m_manager->scriptingEnv()->supportsAbortRequests(); } /** - * Accept a custom event and in this case test if it is a ScriptingChangeEvent - * @param event :: The custom event + * Opens a set of files in new tabs + * @param tabsToOpen A list of filenames to open in new tabs */ -void ScriptingWindow::customEvent(QEvent *event) { - if (!m_manager->isExecuting() && event->type() == SCRIPTING_CHANGE_EVENT) { - ScriptingChangeEvent *sce = static_cast<ScriptingChangeEvent *>(event); - setWindowTitle("MantidPlot: " + sce->scriptingEnv()->languageName() + - " Window"); - } -} - -/** - * Accept a drag move event and selects whether to accept the action - * @param de :: The drag move event - */ -void ScriptingWindow::dragMoveEvent(QDragMoveEvent *de) { - const QMimeData *mimeData = de->mimeData(); - if (mimeData->hasUrls()) { - if (extractPyFiles(mimeData->urls()).size() > 0) { - de->accept(); +void ScriptingWindow::openPreviousTabs(const QStringList &tabsToOpen) { + const int totalFiles = tabsToOpen.size(); + if (totalFiles == 0) { + m_manager->newTab(); + } else { + for (int i = 0; i < totalFiles; i++) { + m_manager->newTab(i, tabsToOpen[i]); } } } /** - * Accept a drag enter event and selects whether to accept the action - * @param de :: The drag enter event + * Returns the current execution mode set in the menu */ -void ScriptingWindow::dragEnterEvent(QDragEnterEvent *de) { - const QMimeData *mimeData = de->mimeData(); - if (mimeData->hasUrls()) { - if (extractPyFiles(mimeData->urls()).size() > 0) { - de->acceptProposedAction(); - } - } +Script::ExecutionMode ScriptingWindow::getExecutionMode() const { + if (m_execParallel->isChecked()) + return Script::Asynchronous; + else + return Script::Serialised; } -/** - * Accept a drag drop event and process the data appropriately - * @param de :: The drag drop event - */ -void ScriptingWindow::dropEvent(QDropEvent *de) { - const QMimeData *mimeData = de->mimeData(); - if (mimeData->hasUrls()) { - QStringList filenames = extractPyFiles(mimeData->urls()); - de->acceptProposedAction(); - - for (int i = 0; i < filenames.size(); ++i) { - m_manager->openInNewTab(filenames[i]); - } - } -} QStringList ScriptingWindow::extractPyFiles(const QList<QUrl> &urlList) const { QStringList filenames; @@ -744,14 +797,3 @@ QStringList ScriptingWindow::extractPyFiles(const QList<QUrl> &urlList) const { } return filenames; } - -void ScriptingWindow::openPreviousTabs(const QStringList &tabsToOpen) { - const int totalFiles = tabsToOpen.size(); - if (totalFiles == 0) { - m_manager->newTab(); - } else { - for (int i = 0; i < totalFiles; i++) { - m_manager->newTab(i, tabsToOpen[i]); - } - } -} diff --git a/Code/Mantid/MantidPlot/src/ScriptingWindow.h b/Code/Mantid/MantidPlot/src/ScriptingWindow.h index 4d1e8e3d9459ea484305bee1527699f8f4987ae0..eac3dfe59e62010cb42acf76e2ea1df461d1e73e 100755 --- a/Code/Mantid/MantidPlot/src/ScriptingWindow.h +++ b/Code/Mantid/MantidPlot/src/ScriptingWindow.h @@ -65,9 +65,11 @@ signals: void hideMe(); protected: - void dropEvent(QDropEvent *de); - void dragMoveEvent(QDragMoveEvent *de); + /// Accept a custom defined event + void customEvent(QEvent *event); void dragEnterEvent(QDragEnterEvent *de); + void dragMoveEvent(QDragMoveEvent *de); + void dropEvent(QDropEvent *de); private slots: /// Populate file menu @@ -88,9 +90,11 @@ private slots: /// Update menus based on current tab counts void setMenuStates(int nTabs); /// Sets the execution actions based on the flag - void setEditActionsDisabled(bool state); + void setEditActionsDisabled(bool off); /// Sets the execution actions based on the flag - void setExecutionActionsDisabled(bool state); + void setExecutionActionsDisabled(bool off); + /// Sets the abort actions based on the flag or the environment + void setAbortActionsDisabled(bool off); /// Finds the script corresponding to the action and /// asks the manager to open it @@ -100,6 +104,8 @@ private slots: void executeAll(); /// Execute selection using the current mode option void executeSelection(); + /// Abort the current script + void abortCurrent(); /// Clear out any previous variable definitions in the current script void clearScriptVariables(); @@ -124,15 +130,14 @@ private: void initWindowMenuActions(); /// Create the help menu actions void initHelpMenuActions(); + + /// Should we enable abort functionality + bool shouldEnableAbort() const; /// Opens tabs based on QStringList void openPreviousTabs(const QStringList &tabsToOpen); - /// Returns the current execution mode Script::ExecutionMode getExecutionMode() const; - /// Accept a custom defined event - void customEvent(QEvent *event); - /// Extract py files from urllist QStringList extractPyFiles(const QList<QUrl> &urlList) const; @@ -154,7 +159,7 @@ private: /// Run menu QMenu *m_runMenu; /// Execute menu actions - QAction *m_execSelect, *m_execAll, *m_clearScriptVars; + QAction *m_execSelect, *m_execAll, *m_abortCurrent, *m_clearScriptVars; /// Execution mode menu QMenu *m_execModeMenu; /// Execute mode actions diff --git a/Code/Mantid/MantidPlot/src/muParserScript.cpp b/Code/Mantid/MantidPlot/src/muParserScript.cpp index 6f9e56ff2f2f708e77a66f5f201b430b2828c3ef..6cae4e9978966476343f068d0fc7184d11513b28 100644 --- a/Code/Mantid/MantidPlot/src/muParserScript.cpp +++ b/Code/Mantid/MantidPlot/src/muParserScript.cpp @@ -75,7 +75,7 @@ muParserScript::muParserScript(ScriptingEnv *env, const QString &name, QObject rparser = parser; parser.SetVarFactory(mu_addVariable); rparser.SetVarFactory(mu_addVariableR); - + } double muParserScript::col(const QString &arg) @@ -486,3 +486,8 @@ bool muParserScript::executeImpl() } return true; } + +void muParserScript::abortImpl() +{ + // does nothing +} diff --git a/Code/Mantid/MantidPlot/src/muParserScript.h b/Code/Mantid/MantidPlot/src/muParserScript.h index ca56f1be2c417725bcefe23e5fc0bd82bc20e3dd..4a0a8644b0c3d46f88d6e5b82f52832a786b37f0 100644 --- a/Code/Mantid/MantidPlot/src/muParserScript.h +++ b/Code/Mantid/MantidPlot/src/muParserScript.h @@ -1,6 +1,6 @@ /*************************************************************************** File : muParserScript.h - Project : QtiPlot + Project : QtiPlot -------------------------------------------------------------------- Copyright : (C) 2006 by Ion Vasilief, Knut Franke @@ -55,6 +55,7 @@ class muParserScript: public Script QString evalSingleLineToString(const QLocale& locale, char f, int prec); bool compileImpl(); bool executeImpl(); + void abortImpl(); bool setQObject(QObject *val, const char *name); bool setInt(int val, const char* name); bool setDouble(double val, const char* name); @@ -80,8 +81,8 @@ class muParserScript: public Script mu::Parser parser, rparser; Q3AsciiDict<double> variables, rvariables; QStringList muCode; - //! Flag telling is the parser should warn users on multiline code input - bool d_warn_multiline_code; + //! Flag telling is the parser should warn users on multiline code input + bool d_warn_multiline_code; public: static muParserScript *current; diff --git a/Code/Mantid/MantidPlot/src/qti.sip b/Code/Mantid/MantidPlot/src/qti.sip index a01d99b11cff9ed687de8c9efc5364c166f35aef..f7649c27bf66c9f43244047e19e7e31509bbbbf3 100644 --- a/Code/Mantid/MantidPlot/src/qti.sip +++ b/Code/Mantid/MantidPlot/src/qti.sip @@ -1367,6 +1367,12 @@ class InstrumentWindowTab: QFrame %TypeHeaderCode #include "src/Mantid/InstrumentWidget/InstrumentWindowTab.h" %End +public: + // This is a duplicate from the same enum in InstrumentWindow, just so you + // can do like InstrumentWindowRenderTab.FULL3D + enum SurfaceType { FULL3D, CYLINDRICAL_X, CYLINDRICAL_Y, CYLINDRICAL_Z, + SPHERICAL_X, SPHERICAL_Y, SPHERICAL_Z, + RENDERMODE_SIZE }; private: InstrumentWindowTab(const InstrumentWindowTab &); }; @@ -1390,6 +1396,7 @@ class InstrumentWindow: MdiSubWindow %End public: + // Note this enum is also defined in InstrumentWindowTab enum SurfaceType { FULL3D, CYLINDRICAL_X, CYLINDRICAL_Y, CYLINDRICAL_Z, SPHERICAL_X, SPHERICAL_Y, SPHERICAL_Z, RENDERMODE_SIZE }; diff --git a/Code/Mantid/MantidPlot/test/MantidPlotInstrumentViewTest.py b/Code/Mantid/MantidPlot/test/MantidPlotInstrumentViewTest.py index dbad3f6b3748491e1c0330684fd22b774ca682d8..bf1e3ea03c7121759daa943984b184a056022ee0 100644 --- a/Code/Mantid/MantidPlot/test/MantidPlotInstrumentViewTest.py +++ b/Code/Mantid/MantidPlot/test/MantidPlotInstrumentViewTest.py @@ -64,7 +64,15 @@ class MantidPlotInstrumentViewTest(unittest.TestCase): render_tab.setSurfaceType(0) render_tab.setAxis("Y-") render_tab.flipUnwrappedView(True) - + + def test_window_render_tab(self): + render_tab = INST_WIN.getTab("Render") + render_tab.setSurfaceType(InstrumentWindowRenderTab.FULL3D) + self.assertTrue(render_tab is not None) + current_scale = render_tab.getScaleType() + self.assertTrue(isinstance(current_scale, GraphOptions.ScaleType)) + render_tab.setScaleType(GraphOptions.Log10) + def test_closing_window_invalidates_reference(self): inst = getInstrumentView("loq_inst") inst.close() diff --git a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt index 204087c2d85f6c1aab7be466dcd8c26b979df3b7..fb125624cfc370800c88569d1f7e5b1f9d9c2003 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt +++ b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt @@ -79,6 +79,7 @@ set ( SRC_FILES src/ReflSearchModel.cpp src/SampleTransmission.cpp src/SANSAddFiles.cpp + src/SANSConstants.cpp src/SANSDiagnostics.cpp src/SANSEventSlicing.cpp src/SANSPlotSpecial.cpp @@ -186,6 +187,7 @@ set ( INC_FILES inc/MantidQtCustomInterfaces/QtReflOptionsDialog.h inc/MantidQtCustomInterfaces/SampleTransmission.h inc/MantidQtCustomInterfaces/SANSAddFiles.h + inc/MantidQtCustomInterfaces/SANSConstants.h inc/MantidQtCustomInterfaces/SANSDiagnostics.h inc/MantidQtCustomInterfaces/SANSEventSlicing.h inc/MantidQtCustomInterfaces/SANSPlotSpecial.h diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.ui b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.ui index 5ebf68508fa54486b73b3359ceac154ebe5b708c..1146483946c362e19326948aa502624ce23cc18d 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.ui +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.ui @@ -107,22 +107,19 @@ </property> <layout class="QHBoxLayout" name="horizontalLayout"> <item> - <widget class="QLabel" name="lbEFixed"> + <widget class="QLabel" name="lbEfixed"> <property name="text"> - <string>Efixed value:</string> - </property> - <property name="buddy"> - <cstring>leEfixed</cstring> + <string>Efixed:</string> </property> </widget> </item> <item> - <widget class="QLineEdit" name="leEfixed"> - <property name="enabled"> - <bool>false</bool> + <widget class="QDoubleSpinBox" name="spEfixed"> + <property name="decimals"> + <number>4</number> </property> - <property name="toolTip"> - <string>Value of Efixed for conversion from time to energy.</string> + <property name="singleStep"> + <double>0.100000000000000</double> </property> </widget> </item> @@ -963,7 +960,7 @@ <tabstop>ckSumFiles</tabstop> <tabstop>ckLoadLogFiles</tabstop> <tabstop>ckUseCalib</tabstop> - <tabstop>leEfixed</tabstop> + <tabstop>spEfixed</tabstop> <tabstop>spSpectraMin</tabstop> <tabstop>spSpectraMax</tabstop> <tabstop>cbGroupingOptions</tabstop> diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSAddFiles.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSAddFiles.h index 15bf132635a9720a3ec9ceef01d50bda475bf8bc..f80ca42edbf513760945237a6a07ca7e0f8d7070 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSAddFiles.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSAddFiles.h @@ -2,6 +2,7 @@ #define MANTIDQTCUSTOMINTERFACES_SANSADDFILES_H_ #include "ui_SANSRunWindow.h" +#include "MantidQtCustomInterfaces/SANSConstants.h" #include "MantidQtAPI/UserSubWindow.h" #include "MantidKernel/ConfigService.h" #include <Poco/NObserver.h> @@ -55,6 +56,8 @@ private: void setInputEnabled(bool enabled); /// Create Python string list QString createPythonStringList(QString inputString); + /// SANS constants + SANSConstants m_constants; void initLayout(); void setToolTips(); @@ -85,6 +88,12 @@ private slots: void onCurrentIndexChangedForHistogramChoice(int index); /// reacts to changes of the overlay check box void onStateChangedForOverlayCheckBox(int); + /// checks if a file corresponds to a histogram worksapce + bool isEventWorkspace(QString file_name); + /// checks if the files which are to be added are all based on event workspaces + bool existNonEventFiles(); + /// sets the binning options + void setBinningOptions(bool enable); }; } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSConstants.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSConstants.h new file mode 100644 index 0000000000000000000000000000000000000000..77286a688dd659ce14e42d961638e0a07329be15 --- /dev/null +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSConstants.h @@ -0,0 +1,24 @@ +#ifndef MANTIDQTCUSTOMINTERFACES_SANSCONSTANTS_H_ +#define MANTIDQTCUSTOMINTERFACES_SANSCONSTANTS_H_ + +#include <QString> + +namespace MantidQt +{ +namespace CustomInterfaces +{ + +class SANSConstants +{ +public: + SANSConstants(); + ~SANSConstants(); + QString getPythonSuccessKeyword(); + QString getPythonEmptyKeyword(); + QString getPythonTrueKeyword(); +}; + +} +} + +#endif //MANTIDQTCUSTOMINTERFACES_SANSADDFILES_H_ \ No newline at end of file diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.h index 0421203748361efd6b75632e7889fb0cc5aa72c6..3c144a09848d50bd922b39a64893968f03f48ca5 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.h @@ -10,6 +10,7 @@ #include "MantidQtMantidWidgets/SaveWorkspaces.h" #include "MantidQtCustomInterfaces/SANSDiagnostics.h" #include "MantidQtCustomInterfaces/SANSPlotSpecial.h" +#include "MantidQtCustomInterfaces/SANSConstants.h" #include <QHash> #include <QSettings> @@ -369,13 +370,11 @@ private: QAction *m_batch_clear; //Time/Pixel mask string QString m_maskScript; - /// Success keyword - static const QString m_pythonSuccessKeyword; - /// Keyword for empty return value in python - static const QString m_pythonEmptyKeyword; /// Stores the URL of each tab's help page. QMap<Tab, QString> m_helpPageUrls; - + /// SANS constants + SANSConstants m_constants; + void initAnalysDetTab(); void makeValidator(QLabel * const newValid, QWidget * control, QWidget * tab, const QString & errorMsg); void upDateDataDir(); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.ui b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.ui index 58f52aa2e93a89fcb203d6aadf2a7c71833bfb0a..6ce415d2101763d593d303af478182841bfd944e 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.ui +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/SANSRunWindow.ui @@ -4068,7 +4068,7 @@ p, li { white-space: pre-wrap; } </widget> </item> <item row="9" column="2"> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="histogram_binning_label"> <property name="text"> <string>Histogram binning (when adding event data):</string> </property> @@ -4113,7 +4113,7 @@ p, li { white-space: pre-wrap; } <item row="12" column="2"> <layout class="QHBoxLayout" name="horizontalLayout_24"> <item> - <widget class="QLabel" name="labelBinning"> + <widget class="QLabel" name="binning_label"> <property name="minimumSize"> <size> <width>70</width> diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ConvFit.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ConvFit.cpp index 0c08f51ba496c2b6f68089b501c2ac0e6802a0fa..64800758d8457e1b17a35c17f33ae880b4864745 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ConvFit.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ConvFit.cpp @@ -926,8 +926,8 @@ void ConvFit::plotGuess() { m_uiForm.ckPlotGuess->isChecked())) return; - if(m_uiForm.cbFitType->currentIndex() > 2){ - return; + if (m_uiForm.cbFitType->currentIndex() > 2) { + return; } bool tieCentres = (m_uiForm.cbFitType->currentIndex() > 1); @@ -1132,7 +1132,7 @@ void ConvFit::singleFitComplete(bool error) { m_dblManager->setValue(m_properties[functionParam], parameters[paramValue]); } - + funcIndex++; pref = prefBase; pref += "f" + QString::number(funcIndex) + ".f" + QString::number(subIndex) + "."; @@ -1154,7 +1154,6 @@ void ConvFit::singleFitComplete(bool error) { parameters[paramValue]); } } - funcIndex++; m_pythonExportWsName = ""; } @@ -1468,12 +1467,12 @@ void ConvFit::updatePlotOptions() { QStringList plotOptions; plotOptions << "None"; - if (deltaFunction && fitFunctionType < 3){ + if (deltaFunction && fitFunctionType < 3) { plotOptions << "Height"; } - + QStringList params = QStringList(); - + if (fitFunctionType != 2) { params = getFunctionParameters(m_uiForm.cbFitType->currentText()); } else { @@ -1483,8 +1482,8 @@ void ConvFit::updatePlotOptions() { plotOptions.append(params); } - if(fitFunctionType != 0 || deltaFunction){ - plotOptions << "All"; + if (fitFunctionType != 0 || deltaFunction) { + plotOptions << "All"; } m_uiForm.cbPlotType->addItems(plotOptions); } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp index 4c213b4662bd641eaaa571e0a739a850731cbc12..79c1c3c011848912b0deea47e2bea00702b29542 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp @@ -9,525 +9,544 @@ using namespace Mantid::API; using MantidQt::API::BatchAlgorithmRunner; -namespace MantidQt -{ -namespace CustomInterfaces -{ - //---------------------------------------------------------------------------------------------- - /** Constructor - */ - ISISEnergyTransfer::ISISEnergyTransfer(IndirectDataReduction * idrUI, QWidget * parent) : - IndirectDataReductionTab(idrUI, parent) - { - m_uiForm.setupUi(parent); - - // SIGNAL/SLOT CONNECTIONS - // Update instrument information when a new instrument config is selected - connect(this, SIGNAL(newInstrumentConfiguration()), this, SLOT(setInstrumentDefault())); - // Shows required mapping option UI widgets when a new mapping option is selected from drop down - connect(m_uiForm.cbGroupingOptions, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(mappingOptionSelected(const QString&))); - // Plots raw input data when user clicks Plot Time - connect(m_uiForm.pbPlotTime, SIGNAL(clicked()), this, SLOT(plotRaw())); - // Shows message on run button when user is inputting a run number - connect(m_uiForm.dsRunFiles, SIGNAL(fileTextChanged(const QString &)), this, SLOT(pbRunEditing())); - // Shows message on run button when Mantid is finding the file for a given run number - connect(m_uiForm.dsRunFiles, SIGNAL(findingFiles()), this, SLOT(pbRunFinding())); - // Reverts run button back to normal when file finding has finished - connect(m_uiForm.dsRunFiles, SIGNAL(fileFindingFinished()), this, SLOT(pbRunFinished())); - - // Re-validate when certain inputs are changed - connect(m_uiForm.spRebinLow, SIGNAL(valueChanged(double)), this, SLOT(validate())); - connect(m_uiForm.spRebinWidth, SIGNAL(valueChanged(double)), this, SLOT(validate())); - connect(m_uiForm.spRebinHigh, SIGNAL(valueChanged(double)), this, SLOT(validate())); - connect(m_uiForm.leRebinString, SIGNAL(textChanged(const QString &)), this, SLOT(validate())); - - // Update UI widgets to show default values - mappingOptionSelected(m_uiForm.cbGroupingOptions->currentText()); - - // Validate to remove invalid markers - validateTab(); +namespace MantidQt { +namespace CustomInterfaces { +//---------------------------------------------------------------------------------------------- +/** Constructor + */ +ISISEnergyTransfer::ISISEnergyTransfer(IndirectDataReduction *idrUI, + QWidget *parent) + : IndirectDataReductionTab(idrUI, parent) { + m_uiForm.setupUi(parent); + + // SIGNAL/SLOT CONNECTIONS + // Update instrument information when a new instrument config is selected + connect(this, SIGNAL(newInstrumentConfiguration()), this, + SLOT(setInstrumentDefault())); + // Shows required mapping option UI widgets when a new mapping option is + // selected from drop down + connect(m_uiForm.cbGroupingOptions, + SIGNAL(currentIndexChanged(const QString &)), this, + SLOT(mappingOptionSelected(const QString &))); + // Plots raw input data when user clicks Plot Time + connect(m_uiForm.pbPlotTime, SIGNAL(clicked()), this, SLOT(plotRaw())); + // Shows message on run button when user is inputting a run number + connect(m_uiForm.dsRunFiles, SIGNAL(fileTextChanged(const QString &)), this, + SLOT(pbRunEditing())); + // Shows message on run button when Mantid is finding the file for a given run + // number + connect(m_uiForm.dsRunFiles, SIGNAL(findingFiles()), this, + SLOT(pbRunFinding())); + // Reverts run button back to normal when file finding has finished + connect(m_uiForm.dsRunFiles, SIGNAL(fileFindingFinished()), this, + SLOT(pbRunFinished())); + + // Re-validate when certain inputs are changed + connect(m_uiForm.spRebinLow, SIGNAL(valueChanged(double)), this, + SLOT(validate())); + connect(m_uiForm.spRebinWidth, SIGNAL(valueChanged(double)), this, + SLOT(validate())); + connect(m_uiForm.spRebinHigh, SIGNAL(valueChanged(double)), this, + SLOT(validate())); + connect(m_uiForm.leRebinString, SIGNAL(textChanged(const QString &)), this, + SLOT(validate())); + + // Update UI widgets to show default values + mappingOptionSelected(m_uiForm.cbGroupingOptions->currentText()); + + // Validate to remove invalid markers + validateTab(); +} + +//---------------------------------------------------------------------------------------------- +/** Destructor + */ +ISISEnergyTransfer::~ISISEnergyTransfer() {} + +void ISISEnergyTransfer::setup() {} + +bool ISISEnergyTransfer::validate() { + UserInputValidator uiv; + + // Run files input + if (!m_uiForm.dsRunFiles->isValid()) + uiv.addErrorMessage("Run file range is invalid."); + + // Calibration file input + if (m_uiForm.ckUseCalib->isChecked() && + !m_uiForm.dsCalibrationFile->isValid()) + uiv.addErrorMessage("Calibration file/workspace is invalid."); + + // Mapping file + if ((m_uiForm.cbGroupingOptions->currentText() == "File") && + (!m_uiForm.dsMapFile->isValid())) + uiv.addErrorMessage("Mapping file is invalid."); + + // Rebinning + if (!m_uiForm.ckDoNotRebin->isChecked()) { + if (m_uiForm.cbRebinType->currentText() == "Single") { + bool rebinValid = !uiv.checkBins(m_uiForm.spRebinLow->value(), + m_uiForm.spRebinWidth->value(), + m_uiForm.spRebinHigh->value()); + m_uiForm.valRebinLow->setVisible(rebinValid); + m_uiForm.valRebinWidth->setVisible(rebinValid); + m_uiForm.valRebinHigh->setVisible(rebinValid); + } else { + uiv.checkFieldIsNotEmpty("Rebin string", m_uiForm.leRebinString, + m_uiForm.valRebinString); + } + } else { + m_uiForm.valRebinLow->setVisible(false); + m_uiForm.valRebinWidth->setVisible(false); + m_uiForm.valRebinHigh->setVisible(false); + m_uiForm.valRebinString->setVisible(false); } - //---------------------------------------------------------------------------------------------- - /** Destructor - */ - ISISEnergyTransfer::~ISISEnergyTransfer() - { + return uiv.isAllInputValid(); +} + +void ISISEnergyTransfer::run() { + IAlgorithm_sptr reductionAlg = + AlgorithmManager::Instance().create("ISISIndirectEnergyTransfer"); + reductionAlg->initialize(); + BatchAlgorithmRunner::AlgorithmRuntimeProps reductionRuntimeProps; + + QString instName = getInstrumentConfiguration()->getInstrumentName(); + + reductionAlg->setProperty("Instrument", instName.toStdString()); + reductionAlg->setProperty( + "Analyser", + getInstrumentConfiguration()->getAnalyserName().toStdString()); + reductionAlg->setProperty( + "Reflection", + getInstrumentConfiguration()->getReflectionName().toStdString()); + + // Override the efixed for QENS spectrometers only + QStringList qens; + qens << "IRIS" + << "OSIRIS"; + if (qens.contains(instName)) + reductionAlg->setProperty("Efixed", m_uiForm.spEfixed->value()); + + QString files = m_uiForm.dsRunFiles->getFilenames().join(","); + reductionAlg->setProperty("InputFiles", files.toStdString()); + + reductionAlg->setProperty("SumFiles", m_uiForm.ckSumFiles->isChecked()); + reductionAlg->setProperty("LoadLogFiles", + m_uiForm.ckLoadLogFiles->isChecked()); + + if (m_uiForm.ckUseCalib->isChecked()) { + QString calibWorkspaceName = + m_uiForm.dsCalibrationFile->getCurrentDataName(); + reductionAlg->setProperty("CalibrationWorkspace", + calibWorkspaceName.toStdString()); } + std::vector<long> detectorRange; + detectorRange.push_back(m_uiForm.spSpectraMin->value()); + detectorRange.push_back(m_uiForm.spSpectraMax->value()); + reductionAlg->setProperty("SpectraRange", detectorRange); - void ISISEnergyTransfer::setup() - { + if (m_uiForm.ckBackgroundRemoval->isChecked()) { + std::vector<double> backgroundRange; + backgroundRange.push_back(m_uiForm.spBackgroundStart->value()); + backgroundRange.push_back(m_uiForm.spBackgroundEnd->value()); + reductionAlg->setProperty("BackgroundRange", backgroundRange); } - - bool ISISEnergyTransfer::validate() - { - UserInputValidator uiv; - - // Run files input - if(!m_uiForm.dsRunFiles->isValid()) - uiv.addErrorMessage("Run file range is invalid."); - - // Calibration file input - if(m_uiForm.ckUseCalib->isChecked() && !m_uiForm.dsCalibrationFile->isValid()) - uiv.addErrorMessage("Calibration file/workspace is invalid."); - - // Mapping file - if((m_uiForm.cbGroupingOptions->currentText() == "File") && (!m_uiForm.dsMapFile->isValid())) - uiv.addErrorMessage("Mapping file is invalid."); - - // Rebinning - if(!m_uiForm.ckDoNotRebin->isChecked()) - { - if(m_uiForm.cbRebinType->currentText() == "Single") - { - bool rebinValid = !uiv.checkBins(m_uiForm.spRebinLow->value(), m_uiForm.spRebinWidth->value(), m_uiForm.spRebinHigh->value()); - m_uiForm.valRebinLow->setVisible(rebinValid); - m_uiForm.valRebinWidth->setVisible(rebinValid); - m_uiForm.valRebinHigh->setVisible(rebinValid); - } - else - { - uiv.checkFieldIsNotEmpty("Rebin string", m_uiForm.leRebinString, m_uiForm.valRebinString); - } - } + if (!m_uiForm.ckDoNotRebin->isChecked()) { + QString rebin; + if (m_uiForm.cbRebinType->currentIndex() == 0) + rebin = m_uiForm.spRebinLow->text() + "," + + m_uiForm.spRebinWidth->text() + "," + + m_uiForm.spRebinHigh->text(); else - { - m_uiForm.valRebinLow->setVisible(false); - m_uiForm.valRebinWidth->setVisible(false); - m_uiForm.valRebinHigh->setVisible(false); - m_uiForm.valRebinString->setVisible(false); - } + rebin = m_uiForm.leRebinString->text(); - return uiv.isAllInputValid(); + reductionAlg->setProperty("RebinString", rebin.toStdString()); } + if (m_uiForm.ckDetailedBalance->isChecked()) + reductionAlg->setProperty("DetailedBalance", + m_uiForm.spDetailedBalance->value()); + + if (m_uiForm.ckScaleMultiplier->isChecked()) + reductionAlg->setProperty("ScaleFactor", + m_uiForm.spScaleMultiplier->value()); + + if (m_uiForm.ckCm1Units->isChecked()) + reductionAlg->setProperty("UnitX", "DeltaE_inWavenumber"); + + QPair<QString, QString> grouping = + createMapFile(m_uiForm.cbGroupingOptions->currentText()); + reductionAlg->setProperty("GroupingMethod", grouping.first.toStdString()); + + if (grouping.first == "Workspace") + reductionRuntimeProps["GroupingWorkspace"] = grouping.second.toStdString(); + else if (grouping.first == "File") + reductionAlg->setProperty("MapFile", grouping.second.toStdString()); + + reductionAlg->setProperty("FoldMultipleFrames", m_uiForm.ckFold->isChecked()); + reductionAlg->setProperty("Plot", + m_uiForm.cbPlotType->currentText().toStdString()); + reductionAlg->setProperty("SaveFormats", getSaveFormats()); + reductionAlg->setProperty("OutputWorkspace", + "IndirectEnergyTransfer_Workspaces"); + + m_batchAlgoRunner->addAlgorithm(reductionAlg, reductionRuntimeProps); + + connect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, + SLOT(algorithmComplete(bool))); + disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, + SLOT(plotRawComplete(bool))); + m_batchAlgoRunner->executeBatchAsync(); +} + +/** + * Handles completion of the algorithm. + * + * Sets result workspace for Python export and ungroups result WorkspaceGroup. + * + * @param error True if the algorithm was stopped due to error, false otherwise + */ +void ISISEnergyTransfer::algorithmComplete(bool error) { + disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, + SLOT(algorithmComplete(bool))); + + if (error) + return; + + WorkspaceGroup_sptr energyTransferOutputGroup = + AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>( + "IndirectEnergyTransfer_Workspaces"); + if (energyTransferOutputGroup->size() == 0) + return; + + // Set workspace for Python export as the first result workspace + m_pythonExportWsName = energyTransferOutputGroup->getNames()[0]; + + // Ungroup the output workspace + energyTransferOutputGroup->removeAll(); + AnalysisDataService::Instance().remove("IndirectEnergyTransfer_Workspaces"); +} + +/** + * Called when the instrument has changed, used to update default values. + */ +void ISISEnergyTransfer::setInstrumentDefault() { + QMap<QString, QString> instDetails = getInstrumentDetails(); + + // Set the search instrument for runs + m_uiForm.dsRunFiles->setInstrumentOverride(instDetails["instrument"]); + + QStringList qens; + qens << "IRIS" + << "OSIRIS"; + m_uiForm.spEfixed->setEnabled(qens.contains(instDetails["instrument"])); + + if (instDetails["spectra-min"].isEmpty() || + instDetails["spectra-max"].isEmpty()) { + emit showMessageBox("Could not gather necessary data from parameter file."); + return; + } - void ISISEnergyTransfer::run() - { - IAlgorithm_sptr reductionAlg = AlgorithmManager::Instance().create("ISISIndirectEnergyTransfer"); - reductionAlg->initialize(); - BatchAlgorithmRunner::AlgorithmRuntimeProps reductionRuntimeProps; - - reductionAlg->setProperty("Instrument", getInstrumentConfiguration()->getInstrumentName().toStdString()); - reductionAlg->setProperty("Analyser", getInstrumentConfiguration()->getAnalyserName().toStdString()); - reductionAlg->setProperty("Reflection", getInstrumentConfiguration()->getReflectionName().toStdString()); - - QString files = m_uiForm.dsRunFiles->getFilenames().join(","); - reductionAlg->setProperty("InputFiles", files.toStdString()); - - reductionAlg->setProperty("SumFiles", m_uiForm.ckSumFiles->isChecked()); - reductionAlg->setProperty("LoadLogFiles", m_uiForm.ckLoadLogFiles->isChecked()); - - if(m_uiForm.ckUseCalib->isChecked()) - { - QString calibWorkspaceName = m_uiForm.dsCalibrationFile->getCurrentDataName(); - reductionAlg->setProperty("CalibrationWorkspace", calibWorkspaceName.toStdString()); - } - - std::vector<long> detectorRange; - detectorRange.push_back(m_uiForm.spSpectraMin->value()); - detectorRange.push_back(m_uiForm.spSpectraMax->value()); - reductionAlg->setProperty("SpectraRange", detectorRange); - - if(m_uiForm.ckBackgroundRemoval->isChecked()) - { - std::vector<double> backgroundRange; - backgroundRange.push_back(m_uiForm.spBackgroundStart->value()); - backgroundRange.push_back(m_uiForm.spBackgroundEnd->value()); - reductionAlg->setProperty("BackgroundRange", backgroundRange); - } - - if(!m_uiForm.ckDoNotRebin->isChecked()) - { - QString rebin; - if(m_uiForm.cbRebinType->currentIndex() == 0) - rebin = m_uiForm.spRebinLow->text() + "," + m_uiForm.spRebinWidth->text() + "," + m_uiForm.spRebinHigh->text(); - else - rebin = m_uiForm.leRebinString->text(); - - reductionAlg->setProperty("RebinString", rebin.toStdString()); + int specMin = instDetails["spectra-min"].toInt(); + int specMax = instDetails["spectra-max"].toInt(); + + m_uiForm.spSpectraMin->setMinimum(specMin); + m_uiForm.spSpectraMin->setMaximum(specMax); + m_uiForm.spSpectraMin->setValue(specMin); + + m_uiForm.spSpectraMax->setMinimum(specMin); + m_uiForm.spSpectraMax->setMaximum(specMax); + m_uiForm.spSpectraMax->setValue(specMax); + + if (!instDetails["Efixed"].isEmpty()) + m_uiForm.spEfixed->setValue(instDetails["Efixed"].toDouble()); + else + m_uiForm.spEfixed->setValue(0.0); + + // Default rebinning parameters can be set in instrument parameter file + if (!instDetails["rebin-default"].isEmpty()) { + m_uiForm.leRebinString->setText(instDetails["rebin-default"]); + m_uiForm.ckDoNotRebin->setChecked(false); + QStringList rbp = + instDetails["rebin-default"].split(",", QString::SkipEmptyParts); + if (rbp.size() == 3) { + m_uiForm.spRebinLow->setValue(rbp[0].toDouble()); + m_uiForm.spRebinWidth->setValue(rbp[1].toDouble()); + m_uiForm.spRebinHigh->setValue(rbp[2].toDouble()); + m_uiForm.cbRebinType->setCurrentIndex(0); + } else { + m_uiForm.cbRebinType->setCurrentIndex(1); } - - if(m_uiForm.ckDetailedBalance->isChecked()) - reductionAlg->setProperty("DetailedBalance", m_uiForm.spDetailedBalance->value()); - - if(m_uiForm.ckScaleMultiplier->isChecked()) - reductionAlg->setProperty("ScaleFactor", m_uiForm.spScaleMultiplier->value()); - - if(m_uiForm.ckCm1Units->isChecked()) - reductionAlg->setProperty("UnitX", "DeltaE_inWavenumber"); - - QPair<QString, QString> grouping = createMapFile(m_uiForm.cbGroupingOptions->currentText()); - reductionAlg->setProperty("GroupingMethod", grouping.first.toStdString()); - - if(grouping.first == "Workspace") - reductionRuntimeProps["GroupingWorkspace"] = grouping.second.toStdString(); - else if(grouping.first == "File") - reductionAlg->setProperty("MapFile", grouping.second.toStdString()); - - reductionAlg->setProperty("FoldMultipleFrames", m_uiForm.ckFold->isChecked()); - reductionAlg->setProperty("Plot", m_uiForm.cbPlotType->currentText().toStdString()); - reductionAlg->setProperty("SaveFormats", getSaveFormats()); - reductionAlg->setProperty("OutputWorkspace", "IndirectEnergyTransfer_Workspaces"); - - m_batchAlgoRunner->addAlgorithm(reductionAlg, reductionRuntimeProps); - - connect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(algorithmComplete(bool))); - disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(plotRawComplete(bool))); - m_batchAlgoRunner->executeBatchAsync(); + } else { + m_uiForm.ckDoNotRebin->setChecked(true); + m_uiForm.spRebinLow->setValue(0.0); + m_uiForm.spRebinWidth->setValue(0.0); + m_uiForm.spRebinHigh->setValue(0.0); + m_uiForm.leRebinString->setText(""); } - - /** - * Handles completion of the algorithm. - * - * Sets result workspace for Python export and ungroups result WorkspaceGroup. - * - * @param error True if the algorithm was stopped due to error, false otherwise - */ - void ISISEnergyTransfer::algorithmComplete(bool error) - { - disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(algorithmComplete(bool))); - - if(error) - return; - - WorkspaceGroup_sptr energyTransferOutputGroup = AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("IndirectEnergyTransfer_Workspaces"); - if(energyTransferOutputGroup->size() == 0) - return; - - // Set workspace for Python export as the first result workspace - m_pythonExportWsName = energyTransferOutputGroup->getNames()[0]; - - // Ungroup the output workspace - energyTransferOutputGroup->removeAll(); - AnalysisDataService::Instance().remove("IndirectEnergyTransfer_Workspaces"); + if (!instDetails["cm-1-convert-choice"].isEmpty()) { + bool defaultOptions = instDetails["cm-1-convert-choice"] == "true"; + m_uiForm.ckCm1Units->setChecked(defaultOptions); } - - /** - * Called when the instrument has changed, used to update default values. - */ - void ISISEnergyTransfer::setInstrumentDefault() - { - QMap<QString, QString> instDetails = getInstrumentDetails(); - - // Set the search instrument for runs - m_uiForm.dsRunFiles->setInstrumentOverride(instDetails["instrument"]); - - if(instDetails["spectra-min"].isEmpty() || instDetails["spectra-max"].isEmpty()) - { - emit showMessageBox("Could not gather necessary data from parameter file."); - return; - } - - int specMin = instDetails["spectra-min"].toInt(); - int specMax = instDetails["spectra-max"].toInt(); - - m_uiForm.spSpectraMin->setMinimum(specMin); - m_uiForm.spSpectraMin->setMaximum(specMax); - m_uiForm.spSpectraMin->setValue(specMin); - - m_uiForm.spSpectraMax->setMinimum(specMin); - m_uiForm.spSpectraMax->setMaximum(specMax); - m_uiForm.spSpectraMax->setValue(specMax); - - if(!instDetails["Efixed"].isEmpty()) - m_uiForm.leEfixed->setText(instDetails["Efixed"]); - else - m_uiForm.leEfixed->clear(); - - // Default rebinning parameters can be set in instrument parameter file - if(!instDetails["rebin-default"].isEmpty()) - { - m_uiForm.leRebinString->setText(instDetails["rebin-default"]); - m_uiForm.ckDoNotRebin->setChecked(false); - QStringList rbp = instDetails["rebin-default"].split(",", QString::SkipEmptyParts); - if(rbp.size() == 3) - { - m_uiForm.spRebinLow->setValue(rbp[0].toDouble()); - m_uiForm.spRebinWidth->setValue(rbp[1].toDouble()); - m_uiForm.spRebinHigh->setValue(rbp[2].toDouble()); - m_uiForm.cbRebinType->setCurrentIndex(0); - } - else - { - m_uiForm.cbRebinType->setCurrentIndex(1); - } - } - else - { - m_uiForm.ckDoNotRebin->setChecked(true); - m_uiForm.spRebinLow->setValue(0.0); - m_uiForm.spRebinWidth->setValue(0.0); - m_uiForm.spRebinHigh->setValue(0.0); - m_uiForm.leRebinString->setText(""); - } - - if(!instDetails["cm-1-convert-choice"].isEmpty()) - { - bool defaultOptions = instDetails["cm-1-convert-choice"] == "true"; - m_uiForm.ckCm1Units->setChecked(defaultOptions); - } - - if(!instDetails["save-nexus-choice"].isEmpty()) - { - bool defaultOptions = instDetails["save-nexus-choice"] == "true"; - m_uiForm.ckSaveNexus->setChecked(defaultOptions); - } - - if(!instDetails["save-ascii-choice"].isEmpty()) - { - bool defaultOptions = instDetails["save-ascii-choice"] == "true"; - m_uiForm.ckSaveASCII->setChecked(defaultOptions); - } - - if(!instDetails["fold-frames-choice"].isEmpty()) - { - bool defaultOptions = instDetails["fold-frames-choice"] == "true"; - m_uiForm.ckFold->setChecked(defaultOptions); - } + if (!instDetails["save-nexus-choice"].isEmpty()) { + bool defaultOptions = instDetails["save-nexus-choice"] == "true"; + m_uiForm.ckSaveNexus->setChecked(defaultOptions); } - /** - * This function runs when the user makes a selection on the cbGroupingOptions QComboBox. - * @param groupType :: Value of selection made by user. - */ - void ISISEnergyTransfer::mappingOptionSelected(const QString& groupType) - { - if ( groupType == "File" ) - { - m_uiForm.swGrouping->setCurrentIndex(0); - } - else if ( groupType == "Groups" ) - { - m_uiForm.swGrouping->setCurrentIndex(1); - } - else if ( groupType == "All" || groupType == "Individual" || groupType == "Default" ) - { - m_uiForm.swGrouping->setCurrentIndex(2); - } + if (!instDetails["save-ascii-choice"].isEmpty()) { + bool defaultOptions = instDetails["save-ascii-choice"] == "true"; + m_uiForm.ckSaveASCII->setChecked(defaultOptions); } - /** - * This function creates the mapping/grouping file for the data analysis. - * @param groupType :: Type of grouping (All, Group, Indiviual) - * @return path to mapping file, or an empty string if file could not be created. - */ - QPair<QString, QString> ISISEnergyTransfer::createMapFile(const QString& groupType) - { - QString specRange = m_uiForm.spSpectraMin->text() + "," + m_uiForm.spSpectraMax->text(); - - if(groupType == "File") - { - QString groupFile = m_uiForm.dsMapFile->getFirstFilename(); - if(groupFile == "") - emit showMessageBox("You must enter a path to the .map file."); - - return qMakePair(QString("File"), groupFile); - } - else if(groupType == "Groups") - { - QString groupWS = "__Grouping"; - - IAlgorithm_sptr groupingAlg = AlgorithmManager::Instance().create("CreateGroupingWorkspace"); - groupingAlg->initialize(); - - groupingAlg->setProperty("FixedGroupCount", m_uiForm.spNumberGroups->value()); - groupingAlg->setProperty("InstrumentName", getInstrumentConfiguration()->getInstrumentName().toStdString()); - groupingAlg->setProperty("ComponentName", getInstrumentConfiguration()->getAnalyserName().toStdString()); - groupingAlg->setProperty("OutputWorkspace", groupWS.toStdString()); - - m_batchAlgoRunner->addAlgorithm(groupingAlg); - - return qMakePair(QString("Workspace"), groupWS); - } - else if (groupType == "Default") - { - return qMakePair(QString("IPF"), QString()); - } - else - { - // Catch All and Individual - return qMakePair(groupType, QString()); - } + if (!instDetails["fold-frames-choice"].isEmpty()) { + bool defaultOptions = instDetails["fold-frames-choice"] == "true"; + m_uiForm.ckFold->setChecked(defaultOptions); } - - /** - * Converts the checkbox selection to a comma delimited list of save formats for the - * ISISIndirectEnergyTransfer algorithm. - * - * @return A vector of save formats - */ - std::vector<std::string> ISISEnergyTransfer::getSaveFormats() - { - std::vector<std::string> fileFormats; - - if ( m_uiForm.ckSaveNexus->isChecked() ) - fileFormats.push_back("nxs"); - if ( m_uiForm.ckSaveSPE->isChecked() ) - fileFormats.push_back("spe"); - if ( m_uiForm.ckSaveNXSPE->isChecked() ) - fileFormats.push_back("nxspe"); - if ( m_uiForm.ckSaveASCII->isChecked() ) - fileFormats.push_back("ascii"); - if ( m_uiForm.ckSaveAclimax->isChecked() ) - fileFormats.push_back("aclimax"); - if ( m_uiForm.ckSaveDaveGrp->isChecked() ) - fileFormats.push_back("davegrp"); - - return fileFormats; +} + +/** + * This function runs when the user makes a selection on the cbGroupingOptions + * QComboBox. + * @param groupType :: Value of selection made by user. + */ +void ISISEnergyTransfer::mappingOptionSelected(const QString &groupType) { + if (groupType == "File") { + m_uiForm.swGrouping->setCurrentIndex(0); + } else if (groupType == "Groups") { + m_uiForm.swGrouping->setCurrentIndex(1); + } else if (groupType == "All" || groupType == "Individual" || + groupType == "Default") { + m_uiForm.swGrouping->setCurrentIndex(2); } - - /** - * Plots raw time data from .raw file before any data conversion has been performed. - */ - void ISISEnergyTransfer::plotRaw() - { - using Mantid::specid_t; - using MantidQt::API::BatchAlgorithmRunner; - - if(!m_uiForm.dsRunFiles->isValid()) - { - emit showMessageBox("You must select a run file."); - return; - } - - specid_t detectorMin = static_cast<specid_t>(m_uiForm.spPlotTimeSpecMin->value()); - specid_t detectorMax = static_cast<specid_t>(m_uiForm.spPlotTimeSpecMax->value()); - - if(detectorMin > detectorMax) - { - emit showMessageBox("Minimum spectra must be less than or equal to maximum spectra."); - return; - } - - QString rawFile = m_uiForm.dsRunFiles->getFirstFilename(); - QFileInfo rawFileInfo(rawFile); - std::string name = rawFileInfo.baseName().toStdString(); - - IAlgorithm_sptr loadAlg = AlgorithmManager::Instance().create("Load"); - loadAlg->initialize(); - loadAlg->setProperty("Filename", rawFile.toStdString()); - loadAlg->setProperty("OutputWorkspace", name); - loadAlg->setProperty("SpectrumMin", detectorMin); - loadAlg->setProperty("SpectrumMax", detectorMax); - m_batchAlgoRunner->addAlgorithm(loadAlg); - - // Rebin the workspace to its self to ensure constant binning - BatchAlgorithmRunner::AlgorithmRuntimeProps inputToRebin; - inputToRebin["WorkspaceToMatch"] = name; - inputToRebin["WorkspaceToRebin"] = name; - inputToRebin["OutputWorkspace"] = name; - - IAlgorithm_sptr rebinAlg = AlgorithmManager::Instance().create("RebinToWorkspace"); - rebinAlg->initialize(); - m_batchAlgoRunner->addAlgorithm(rebinAlg, inputToRebin); - - BatchAlgorithmRunner::AlgorithmRuntimeProps inputFromRebin; - inputFromRebin["InputWorkspace"] = name; - - std::vector<specid_t> detectorList; - for(specid_t i = detectorMin; i <= detectorMax; i++) - detectorList.push_back(i); - - if(m_uiForm.ckBackgroundRemoval->isChecked()) - { - std::vector<double> range; - range.push_back(m_uiForm.spBackgroundStart->value()); - range.push_back(m_uiForm.spBackgroundEnd->value()); - - IAlgorithm_sptr calcBackAlg = AlgorithmManager::Instance().create("CalculateFlatBackground"); - calcBackAlg->initialize(); - calcBackAlg->setProperty("OutputWorkspace", name + "_bg"); - calcBackAlg->setProperty("Mode", "Mean"); - calcBackAlg->setProperty("StartX", range[0]); - calcBackAlg->setProperty("EndX", range[1]); - m_batchAlgoRunner->addAlgorithm(calcBackAlg, inputFromRebin); - - BatchAlgorithmRunner::AlgorithmRuntimeProps inputFromCalcBG; - inputFromCalcBG["InputWorkspace"] = name + "_bg"; - - IAlgorithm_sptr groupAlg = AlgorithmManager::Instance().create("GroupDetectors"); - groupAlg->initialize(); - groupAlg->setProperty("OutputWorkspace", name + "_grp"); - groupAlg->setProperty("DetectorList", detectorList); - m_batchAlgoRunner->addAlgorithm(groupAlg, inputFromCalcBG); - - IAlgorithm_sptr rawGroupAlg = AlgorithmManager::Instance().create("GroupDetectors"); - rawGroupAlg->initialize(); - rawGroupAlg->setProperty("OutputWorkspace", name + "_grp_raw"); - rawGroupAlg->setProperty("DetectorList", detectorList); - m_batchAlgoRunner->addAlgorithm(rawGroupAlg, inputFromRebin); - } - else - { - IAlgorithm_sptr rawGroupAlg = AlgorithmManager::Instance().create("GroupDetectors"); - rawGroupAlg->initialize(); - rawGroupAlg->setProperty("OutputWorkspace", name + "_grp"); - rawGroupAlg->setProperty("DetectorList", detectorList); - m_batchAlgoRunner->addAlgorithm(rawGroupAlg, inputFromRebin); - } - - disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(algorithmComplete(bool))); - connect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(plotRawComplete(bool))); - m_batchAlgoRunner->executeBatchAsync(); +} + +/** + * This function creates the mapping/grouping file for the data analysis. + * @param groupType :: Type of grouping (All, Group, Indiviual) + * @return path to mapping file, or an empty string if file could not be + * created. + */ +QPair<QString, QString> +ISISEnergyTransfer::createMapFile(const QString &groupType) { + QString specRange = + m_uiForm.spSpectraMin->text() + "," + m_uiForm.spSpectraMax->text(); + + if (groupType == "File") { + QString groupFile = m_uiForm.dsMapFile->getFirstFilename(); + if (groupFile == "") + emit showMessageBox("You must enter a path to the .map file."); + + return qMakePair(QString("File"), groupFile); + } else if (groupType == "Groups") { + QString groupWS = "__Grouping"; + + IAlgorithm_sptr groupingAlg = + AlgorithmManager::Instance().create("CreateGroupingWorkspace"); + groupingAlg->initialize(); + + groupingAlg->setProperty("FixedGroupCount", + m_uiForm.spNumberGroups->value()); + groupingAlg->setProperty( + "InstrumentName", + getInstrumentConfiguration()->getInstrumentName().toStdString()); + groupingAlg->setProperty( + "ComponentName", + getInstrumentConfiguration()->getAnalyserName().toStdString()); + groupingAlg->setProperty("OutputWorkspace", groupWS.toStdString()); + + m_batchAlgoRunner->addAlgorithm(groupingAlg); + + return qMakePair(QString("Workspace"), groupWS); + } else if (groupType == "Default") { + return qMakePair(QString("IPF"), QString()); + } else { + // Catch All and Individual + return qMakePair(groupType, QString()); + } +} + +/** + * Converts the checkbox selection to a comma delimited list of save formats for + *the + * ISISIndirectEnergyTransfer algorithm. + * + * @return A vector of save formats + */ +std::vector<std::string> ISISEnergyTransfer::getSaveFormats() { + std::vector<std::string> fileFormats; + + if (m_uiForm.ckSaveNexus->isChecked()) + fileFormats.push_back("nxs"); + if (m_uiForm.ckSaveSPE->isChecked()) + fileFormats.push_back("spe"); + if (m_uiForm.ckSaveNXSPE->isChecked()) + fileFormats.push_back("nxspe"); + if (m_uiForm.ckSaveASCII->isChecked()) + fileFormats.push_back("ascii"); + if (m_uiForm.ckSaveAclimax->isChecked()) + fileFormats.push_back("aclimax"); + if (m_uiForm.ckSaveDaveGrp->isChecked()) + fileFormats.push_back("davegrp"); + + return fileFormats; +} + +/** + * Plots raw time data from .raw file before any data conversion has been + * performed. + */ +void ISISEnergyTransfer::plotRaw() { + using Mantid::specid_t; + using MantidQt::API::BatchAlgorithmRunner; + + if (!m_uiForm.dsRunFiles->isValid()) { + emit showMessageBox("You must select a run file."); + return; } - /** - * Handles plotting the result of Plot Raw - * - * @param error Indicates if the algorithm chain failed - */ - void ISISEnergyTransfer::plotRawComplete(bool error) - { - disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(plotRawComplete(bool))); - - if(error) - return; - - QString rawFile = m_uiForm.dsRunFiles->getFirstFilename(); - QFileInfo rawFileInfo(rawFile); - std::string name = rawFileInfo.baseName().toStdString(); + specid_t detectorMin = + static_cast<specid_t>(m_uiForm.spPlotTimeSpecMin->value()); + specid_t detectorMax = + static_cast<specid_t>(m_uiForm.spPlotTimeSpecMax->value()); - plotSpectrum(QString::fromStdString(name) + "_grp"); + if (detectorMin > detectorMax) { + emit showMessageBox( + "Minimum spectra must be less than or equal to maximum spectra."); + return; } - /** - * Called when a user starts to type / edit the runs to load. - */ - void ISISEnergyTransfer::pbRunEditing() - { - emit updateRunButton(false, "Editing...", "Run numbers are currently being edited."); + QString rawFile = m_uiForm.dsRunFiles->getFirstFilename(); + QFileInfo rawFileInfo(rawFile); + std::string name = rawFileInfo.baseName().toStdString(); + + IAlgorithm_sptr loadAlg = AlgorithmManager::Instance().create("Load"); + loadAlg->initialize(); + loadAlg->setProperty("Filename", rawFile.toStdString()); + loadAlg->setProperty("OutputWorkspace", name); + loadAlg->setProperty("SpectrumMin", detectorMin); + loadAlg->setProperty("SpectrumMax", detectorMax); + m_batchAlgoRunner->addAlgorithm(loadAlg); + + // Rebin the workspace to its self to ensure constant binning + BatchAlgorithmRunner::AlgorithmRuntimeProps inputToRebin; + inputToRebin["WorkspaceToMatch"] = name; + inputToRebin["WorkspaceToRebin"] = name; + inputToRebin["OutputWorkspace"] = name; + + IAlgorithm_sptr rebinAlg = + AlgorithmManager::Instance().create("RebinToWorkspace"); + rebinAlg->initialize(); + m_batchAlgoRunner->addAlgorithm(rebinAlg, inputToRebin); + + BatchAlgorithmRunner::AlgorithmRuntimeProps inputFromRebin; + inputFromRebin["InputWorkspace"] = name; + + std::vector<specid_t> detectorList; + for (specid_t i = detectorMin; i <= detectorMax; i++) + detectorList.push_back(i); + + if (m_uiForm.ckBackgroundRemoval->isChecked()) { + std::vector<double> range; + range.push_back(m_uiForm.spBackgroundStart->value()); + range.push_back(m_uiForm.spBackgroundEnd->value()); + + IAlgorithm_sptr calcBackAlg = + AlgorithmManager::Instance().create("CalculateFlatBackground"); + calcBackAlg->initialize(); + calcBackAlg->setProperty("OutputWorkspace", name + "_bg"); + calcBackAlg->setProperty("Mode", "Mean"); + calcBackAlg->setProperty("StartX", range[0]); + calcBackAlg->setProperty("EndX", range[1]); + m_batchAlgoRunner->addAlgorithm(calcBackAlg, inputFromRebin); + + BatchAlgorithmRunner::AlgorithmRuntimeProps inputFromCalcBG; + inputFromCalcBG["InputWorkspace"] = name + "_bg"; + + IAlgorithm_sptr groupAlg = + AlgorithmManager::Instance().create("GroupDetectors"); + groupAlg->initialize(); + groupAlg->setProperty("OutputWorkspace", name + "_grp"); + groupAlg->setProperty("DetectorList", detectorList); + m_batchAlgoRunner->addAlgorithm(groupAlg, inputFromCalcBG); + + IAlgorithm_sptr rawGroupAlg = + AlgorithmManager::Instance().create("GroupDetectors"); + rawGroupAlg->initialize(); + rawGroupAlg->setProperty("OutputWorkspace", name + "_grp_raw"); + rawGroupAlg->setProperty("DetectorList", detectorList); + m_batchAlgoRunner->addAlgorithm(rawGroupAlg, inputFromRebin); + } else { + IAlgorithm_sptr rawGroupAlg = + AlgorithmManager::Instance().create("GroupDetectors"); + rawGroupAlg->initialize(); + rawGroupAlg->setProperty("OutputWorkspace", name + "_grp"); + rawGroupAlg->setProperty("DetectorList", detectorList); + m_batchAlgoRunner->addAlgorithm(rawGroupAlg, inputFromRebin); } - /** - * Called when the FileFinder starts finding the files. - */ - void ISISEnergyTransfer::pbRunFinding() - { - emit updateRunButton(false, "Finding files...", "Searching for data files for the run numbers entered..."); - m_uiForm.dsRunFiles->setEnabled(false); + disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, + SLOT(algorithmComplete(bool))); + connect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, + SLOT(plotRawComplete(bool))); + m_batchAlgoRunner->executeBatchAsync(); +} + +/** + * Handles plotting the result of Plot Raw + * + * @param error Indicates if the algorithm chain failed + */ +void ISISEnergyTransfer::plotRawComplete(bool error) { + disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, + SLOT(plotRawComplete(bool))); + + if (error) + return; + + QString rawFile = m_uiForm.dsRunFiles->getFirstFilename(); + QFileInfo rawFileInfo(rawFile); + std::string name = rawFileInfo.baseName().toStdString(); + + plotSpectrum(QString::fromStdString(name) + "_grp"); +} + +/** + * Called when a user starts to type / edit the runs to load. + */ +void ISISEnergyTransfer::pbRunEditing() { + emit updateRunButton(false, "Editing...", + "Run numbers are currently being edited."); +} + +/** + * Called when the FileFinder starts finding the files. + */ +void ISISEnergyTransfer::pbRunFinding() { + emit updateRunButton( + false, "Finding files...", + "Searching for data files for the run numbers entered..."); + m_uiForm.dsRunFiles->setEnabled(false); +} + +/** + * Called when the FileFinder has finished finding the files. + */ +void ISISEnergyTransfer::pbRunFinished() { + if (!m_uiForm.dsRunFiles->isValid()) { + emit updateRunButton( + false, "Invalid Run(s)", + "Cannot find data files for some of the run numbers entered."); + } else { + emit updateRunButton(); } - /** - * Called when the FileFinder has finished finding the files. - */ - void ISISEnergyTransfer::pbRunFinished() - { - if(!m_uiForm.dsRunFiles->isValid()) - { - emit updateRunButton(false, "Invalid Run(s)", "Cannot find data files for some of the run numbers entered."); - } - else - { - emit updateRunButton(); - } - - m_uiForm.dsRunFiles->setEnabled(true); - } + m_uiForm.dsRunFiles->setEnabled(true); +} } // namespace CustomInterfaces } // namespace Mantid diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/SANSAddFiles.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/SANSAddFiles.cpp index 98a2b4272fee0a5199f291d388da03dc75203002..e41439b5f60bf37e9cfa9260dbaa94e27da49405 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/SANSAddFiles.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/SANSAddFiles.cpp @@ -248,6 +248,11 @@ void SANSAddFiles::add2Runs2Add() if ( search.isValid() == "" ) {//this means the file was found newL->setToolTip(QString::fromStdString(search.value())); + + // If we don't have an event workspace data set, then we disable the event options + if (!isEventWorkspace(QString::fromStdString(search.value()))) { + setBinningOptions(false); + } } } } @@ -422,6 +427,7 @@ void SANSAddFiles::clearClicked() { m_SANSForm->toAdd_List->clear(); insertListFront(""); + setBinningOptions(true); } void SANSAddFiles::removeSelected() @@ -433,6 +439,11 @@ void SANSAddFiles::removeSelected() delete m_SANSForm->toAdd_List->takeItem(selRow); sels = m_SANSForm->toAdd_List->selectedItems(); } + + // Check if the remaining files correspond to only event workspaces + if (!existNonEventFiles()) { + setBinningOptions(true); + } } /** @@ -526,8 +537,8 @@ void SANSAddFiles::setHistogramUiLogic(QString label, QString toolTip, QString l m_SANSForm->eventToHistBinning->setToolTip(toolTip); // Label for line edit field - m_SANSForm->labelBinning->setText(label); - m_SANSForm->labelBinning->setToolTip(toolTip); + m_SANSForm->binning_label->setText(label); + m_SANSForm->binning_label->setToolTip(toolTip); setInputEnabled(enabled); } @@ -539,7 +550,7 @@ void SANSAddFiles::setHistogramUiLogic(QString label, QString toolTip, QString l */ void SANSAddFiles::setInputEnabled(bool enabled) { m_SANSForm->eventToHistBinning->setEnabled(enabled); - m_SANSForm->labelBinning->setEnabled(enabled); + m_SANSForm->binning_label->setEnabled(enabled); } /** @@ -570,6 +581,59 @@ QString SANSAddFiles::createPythonStringList(QString inputString) { return formattedString; } +/** + * Check if a file corresponds to a histogram workspace + * @param fileName: the file name + * @returns true if it is a histogram workspace + */ +bool SANSAddFiles::isEventWorkspace(QString fileName) { + auto isEvent = false; + fileName.replace("\\", "/"); + QString code_torun = "import ISISCommandInterface as i\n"; + code_torun += "i.check_if_event_workspace(file_name='"; + code_torun += fileName; + code_torun += + "')\n"; + + auto status = runPythonCode(code_torun, false); + if (status.contains(m_constants.getPythonTrueKeyword())) { + isEvent = true; + } + return isEvent; +} + +/** + * Enable or disable the binning options + * @param enable: If the options should be enabled or disabled + */ +void SANSAddFiles::setBinningOptions(bool enable) { + m_SANSForm->eventToHistBinning->setEnabled(enable); + m_SANSForm->comboBox_histogram_choice->setEnabled(enable); + m_SANSForm->overlayCheckBox->setEnabled(enable); + m_SANSForm->histogram_binning_label->setEnabled(enable); + m_SANSForm->binning_label->setEnabled(enable); +} + +/** + * Check if non-event type files exist + * returns true if all are event files or if there are no files else false + */ +bool SANSAddFiles::existNonEventFiles() { + auto elements = m_SANSForm->toAdd_List->count(); + for (int i = 0; i < elements; ++i) { + auto fileName = m_SANSForm->toAdd_List->item(i)->data(Qt::WhatsThisRole).toString(); + if ( !fileName.isEmpty() ) + { + // Make sure that the file separators are valid + fileName.replace("\\", "/"); + // Run the check + if (!isEventWorkspace(fileName)) { + return true; + } + } + } + + return false; +} }//namespace CustomInterfaces diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/SANSConstants.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/SANSConstants.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d34051f49f63831215aa44f83149ebf96693fe8 --- /dev/null +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/SANSConstants.cpp @@ -0,0 +1,40 @@ +#include "MantidQtCustomInterfaces/SANSConstants.h" + + +namespace MantidQt +{ +namespace CustomInterfaces +{ +SANSConstants::SANSConstants() {} + +SANSConstants::~SANSConstants() {} + +/** + * Defines the python keyword for a succssful operation + * @returns the keyword for success + */ +QString SANSConstants::getPythonSuccessKeyword() { + const static QString pythonSuccessKeyword = "pythonExecutionWasSuccessful"; + return pythonSuccessKeyword; +} + +/** + * Defines the python keyword for an empty object , ie None + * @returns the python keyword for an empty object + */ +QString SANSConstants::getPythonEmptyKeyword() { + const static QString pythonSuccessKeyword = "None"; + return pythonSuccessKeyword; +} + +/** + * Defines the python keyword for true , ie True + * @returns the python true keyword + */ +QString SANSConstants::getPythonTrueKeyword() { + const static QString pythonSuccessKeyword = "True"; + return pythonSuccessKeyword; +} + +} +} \ No newline at end of file diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp index 55ad4ebcedeeee13059944fb987fd94f3ccdfd7a..5d65ef1397ce917251000bb2a8702cdf6cc9f217 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/SANSRunWindow.cpp @@ -149,11 +149,7 @@ namespace } } -//---------------------------------------------- -// Static key strings -//---------------------------------------------- -const QString SANSRunWindow::m_pythonSuccessKeyword = "pythonExecutionWasSuccessful"; -const QString SANSRunWindow::m_pythonEmptyKeyword = "None"; + //---------------------------------------------- // Public member functions //---------------------------------------------- @@ -3031,7 +3027,7 @@ void SANSRunWindow::handleInstrumentChange() int ind = m_uiForm.detbank_sel->findText(detect); // We set the detector selection only if nothing is set yet. // Previously, we didn't handle merged and both at this point - if (detectorSelection == m_pythonEmptyKeyword || detectorSelection.isEmpty()) { + if (detectorSelection == m_constants.getPythonEmptyKeyword() || detectorSelection.isEmpty()) { if( ind != -1 ) { m_uiForm.detbank_sel->setCurrentIndex(ind); } @@ -3892,11 +3888,11 @@ void SANSRunWindow::createZeroErrorFreeClone(QString& originalWorkspaceName, QSt QString pythonCode("print i.CreateZeroErrorFreeClonedWorkspace(input_workspace_name='"); pythonCode += originalWorkspaceName + "',"; pythonCode += " output_workspace_name='" + clonedWorkspaceName + "')\n"; - pythonCode += "print '" + m_pythonSuccessKeyword + "'\n"; + pythonCode += "print '" + m_constants.getPythonSuccessKeyword() + "'\n"; QString result(runPythonCode(pythonCode, false)); result = result.simplified(); - if (result != m_pythonSuccessKeyword) { - result.replace(m_pythonSuccessKeyword, ""); + if (result != m_constants.getPythonSuccessKeyword()) { + result.replace(m_constants.getPythonSuccessKeyword(), ""); g_log.warning("Error creating a zerror error free cloned workspace. Will save original workspace. More info: " + result.toStdString()); } } @@ -3911,11 +3907,11 @@ void SANSRunWindow::deleteZeroErrorFreeClone(QString& clonedWorkspaceName) { // Run the python script which destroys the cloned workspace QString pythonCode("print i.DeleteZeroErrorFreeClonedWorkspace(input_workspace_name='"); pythonCode += clonedWorkspaceName + "')\n"; - pythonCode += "print '" + m_pythonSuccessKeyword + "'\n"; + pythonCode += "print '" + m_constants.getPythonSuccessKeyword() + "'\n"; QString result(runPythonCode(pythonCode, false)); result = result.simplified(); - if (result != m_pythonSuccessKeyword) { - result.replace(m_pythonSuccessKeyword, ""); + if (result != m_constants.getPythonSuccessKeyword()) { + result.replace(m_constants.getPythonSuccessKeyword(), ""); g_log.warning("Error deleting a zerror error free cloned workspace. More info: " + result.toStdString()); } } @@ -3928,12 +3924,12 @@ void SANSRunWindow::deleteZeroErrorFreeClone(QString& clonedWorkspaceName) { bool SANSRunWindow::isValidWsForRemovingZeroErrors(QString& wsName) { QString pythonCode("\nprint i.IsValidWsForRemovingZeroErrors(input_workspace_name='"); pythonCode += wsName + "')"; - pythonCode += "\nprint '" + m_pythonSuccessKeyword + "'"; + pythonCode += "\nprint '" + m_constants.getPythonSuccessKeyword() + "'"; QString result(runPythonCode(pythonCode, false)); result = result.simplified(); bool isValid = true; - if (result != m_pythonSuccessKeyword) { - result.replace(m_pythonSuccessKeyword, ""); + if (result != m_constants.getPythonSuccessKeyword()) { + result.replace(m_constants.getPythonSuccessKeyword(), ""); g_log.warning("Not a valid workspace for zero error replacement. Will save original workspace. More info: " + result.toStdString()); isValid = false; } @@ -4014,7 +4010,7 @@ void SANSRunWindow::setTransmissionSettingsFromUserFile() { QString transmissionRadiusRequest("\nprint i.GetTransmissionRadiusInMM()"); QString resultTransmissionRadius(runPythonCode(transmissionRadiusRequest, false)); resultTransmissionRadius = resultTransmissionRadius.simplified(); - if (resultTransmissionRadius != m_pythonEmptyKeyword) { + if (resultTransmissionRadius != m_constants.getPythonEmptyKeyword()) { this->m_uiForm.trans_radius_line_edit->setText(resultTransmissionRadius); this->m_uiForm.trans_radius_check_box->setChecked(true); setBeamStopLogic(TransSettings::RADIUS, true); @@ -4024,7 +4020,7 @@ void SANSRunWindow::setTransmissionSettingsFromUserFile() { QString transmissionROIRequest("\nprint i.GetTransmissionROI()"); QString resultTransmissionROI(runPythonCode(transmissionROIRequest, false)); resultTransmissionROI = resultTransmissionROI.simplified(); - if (resultTransmissionROI != m_pythonEmptyKeyword) { + if (resultTransmissionROI != m_constants.getPythonEmptyKeyword()) { resultTransmissionROI = runPythonCode("\nprint i.ConvertFromPythonStringList(to_convert=" + resultTransmissionROI+ ")", false); this->m_uiForm.trans_roi_files_line_edit->setText(resultTransmissionROI); this->m_uiForm.trans_roi_files_checkbox->setChecked(true); @@ -4036,7 +4032,7 @@ void SANSRunWindow::setTransmissionSettingsFromUserFile() { QString transmissionMaskRequest("\nprint i.GetTransmissionMask()"); QString resultTransmissionMask(runPythonCode(transmissionMaskRequest, false)); resultTransmissionMask = resultTransmissionMask.simplified(); - if (resultTransmissionMask != m_pythonEmptyKeyword) { + if (resultTransmissionMask != m_constants.getPythonEmptyKeyword()) { resultTransmissionMask = runPythonCode("\nprint i.ConvertFromPythonStringList(to_convert=" + resultTransmissionMask+ ")", false); this->m_uiForm.trans_masking_line_edit->setText(resultTransmissionMask); } @@ -4045,7 +4041,7 @@ void SANSRunWindow::setTransmissionSettingsFromUserFile() { QString transmissionMonitorSpectrumShiftRequest("\nprint i.GetTransmissionMonitorSpectrumShift()"); QString resultTransmissionMonitorSpectrumShift(runPythonCode(transmissionMonitorSpectrumShiftRequest, false)); resultTransmissionMonitorSpectrumShift = resultTransmissionMonitorSpectrumShift.simplified(); - if (resultTransmissionMonitorSpectrumShift != m_pythonEmptyKeyword) { + if (resultTransmissionMonitorSpectrumShift != m_constants.getPythonEmptyKeyword()) { this->m_uiForm.trans_M3M4_line_edit->setText(resultTransmissionMonitorSpectrumShift); } @@ -4054,7 +4050,7 @@ void SANSRunWindow::setTransmissionSettingsFromUserFile() { QString transmissionMonitorSpectrumRequest("\nprint i.GetTransmissionMonitorSpectrum()"); QString resultTransmissionMonitorSpectrum(runPythonCode(transmissionMonitorSpectrumRequest, false)); resultTransmissionMonitorSpectrum = resultTransmissionMonitorSpectrum.simplified(); - if (resultTransmissionMonitorSpectrum != m_pythonEmptyKeyword) { + if (resultTransmissionMonitorSpectrum != m_constants.getPythonEmptyKeyword()) { if (resultTransmissionMonitorSpectrum == "3") { this->m_uiForm.trans_M3_check_box->setChecked(true); setM3M4Logic(TransSettings::M3, true); diff --git a/Code/Mantid/Testing/Data/DocTest/ENGINX00193749.nxs.md5 b/Code/Mantid/Testing/Data/DocTest/ENGINX00193749.nxs.md5 deleted file mode 100644 index a33b32a31fd878db2cc37c29a9fa587d8a124220..0000000000000000000000000000000000000000 --- a/Code/Mantid/Testing/Data/DocTest/ENGINX00193749.nxs.md5 +++ /dev/null @@ -1 +0,0 @@ -22fc488c3f71eae634a18799dd04da72 diff --git a/Code/Mantid/Testing/Data/DocTest/ENGINX00236516.nxs.md5 b/Code/Mantid/Testing/Data/DocTest/ENGINX00236516.nxs.md5 deleted file mode 100644 index 545450331f4276ed5c8f7195aeac01ca75ee1918..0000000000000000000000000000000000000000 --- a/Code/Mantid/Testing/Data/DocTest/ENGINX00236516.nxs.md5 +++ /dev/null @@ -1 +0,0 @@ -e1e5a526460378b89319886a3509f326 diff --git a/Code/Mantid/Testing/Data/DocTest/ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs.md5 b/Code/Mantid/Testing/Data/DocTest/ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs.md5 new file mode 100644 index 0000000000000000000000000000000000000000..1503e40c721663cac288cd83fa32eae2c5a6f13c --- /dev/null +++ b/Code/Mantid/Testing/Data/DocTest/ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs.md5 @@ -0,0 +1 @@ +e5262f4b8faee3aeb0670dc6a6a79c70 diff --git a/Code/Mantid/Testing/Data/DocTest/ENGINX_precalculated_vanadium_run000236516_integration.nxs.md5 b/Code/Mantid/Testing/Data/DocTest/ENGINX_precalculated_vanadium_run000236516_integration.nxs.md5 new file mode 100644 index 0000000000000000000000000000000000000000..eb35042b21a7e06c08060a9d3d91726100a841e6 --- /dev/null +++ b/Code/Mantid/Testing/Data/DocTest/ENGINX_precalculated_vanadium_run000236516_integration.nxs.md5 @@ -0,0 +1 @@ +b80d6b275be723a41cc90cee179526f8 diff --git a/Code/Mantid/Testing/Data/DocTest/IP0005.dat.md5 b/Code/Mantid/Testing/Data/DocTest/IP0005.dat.md5 new file mode 100644 index 0000000000000000000000000000000000000000..5266783b222ad31df7d370f2140a0491dfa44012 --- /dev/null +++ b/Code/Mantid/Testing/Data/DocTest/IP0005.dat.md5 @@ -0,0 +1 @@ +31834c0613be7294a98fe8568f4d0ce2 \ No newline at end of file diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/EnggCalibrationTest.py b/Code/Mantid/Testing/SystemTests/tests/analysis/EnggCalibrationTest.py index 02937d2e7195cb46502e746094587c13ced5631e..4e2dba4c841987ec797041b31483fe5702663001 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/EnggCalibrationTest.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/EnggCalibrationTest.py @@ -4,7 +4,7 @@ from mantid.simpleapi import * def rel_err_less_delta(val, ref, epsilon): """ - Checks that a value 'val' does not defer from a reference value 'ref' by 'epsilon' + Checks that a value 'val' does not differ from a reference value 'ref' by 'epsilon' or more. This method compares the relative error. An epsilon of 0.1 means a relative difference of 10 % = 100*0.1 % @@ -35,7 +35,7 @@ class EnginXFocusWithVanadiumCorrection(stresstesting.MantidStressTest): OutputWorkspace='ENGIN-X_vanadium_integ_test_ws') self.van_bank_curves_name = 'enginx_van_bank_curves' - self.van_bank_curves_pre_integ_name = 'enginx_van_bank_curves_with_precalc_integ' + self.van_integ_name = 'enginx_van_bank_curves_with_precalc_integ' self.out_ws_name = 'enginx_focussed' self.out_ws_precalc_name = 'enginx_focussed_with_precalculations' @@ -49,21 +49,23 @@ class EnginXFocusWithVanadiumCorrection(stresstesting.MantidStressTest): # note: not giving calibrated detector positions + EnggVanadiumCorrections(VanadiumWorkspace = van_ws, + OutIntegrationWorkspace = self.van_integ_name, + OutCurvesWorkspace = self.van_bank_curves_name) + # do all calculations from raw data EnggFocus(InputWorkspace = long_calib_ws, VanadiumWorkspace = van_ws, Bank = '1', - OutVanadiumCurveFits = self.van_bank_curves_name, OutputWorkspace=self.out_ws_name) # Now with pre-calculated curves and integration values. This makes sure that these do not # change too much AND the final results do not change too much as a result EnggFocus(InputWorkspace = long_calib_ws, - VanadiumWorkspace = van_ws, + VanIntegrationWorkspace = self._precalc_van_integ_tbl, + VanCurvesWorkspace = self._precalc_van_ws, Bank = '1', - VanadiumIntegWorkspace = self._precalc_van_integ_tbl, - OutVanadiumCurveFits = self.van_bank_curves_pre_integ_name, - OutputWorkspace=self.out_ws_precalc_name) + OutputWorkspace = self.out_ws_precalc_name) def validate(self): out_ws = mtd[self.out_ws_name] @@ -99,11 +101,6 @@ class EnginXFocusWithVanadiumCorrection(stresstesting.MantidStressTest): precalc_curve_simul = self._precalc_van_ws.readY(1) self.assertEquals(len(simul), len(precalc_curve_simul)) - # with precalculated curve and precalculated integration - van_pre_integ_out_ws = mtd[self.van_bank_curves_pre_integ_name] - precalc_all_curve_simul = van_pre_integ_out_ws.readY(1) - self.assertEquals(len(simul), len(precalc_all_curve_simul)) - delta = 1e-5 for i in range(0, len(simul)): self.assertTrue(rel_err_less_delta(simul[i], precalc_curve_simul[i], delta), @@ -111,11 +108,18 @@ class EnginXFocusWithVanadiumCorrection(stresstesting.MantidStressTest): "against bank curves previously fitted. got: %f where I expect: %f"% (delta, i, simul[i], precalc_curve_simul[i])) - self.assertTrue(rel_err_less_delta(simul[i], precalc_curve_simul[i], delta), - "Relative difference bigger than acceptable error (%f) when comparing bin %d " - "against bank curves previously fitted (and also using pre-calculated integration " - "values). got: %f where I expect: %f"% - (delta, i, simul[i], precalc_all_curve_simul[i])) + # === check the integration table against the previously saved integration table === + integ_tbl = mtd[self.van_integ_name] + precalc_integ_tbl = self._precalc_van_integ_tbl + self.assertEquals(integ_tbl.rowCount(), 2513) + self.assertEquals(integ_tbl.rowCount(), precalc_integ_tbl.rowCount()) + self.assertEquals(integ_tbl.columnCount(), precalc_integ_tbl.columnCount()) + for i in range(integ_tbl.rowCount()): + self.assertTrue((0 == integ_tbl.cell(i, 0) and 0 == precalc_integ_tbl.cell(i, 0)) or + rel_err_less_delta(integ_tbl.cell(i, 0), precalc_integ_tbl.cell(i, 0), delta), + "Relative difference bigger than gaccepted error (%f) when comparing the " + "integration of a spectrum (%f) against the integration previously calculated and " + "saved (%f)." % (delta, integ_tbl.cell(i, 0), precalc_integ_tbl.cell(i, 0) )) # === check the 'focussed' spectrum === out_precalc_ws = mtd[self.out_ws_precalc_name] @@ -127,13 +131,13 @@ class EnginXFocusWithVanadiumCorrection(stresstesting.MantidStressTest): self.assertTrue(rel_err_less_delta(simul[i], precalc_curve_simul[i], delta), "Relative difference bigger than accepted delta (%f) when comparing bin %d " "of the focussed spectrum against the focussed spectrum obtained using bank curves " - "and spectra integration values pre-calculated. got: %f where I expect: %f"% + "and spectra integration values pre-calculated. Got: %f where I expected: %f"% (delta, i, focussed_sp[i], focussed_sp_precalc[i])) def cleanup(self): mtd.remove(self.out_ws_name) - mtd.remove(self.van_bank_curves_name) - mtd.remove(self.van_bank_curves_pre_integ_name) + mtd.remove(self.out_ws_precalc_name) + mtd.remove(self.van_integ_name) mtd.remove(self.van_bank_curves_name) diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py b/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py index 945e708e3cd04474d58edd1355ad08b9f4aa7778..2b7702622f0995a6eff52b9bd160b25a37e50091 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py @@ -1,18 +1,8 @@ -#pylint: disable=no-init,invalid-name,attribute-defined-outside-init -import stresstesting -import os -import platform -from abc import ABCMeta, abstractmethod - -from mantid.simpleapi import * - -# For debugging only. -from mantid.api import FileFinder - -# Import our workflows. -from IndirectDataAnalysis import furyfitSeq, furyfitMult, confitSeq +#pylint: disable=no-init,invalid-name,attribute-defined-outside-init,too-many-lines,too-many-instance-attributes,non-parent-init-called,abstract-method +# non-parent-init-called is disabled to remove false positives from a bug in pyLint < 1.4 +# abstract-mehod checking seems to ignore the fact some classes are declared abstract using abc -''' +""" - TOSCA only supported by "Reduction" (the Energy Transfer tab of C2E). - OSIRIS/IRIS supported by all tabs / interfaces. - VESUVIO is not supported by any interface as of yet. @@ -74,8 +64,20 @@ stresstesting.MantidStressTest | +--IRISConvFit | +--OSIRISConvFit | -''' +""" +import stresstesting +import os +import platform +from abc import ABCMeta, abstractmethod + +from mantid.simpleapi import * + +# For debugging only. +from mantid.api import FileFinder + +# Import our workflows. +from IndirectDataAnalysis import furyfitSeq, furyfitMult, confitSeq class ISISIndirectInelasticBase(stresstesting.MantidStressTest): ''' @@ -420,10 +422,10 @@ class ISISIndirectInelasticReductionOutput(stresstesting.MantidStressTest): working_directory = config['defaultsave.directory'] output_names = {} - for format, ext in zip(self.file_formats, self.file_extensions): + for file_format, ext in zip(self.file_formats, self.file_extensions): output_file_name = self.result_name + ext output_file_name = os.path.join(working_directory, output_file_name) - output_names[format] = output_file_name + output_names[file_format] = output_file_name return output_names @@ -836,12 +838,12 @@ class ISISIndirectInelasticFuryAndFuryFit(ISISIndirectInelasticBase): LoadNexus(sample, OutputWorkspace=sample) LoadNexus(self.resolution, OutputWorkspace=self.resolution) - fury_props, fury_ws = TransformToIqt(SampleWorkspace=self.samples[0], - ResolutionWorkspace=self.resolution, - EnergyMin=self.e_min, - EnergyMax=self.e_max, - BinReductionFactor=self.num_bins, - DryRun=False) + _, fury_ws = TransformToIqt(SampleWorkspace=self.samples[0], + ResolutionWorkspace=self.resolution, + EnergyMin=self.e_min, + EnergyMax=self.e_max, + BinReductionFactor=self.num_bins, + DryRun=False) # Test FuryFit Sequential furyfitSeq_ws = furyfitSeq(fury_ws.getName(), @@ -954,12 +956,12 @@ class ISISIndirectInelasticFuryAndFuryFitMulti(ISISIndirectInelasticBase): LoadNexus(sample, OutputWorkspace=sample) LoadNexus(self.resolution, OutputWorkspace=self.resolution) - fury_props, fury_ws = TransformToIqt(SampleWorkspace=self.samples[0], - ResolutionWorkspace=self.resolution, - EnergyMin=self.e_min, - EnergyMax=self.e_max, - BinReductionFactor=self.num_bins, - DryRun=False) + _, fury_ws = TransformToIqt(SampleWorkspace=self.samples[0], + ResolutionWorkspace=self.resolution, + EnergyMin=self.e_min, + EnergyMax=self.e_max, + BinReductionFactor=self.num_bins, + DryRun=False) # Test FuryFit Sequential furyfitSeq_ws = furyfitMult(fury_ws.getName(), diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectLoadAsciiTest.py b/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectLoadAsciiTest.py index c47144d927ef77ec07171f11c8553361b79dcc29..a12fb7ef7f20c31bc4dc01c03963525110a0bedc 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectLoadAsciiTest.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectLoadAsciiTest.py @@ -1,6 +1,6 @@ #pylint: disable=no-init,attribute-defined-outside-init import stresstesting -from mantid.simpleapi import * +import mantid.simpleapi as ms #==================================================================================================== class IN10SiliconTest(stresstesting.MantidStressTest): @@ -42,10 +42,10 @@ class IN13CaFTest(stresstesting.MantidStressTest): def validate(self): self.tolerance = 1e-2 - from mantid.simpleapi import Load - - Load(Filename='ISISIndirectLoadAscii_IN13CaFTest.nxs',OutputWorkspace='ISISIndirectLoadAscii_IN13CaFTest') - Load(Filename='ISISIndirectLoadAscii_IN13CaFTest2.nxs',OutputWorkspace='ISISIndirectLoadAscii_IN13CaFTest2') + ms.Load(Filename='ISISIndirectLoadAscii_IN13CaFTest.nxs', + OutputWorkspace='ISISIndirectLoadAscii_IN13CaFTest') + ms.Load(Filename='ISISIndirectLoadAscii_IN13CaFTest2.nxs', + OutputWorkspace='ISISIndirectLoadAscii_IN13CaFTest2') # check each of the resulting workspaces match ws1Match = self.checkWorkspacesMatch('IN13_16347_CaF422_q', 'ISISIndirectLoadAscii_IN13CaFTest2') @@ -56,8 +56,7 @@ class IN13CaFTest(stresstesting.MantidStressTest): # function to check two workspaces match # Used when the result of a test produces more than a single workspace def checkWorkspacesMatch(self, ws1, ws2): - from mantid.simpleapi import SaveNexus, AlgorithmManager - checker = AlgorithmManager.create("CheckWorkspacesMatch") + checker = ms.AlgorithmManager.create("CheckWorkspacesMatch") checker.setLogging(True) checker.setPropertyValue("Workspace1", ws1) checker.setPropertyValue("Workspace2", ws2) @@ -68,7 +67,7 @@ class IN13CaFTest(stresstesting.MantidStressTest): if checker.getPropertyValue("Result") != 'Success!': print self.__class__.__name__ - SaveNexus(InputWorkspace=ws2,Filename=self.__class__.__name__+'-mismatch.nxs') + ms.SaveNexus(InputWorkspace=ws2,Filename=self.__class__.__name__+'-mismatch.nxs') return False return True diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectSimulationTest.py b/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectSimulationTest.py index 508fb03e68700f3bbcfcd1514cb2b7e9a57c6cb9..2e4a6a9d1439611c5f640d3e84538a1950477276 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectSimulationTest.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectSimulationTest.py @@ -1,28 +1,26 @@ #pylint: disable=no-init,attribute-defined-outside-init import stresstesting -from mantid.simpleapi import * +import mantid.simpleapi as ms #==================================================================================================== class MolDynCdlTest(stresstesting.MantidStressTest): def runTest(self): - from mantid.simpleapi import MolDyn - - MolDyn(Filename='DISF_NaF.cdl', - Functions=['Fqt-total', 'Sqw-total'], - Plot='None', - Save=False, - OutputWorkspace='ISISIndirectSimulationTest_MolDynCdl') + ms.MolDyn(Filename='DISF_NaF.cdl', + Functions=['Fqt-total', 'Sqw-total'], + Plot='None', + Save=False, + OutputWorkspace='ISISIndirectSimulationTest_MolDynCdl') def validate(self): self.tolerance = 1e-2 self.disableChecking.append("Instrument") - from mantid.simpleapi import Load - - Load(Filename='ISISIndirectSimulation_MolDynCDL.nxs',OutputWorkspace='ISISIndirectSimulation_MolDynCDL') - Load(Filename='ISISIndirectSimulation_MolDynCDL_SQW.nxs',OutputWorkspace='ISISIndirectSimulation_MolDynCDL_SQW') + ms.Load(Filename='ISISIndirectSimulation_MolDynCDL.nxs', + OutputWorkspace='ISISIndirectSimulation_MolDynCDL') + ms.Load(Filename='ISISIndirectSimulation_MolDynCDL_SQW.nxs', + OutputWorkspace='ISISIndirectSimulation_MolDynCDL_SQW') # check each of the resulting workspaces match ws1Match = self.checkWorkspacesMatch('DISF_NaF_Fqt-total', 'ISISIndirectSimulation_MolDynCDL') @@ -37,9 +35,7 @@ class MolDynCdlTest(stresstesting.MantidStressTest): Used when the result of a test produces more than a single workspace """ - from mantid.simpleapi import SaveNexus, AlgorithmManager - - checker = AlgorithmManager.create("CheckWorkspacesMatch") + checker = ms.AlgorithmManager.create("CheckWorkspacesMatch") checker.setLogging(True) checker.setPropertyValue("Workspace1", ws1) checker.setPropertyValue("Workspace2", ws2) @@ -50,7 +46,7 @@ class MolDynCdlTest(stresstesting.MantidStressTest): if checker.getPropertyValue("Result") != 'Success!': print self.__class__.__name__ - SaveNexus(InputWorkspace=ws2,Filename=self.__class__.__name__+'-mismatch.nxs') + ms.SaveNexus(InputWorkspace=ws2,Filename=self.__class__.__name__+'-mismatch.nxs') return False return True @@ -60,12 +56,10 @@ class MolDynCdlTest(stresstesting.MantidStressTest): class MolDynDatTest(stresstesting.MantidStressTest): def runTest(self): - from mantid.simpleapi import MolDyn - - MolDyn(Filename='WSH_test.dat', - Plot='None', - Save=False, - OutputWorkspace='WSH_test_iqt') + ms.MolDyn(Filename='WSH_test.dat', + Plot='None', + Save=False, + OutputWorkspace='WSH_test_iqt') def validate(self): diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/IndirectDiffractionTests.py b/Code/Mantid/Testing/SystemTests/tests/analysis/IndirectDiffractionTests.py index aa515d519e1b2538659500154f1e1bc003310217..b847316984025ec3dc07581d4871215e23d04538 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/IndirectDiffractionTests.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/IndirectDiffractionTests.py @@ -1,9 +1,13 @@ -#pylint: disable=no-init +#pylint: disable=no-init,non-parent-init-called +# non-parent-init-called is disabled to remove false positives from a bug in pyLint < 1.4 + from abc import ABCMeta, abstractmethod import stresstesting +import mantid.simpleapi as ms +from mantid import mtd -class MSGDiffractionReductionTest(stresstesting.MantidStressTest): +class ISISIndirectDiffractionReduction(stresstesting.MantidStressTest): """ Base class for tests that use the ISISIndirectDiffractionReduction algorithm. """ @@ -22,15 +26,12 @@ class MSGDiffractionReductionTest(stresstesting.MantidStressTest): """ Runs an ISISIndirectDiffractionReduction with the configured parameters. """ - from mantid.simpleapi import ISISIndirectDiffractionReduction - from mantid import mtd - - ISISIndirectDiffractionReduction(InputFiles=self.raw_file, - OutputWorkspace=self.output_workspace_group, - Instrument=self.instrument, - Mode=self.mode, - SpectraRange=self.spectra_range, - RebinParam=self.rebinning) + ms.ISISIndirectDiffractionReduction(InputFiles=self.raw_file, + OutputWorkspace=self.output_workspace_group, + Instrument=self.instrument, + Mode=self.mode, + SpectraRange=self.spectra_range, + RebinParam=self.rebinning) self._output_workspace = mtd[self.output_workspace_group].getNames()[0] @@ -41,12 +42,12 @@ class MSGDiffractionReductionTest(stresstesting.MantidStressTest): self.disableChecking.append('Instrument') return self._output_workspace, self.get_reference_file() - #------------------------------------------------------------------------------- -class IRISDiffspecDiffractionTest(MSGDiffractionReductionTest): + +class IRISDiffspecDiffractionTest(ISISIndirectDiffractionReduction): def __init__(self): - MSGDiffractionReductionTest.__init__(self) + ISISIndirectDiffractionReduction.__init__(self) self.instrument = 'IRIS' self.mode = 'diffspec' @@ -58,12 +59,12 @@ class IRISDiffspecDiffractionTest(MSGDiffractionReductionTest): def get_reference_file(self): return 'IRISDiffspecDiffractionTest.nxs' - #------------------------------------------------------------------------------- -class TOSCADiffractionTest(MSGDiffractionReductionTest): + +class TOSCADiffractionTest(ISISIndirectDiffractionReduction): def __init__(self): - MSGDiffractionReductionTest.__init__(self) + ISISIndirectDiffractionReduction.__init__(self) self.instrument = 'TOSCA' self.mode = 'diffspec' @@ -75,12 +76,12 @@ class TOSCADiffractionTest(MSGDiffractionReductionTest): def get_reference_file(self): return 'TOSCADiffractionTest.nxs' - #------------------------------------------------------------------------------- -class OSIRISDiffspecDiffractionTest(MSGDiffractionReductionTest): + +class OSIRISDiffspecDiffractionTest(ISISIndirectDiffractionReduction): def __init__(self): - MSGDiffractionReductionTest.__init__(self) + ISISIndirectDiffractionReduction.__init__(self) self.instrument = 'OSIRIS' self.mode = 'diffspec' @@ -92,17 +93,15 @@ class OSIRISDiffspecDiffractionTest(MSGDiffractionReductionTest): def get_reference_file(self): return 'OsirisDiffspecDiffractionTest.nxs' - #------------------------------------------------------------------------------- -class OsirisDiffOnlyTest(stresstesting.MantidStressTest): + +class OSIRISDiffonlyDiffractionTest(stresstesting.MantidStressTest): def runTest(self): - from mantid.simpleapi import OSIRISDiffractionReduction - OSIRISDiffractionReduction( - OutputWorkspace="OsirisDiffractionTest", - Sample="OSI89813.raw, OSI89814.raw, OSI89815.raw, OSI89816.raw, OSI89817.raw", - CalFile="osiris_041_RES10.cal", - Vanadium="OSI89757, OSI89758, OSI89759, OSI89760, OSI89761") + ms.OSIRISDiffractionReduction(OutputWorkspace="OsirisDiffractionTest", + Sample="OSI89813.raw, OSI89814.raw, OSI89815.raw, OSI89816.raw, OSI89817.raw", + CalFile="osiris_041_RES10.cal", + Vanadium="OSI89757, OSI89758, OSI89759, OSI89760, OSI89761") def validate(self): self.disableChecking.append('Instrument') diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/LoadVesuvioTest.py b/Code/Mantid/Testing/SystemTests/tests/analysis/LoadVesuvioTest.py index ebb5ca56717df97506cac55b4cb4bad7f1f94e6b..4354f51c17bb3ce8ebeab545710f74466c014c90 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/LoadVesuvioTest.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/LoadVesuvioTest.py @@ -1,8 +1,8 @@ -#pylint: disable=invalid-name,no-init +#pylint: disable=invalid-name,no-init,too-many-public-methods,too-many-arguments import stresstesting from mantid.api import MatrixWorkspace, mtd -from mantid.simpleapi import LoadVesuvio +import mantid.simpleapi as ms import unittest @@ -12,7 +12,6 @@ class VesuvioTests(unittest.TestCase): ws_name = "evs_raw" - def tearDown(self): if self.ws_name in mtd: mtd.remove(self.ws_name) @@ -110,7 +109,7 @@ class VesuvioTests(unittest.TestCase): det0 = evs_raw.getDetector(0) param = det0.getNumberParameter("t0") self.assertEqual(1, len(param)) - self.assertAlmostEqual(-0.4157, param[0],places=4) + self.assertAlmostEqual(-0.4157, param[0], places=4) def test_using_ip_file_adjusts_instrument_and_attaches_parameters_foil_mode(self): self._run_load("14188", "3", "FoilOut", "IP0005.dat") @@ -120,10 +119,10 @@ class VesuvioTests(unittest.TestCase): det0 = evs_raw.getDetector(0) param = det0.getNumberParameter("t0") self.assertEqual(1, len(param)) - self.assertAlmostEqual(-0.4157, param[0],places=4) + self.assertAlmostEqual(-0.4157, param[0], places=4) def test_sumspectra_set_to_true_gives_single_spectra_summed_over_all_inputs(self): - self._run_load("14188", "135-142", "SingleDifference","IP0005.dat",sum=True) + self._run_load("14188", "135-142", "SingleDifference","IP0005.dat",sum_runs=True) evs_raw = mtd[self.ws_name] # Verify @@ -136,7 +135,7 @@ class VesuvioTests(unittest.TestCase): self.assertAlmostEqual(0.10617318614513051, evs_raw.readE(0)[-1], places=DIFF_PLACES) def test_sumspectra_with_multiple_groups_gives_number_output_spectra_as_input_groups(self): - self._run_load("14188", "135-148;152-165", "SingleDifference","IP0005.dat",sum=True) + self._run_load("14188", "135-148;152-165", "SingleDifference","IP0005.dat",sum_runs=True) evs_raw = mtd[self.ws_name] # Verify @@ -157,7 +156,7 @@ class VesuvioTests(unittest.TestCase): range(3118,3132)) def test_sumspectra_set_to_true_gives_single_spectra_summed_over_all_inputs_with_foil_in(self): - self._run_load("14188", "3-15", "FoilIn", "IP0005.dat", sum=True) + self._run_load("14188", "3-15", "FoilIn", "IP0005.dat", sum_runs=True) evs_raw = mtd[self.ws_name] # Verify @@ -174,7 +173,7 @@ class VesuvioTests(unittest.TestCase): def test_sumspectra_with_multiple_groups_gives_number_output_spectra_as_input_groups_with_foil_in(self): - self._run_load("14188", "3-15;30-50", "FoilIn", "IP0005.dat", sum=True) + self._run_load("14188", "3-15;30-50", "FoilIn", "IP0005.dat", sum_runs=True) evs_raw = mtd[self.ws_name] # Verify @@ -199,23 +198,23 @@ class VesuvioTests(unittest.TestCase): for expected_id, det_id in zip(expected_ids, det_ids): self.assertEqual(expected_id, det_id) - def _run_load(self, runs, spectra, diff_opt, ip_file="", sum=False): - LoadVesuvio(Filename=runs,OutputWorkspace=self.ws_name, - SpectrumList=spectra,Mode=diff_opt,InstrumentParFile=ip_file, - SumSpectra=sum) + def _run_load(self, runs, spectra, diff_opt, ip_file="", sum_runs=False): + ms.LoadVesuvio(Filename=runs,OutputWorkspace=self.ws_name, + SpectrumList=spectra,Mode=diff_opt,InstrumentParFile=ip_file, + SumSpectra=sum_runs) self._do_ads_check(self.ws_name) def expected_size(): - if sum: + if sum_runs: if ";" in spectra: return 2 else: return 1 elif "-" in spectra: elements = spectra.split("-") - min,max=(int(elements[0]), int(elements[1])) - return max - min + 1 + min_e, max_e = (int(elements[0]), int(elements[1])) + return max_e - min_e + 1 elif "," in spectra: elements = spectra.strip().split(",") return len(elements) @@ -240,29 +239,29 @@ class VesuvioTests(unittest.TestCase): #================== Failure cases ================================ def test_missing_spectra_property_raises_error(self): - self.assertRaises(RuntimeError, LoadVesuvio, Filename="14188", + self.assertRaises(RuntimeError, ms.LoadVesuvio, Filename="14188", OutputWorkspace=self.ws_name) def test_load_with_invalid_spectra_raises_error(self): - self.assertRaises(RuntimeError, LoadVesuvio, Filename="14188", + self.assertRaises(RuntimeError, ms.LoadVesuvio, Filename="14188", OutputWorkspace=self.ws_name, SpectrumList="200") def test_load_with_spectra_that_are_just_monitors_raises_error(self): - self.assertRaises(RuntimeError, LoadVesuvio, Filename="14188", + self.assertRaises(RuntimeError, ms.LoadVesuvio, Filename="14188", OutputWorkspace=self.ws_name, SpectrumList="1") - self.assertRaises(RuntimeError, LoadVesuvio, Filename="14188", + self.assertRaises(RuntimeError, ms.LoadVesuvio, Filename="14188", OutputWorkspace=self.ws_name, SpectrumList="1-2") def test_load_with_invalid_difference_option_raises_error(self): - self.assertRaises(ValueError, LoadVesuvio, Filename="14188", + self.assertRaises(ValueError, ms.LoadVesuvio, Filename="14188", OutputWorkspace=self.ws_name, Mode="Unknown",SpectrumList="3-134") def test_load_with_difference_option_not_applicable_to_current_spectra_raises_error(self): - self.assertRaises(ValueError, LoadVesuvio, Filename="14188", + self.assertRaises(ValueError, ms.LoadVesuvio, Filename="14188", OutputWorkspace=self.ws_name, Mode="",SpectrumList="3-134") def test_raising_error_removes_temporary_raw_workspaces(self): - self.assertRaises(RuntimeError, LoadVesuvio, Filename="14188,14199", # Second run is invalid + self.assertRaises(RuntimeError, ms.LoadVesuvio, Filename="14188,14199", # Second run is invalid OutputWorkspace=self.ws_name, Mode="SingleDifference",SpectrumList="3-134") self._do_test_temp_raw_workspaces_not_left_around() diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/SANS2DReductionGUI.py b/Code/Mantid/Testing/SystemTests/tests/analysis/SANS2DReductionGUI.py index c3e549da5f943c8e629e9c8a5f911dd22d752ec0..f1003e6c927cad44d851427e19e8fd324f77ddec 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/SANS2DReductionGUI.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/SANS2DReductionGUI.py @@ -49,7 +49,7 @@ class SANS2DMinimalBatchReduction(stresstesting.MantidStressTest): fit_settings = batch.BatchReduce(BATCHFILE,'.nxs', combineDet='rear') def validate(self): - + self.disableChecking.append('Instrument') return "trans_test_rear","SANSReductionGUI.nxs" @@ -206,6 +206,7 @@ class SANS2DGUIBatchReduction(SANS2DMinimalBatchReduction): def validate(self): self.tolerance_is_reller = True self.tolerance = 1.0e-2 + self.disableChecking.append('Instrument') return "trans_test_rear","SANSReductionGUI.nxs" class SANS2DGUIReduction(SANS2DGUIBatchReduction): diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/SANSLoadersTest.py b/Code/Mantid/Testing/SystemTests/tests/analysis/SANSLoadersTest.py index 36a1f284ab1343f671c6c2374451fbcbe480a336..d9866a552403c0b76e640d6a47ce1589d4db46f5 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/SANSLoadersTest.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/SANSLoadersTest.py @@ -241,6 +241,7 @@ class LoadAddedEventDataSampleTestStressTest(stresstesting.MantidStressTest): os.remove(os.path.join(config['defaultsave.directory'],'SANS2D00022023-add.nxs')) def validate(self): + self.disableChecking.append('Instrument') return self._success diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/SANSWorkspaceTypeTest.py b/Code/Mantid/Testing/SystemTests/tests/analysis/SANSWorkspaceTypeTest.py new file mode 100644 index 0000000000000000000000000000000000000000..ac712a997e5f61a9d62875f8587ff6a95f06731a --- /dev/null +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/SANSWorkspaceTypeTest.py @@ -0,0 +1,94 @@ +#pylint: disable=invalid-name,no-init +import stresstesting +from mantid.simpleapi import * +from SANSUtility import can_load_as_event_workspace +import os + +# WORKAROUND FOR IMPORT ISSUE IN UBUNTU --- START +CAN_IMPORT_NXS_TEST = True +try: + import nxs # pylint: disable=unused-import +except ImportError: + CAN_IMPORT_NXS_TEST = False +# WORKAROUND FOR IMPORT ISSUE IN UBUNTU --- STOP + +def create_file_name(base_name): + temp_save_dir = config['defaultsave.directory'] + if temp_save_dir == '': + temp_save_dir = os.getcwd() + filename = os.path.join(temp_save_dir, base_name + '.nxs') + return filename + +def remove_temporary_file(filename): + if os.path.exists(filename): + os.remove(filename) + +def clean_up_workspaces(): + for element in mtd.getObjectNames(): + if element in mtd: + DeleteWorkspace(element) + +class SANSProcessedEventWorkspaceInFile(stresstesting.MantidStressTest): + ''' + Check if a processed nexus file is correctly detected to contain + an event workspace. + ''' + def __init__(self): + stresstesting.MantidStressTest.__init__(self) + self._success = False + + def runTest(self): + if CAN_IMPORT_NXS_TEST: + # Arrange + base_name = "processed_event" + filename = create_file_name(base_name) + ws = CreateSampleWorkspace("Event") + SaveNexusProcessed(InputWorkspace=ws, Filename = filename) + # Act + can_load = can_load_as_event_workspace(filename) + # Assert + if can_load: + self._success = True + else: + self._success = False + # Clean up + remove_temporary_file(filename) + clean_up_workspaces() + else: + self._success = True + + def validate(self): + return self._success + + +class SANSProcessedHistoWorkspaceInFile(stresstesting.MantidStressTest): + ''' + Check if a processed nexus file is correctly detected to contain + a histo workspace. + ''' + def __init__(self): + stresstesting.MantidStressTest.__init__(self) + self._success = False + + def runTest(self): + if CAN_IMPORT_NXS_TEST: + # Arrange + base_name = "processed_histo" + filename = create_file_name(base_name) + ws = CreateSampleWorkspace() + SaveNexusProcessed(InputWorkspace=ws, Filename = filename) + # Act + can_load = can_load_as_event_workspace(filename) + # Assert + if not can_load: + self._success = True + else: + self._success = False + # Clean up + remove_temporary_file(filename) + clean_up_workspaces() + else: + self._success = True + + def validate(self): + return self._success diff --git a/Code/Mantid/docs/source/algorithms/EnggCalibrate-v1.rst b/Code/Mantid/docs/source/algorithms/EnggCalibrate-v1.rst index b1a53e22374632dcfdfd0d2229a0b7c7dc96f668..c4627c82b1b22c5eb9bc069f3cf969a03a5316e3 100644 --- a/Code/Mantid/docs/source/algorithms/EnggCalibrate-v1.rst +++ b/Code/Mantid/docs/source/algorithms/EnggCalibrate-v1.rst @@ -11,8 +11,9 @@ Description .. warning:: - This algorithm is being developed for a specific instrument. It might get changed or even - removed without a notification, should instrument scientists decide to do so. + This algorithm is being developed for a specific instrument. It + might get changed or even removed without a notification, should + instrument scientists decide to do so. Utilises :ref:`algm-EnggFocus` which performs a TOF to dSpacing @@ -60,16 +61,21 @@ Usage out_tbl_name = 'out_params' ws_name = 'test' - van_ws_name = 'test_vanadium' Load('ENGINX00213855.nxs', OutputWorkspace=ws_name) - Load('ENGINX00193749.nxs', OutputWorkspace=van_ws_name) + + # Using precalculated Vanadium corrections. To calculate from scrach see EnggVanadiumCorrections + van_integ_ws = Load('ENGINX_precalculated_vanadium_run000236516_integration.nxs') + van_curves_ws = Load('ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs') + Difc1, Zero1 = EnggCalibrate(InputWorkspace=ws_name, - VanadiumWorkspace= van_ws_name, + VanIntegrationWorkspace=van_integ_ws, + VanCurvesWorkspace=van_curves_ws, ExpectedPeaks=[1.097, 2.1], Bank='1', OutputParametersTableName=out_tbl_name) Difc2, Zero2 = EnggCalibrate(InputWorkspace=ws_name, - VanadiumWorkspace= van_ws_name, + VanIntegrationWorkspace=van_integ_ws, + VanCurvesWorkspace=van_curves_ws, ExpectedPeaks=[1.097, 2.1], Bank='2') # You can produce an instrument parameters (iparam) file for GSAS. @@ -91,7 +97,8 @@ Usage DeleteWorkspace(out_tbl_name) DeleteWorkspace(ws_name) - DeleteWorkspace(van_ws_name) + DeleteWorkspace(van_integ_ws) + DeleteWorkspace(van_curves_ws) os.remove(GSAS_iparm_fname) @@ -99,9 +106,9 @@ Output: .. testoutput:: ExampleCalib - Difc1: 18404.35 - Zero1: -8.77 + Difc1: 18400.71 + Zero1: -3.17 The output table has 1 row(s) - Parameters from the table, Difc1: 18404.35, Zero1: -8.77 + Parameters from the table, Difc1: 18400.71, Zero1: -3.17 Output GSAS iparam file was written? True Number of lines of the GSAS iparam file: 35 diff --git a/Code/Mantid/docs/source/algorithms/EnggCalibrateFull-v1.rst b/Code/Mantid/docs/source/algorithms/EnggCalibrateFull-v1.rst index 3755d0a70f46066fb5ba1bf0aa1d492b229f82a3..80101fbff1fc4b24b8c3a9431d022854e8df1a18 100644 --- a/Code/Mantid/docs/source/algorithms/EnggCalibrateFull-v1.rst +++ b/Code/Mantid/docs/source/algorithms/EnggCalibrateFull-v1.rst @@ -11,8 +11,9 @@ Description .. warning:: - This algorithm is being developed for a specific instrument. It might get changed or even - removed without a notification, should instrument scientists decide to do so. + This algorithm is being developed for a specific instrument. It + might get changed or even removed without a notification, should + instrument scientists decide to do so. Allows to calibrate or correct for variations in detector position @@ -79,13 +80,18 @@ Usage .. testcode:: ExCalFull + # Using a workspace with only one spectrum to keep this test small and fast. + # Normally you would use a long Ceria run file or similar ws_name = 'ws_focussed' - van_ws_name = 'test_vanadium' Load('ENGINX00213855focussed.nxs', OutputWorkspace=ws_name) - Load('ENGINX00193749.nxs', OutputWorkspace=van_ws_name) + + # Using precalculated Vanadium corrections. To calculate from scrach see EnggVanadiumCorrections + van_integ_ws = Load('ENGINX_precalculated_vanadium_run000236516_integration.nxs') + van_curves_ws = Load('ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs') posTable = EnggCalibrateFull(Workspace=ws_name, - VanadiumWorkspace=van_ws_name, + VanIntegrationWorkspace=van_integ_ws, + VanCurvesWorkspace=van_curves_ws, ExpectedPeaks=[1.097, 2.1], Bank='1') detID = posTable.column(0)[0] @@ -99,7 +105,8 @@ Usage .. testcleanup:: ExCalFull DeleteWorkspace(ws_name) - DeleteWorkspace(van_ws_name) + DeleteWorkspace(van_integ_ws) + DeleteWorkspace(van_curves_ws) Output: @@ -116,14 +123,18 @@ Output: import os, csv ws_name = 'ws_focussed' - van_ws_name = 'test_vanadium' pos_filename = 'detectors_pos.csv' # Note that this is a small file which is not very meaningful but simple enough for # this test to run fast. Please user your (proper) run file. Load('ENGINX00213855focussed.nxs', OutputWorkspace=ws_name) - Load('ENGINX00193749.nxs', OutputWorkspace=van_ws_name) + + # Using precalculated Vanadium corrections. To calculate from scrach see EnggVanadiumCorrections + van_integ_ws = Load('ENGINX_precalculated_vanadium_run000236516_integration.nxs') + van_curves_ws = Load('ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs') + posTable = EnggCalibrateFull(Workspace=ws_name, - VanadiumWorkspace=van_ws_name, + VanIntegrationWorkspace=van_integ_ws, + VanCurvesWorkspace=van_curves_ws, ExpectedPeaks=[1.097, 2.1], Bank='1', OutDetPosFilename=pos_filename) @@ -147,7 +158,8 @@ Output: .. testcleanup:: ExCalFullWithOutputFile DeleteWorkspace(ws_name) - DeleteWorkspace(van_ws_name) + DeleteWorkspace(van_integ_ws) + DeleteWorkspace(van_curves_ws) import os os.remove(pos_filename) diff --git a/Code/Mantid/docs/source/algorithms/EnggFitPeaks-v1.rst b/Code/Mantid/docs/source/algorithms/EnggFitPeaks-v1.rst index d6945b8fd9326b5e9a86f70f64d23c424dc97427..ed2cce4378ce3806b11e64fe46f9d46152f77398 100644 --- a/Code/Mantid/docs/source/algorithms/EnggFitPeaks-v1.rst +++ b/Code/Mantid/docs/source/algorithms/EnggFitPeaks-v1.rst @@ -11,8 +11,9 @@ Description .. warning:: - This algorithm is being developed for a specific instrument. It might get changed or even - removed without a notification, should instrument scientists decide to do so. + This algorithm is being developed for a specific instrument. It + might get changed or even removed without a notification, should + instrument scientists decide to do so. The pattern is specified by providing a list of dSpacing values where diff --git a/Code/Mantid/docs/source/algorithms/EnggFocus-v1.rst b/Code/Mantid/docs/source/algorithms/EnggFocus-v1.rst index e098e7bccd310c9de5704f2bcaca8ecc38274739..cd815a58d60965bc5b76cab9cf0bdd47ac56f587 100644 --- a/Code/Mantid/docs/source/algorithms/EnggFocus-v1.rst +++ b/Code/Mantid/docs/source/algorithms/EnggFocus-v1.rst @@ -11,8 +11,9 @@ Description .. warning:: - This algorithm is being developed for a specific instrument. It might get changed or even - removed without a notification, should instrument scientists decide to do so. + This algorithm is being developed for a specific instrument. It + might get changed or even removed without a notification, should + instrument scientists decide to do so. Performs a Time-of-flight (TOF) to dSpacing conversion using calibrated pixel positions, focuses the values in dSpacing (summing @@ -46,17 +47,15 @@ Usage # Run the algorithm on an EnginX file data_ws = Load('ENGINX00213855.nxs') - van_ws = Load('ENGINX00236516.nxs') + + # Using precalculated Vanadium corrections. To calculate from scrach see EnggVanadiumCorrections + van_integ_ws = Load('ENGINX_precalculated_vanadium_run000236516_integration.nxs') + van_curves_ws = Load('ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs') focussed_ws = EnggFocus(InputWorkspace=data_ws, - VanadiumWorkspace=van_ws, + VanIntegrationWorkspace=van_integ_ws, + VanCurvesWorkspace=van_curves_ws, Bank='1') - # Use this additional options if you want to see the curve fit for the summed up vanadium data for - # that bank. It will generate a workspace called 'vanadium_curve' - # focussed_ws = EnggFocus(InputWorkspace=data_ws, - # VanadiumWorkspace=van_ws, - # Bank='1', - # OutVanadiumCurveFits='vanadium_curve') # Should have one spectrum only print "No. of spectra:", focussed_ws.getNumberHistograms() @@ -69,8 +68,9 @@ Usage .. testcleanup:: ExSimpleFocussing DeleteWorkspace(focussed_ws) - DeleteWorkspace(van_ws) DeleteWorkspace(data_ws) + DeleteWorkspace(van_integ_ws) + DeleteWorkspace(van_curves_ws) Output: diff --git a/Code/Mantid/docs/source/algorithms/EnggVanadiumCorrections-v1.rst b/Code/Mantid/docs/source/algorithms/EnggVanadiumCorrections-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..46a308aed122ea2f3bc5cb3dcb58d7be55a742b6 --- /dev/null +++ b/Code/Mantid/docs/source/algorithms/EnggVanadiumCorrections-v1.rst @@ -0,0 +1,141 @@ +.. algorithm:: + +.. summary:: + +.. alias:: + +.. properties:: + +Description +----------- + +.. warning:: + + This algorithm is being developed for a specific instrument and + technique. It might get changed or even removed without a + notification, should instrument scientists decide to do so. + +This algorithm performs calculations related to two types of +corrections with respect to a reference Vanadium diffraction dataset +(workspace), in the following order: + +1. sensitivity correction +2. pixel-by-pixel correction + +The algorithm outputs the features extracted from the Vanadium data +that are used to perform the two types of corrections, respectively: + +1. the *integration* of every individual Vanadium spectrum +2. a list of *curves* of aggregated counts as a function of + time-of-flight (one per bank) + +If an input/output workspace with diffraction data is passed, the +algorithm applies the corrections on the diffraction data workspace by +using the integration and curves calculated from the reference +Vanadium datasset. + +These outputs can be used to apply the corrections on a diffraction +data workspace in the same algorithm run, or be used subsequently to +apply corrections to different input workspaces. In practice, both +outputs need to be calculated only once for every reference Vanadium +dataset, while they would normally be used to correct a (possibly +long) series of different diffraction data workspaces. + +If a vanadium data workspace is passed, the algorithm will calculate +features that can then be used to apply Vanadium corrections in other +*Engg* algorithms. If in addition an input/output workspace with +diffraction data is passed, the corrections will be applied on +it. Afterwards the same corrections can be applied on different +diffraction data workspaces by calling again this algorithm and +providing as inputs the integraion and curves workspaces produced by +the first call. The same correction features (integration and curves) +can be re-used for as long as the same reference Vanadium diffraction +data is still valid. + +Normally this algorithm can be used in two different ways: +1. Pre-calculate correction features from a Vanadium data workspace. +2. Apply Vanadium corrections once the correction features have been + calculated. + +Examples of these two alternatives are shown below. In the first +option, only the input VanadiumWorkspace is required, and the two +outputs (integration and curves workspaces) are produced +normally. Optionally, a diffraction data workspace can be passed in +the input property InputWorkspace for it to be corrected. In the +second option, the corrections can be applied by using pre-calculated +features from a previous run of this algorithm (both +IntegrationWorkspace and CurvesWorkspace have to be passed as input +properties, If these two properties are not passed, they will be +re-calculated provided that a VanadiumWorkspace is passed which is not +recommended). All the calculations (integration, sums, divisions, +etc.) are done in the d-spacing space. + +This algorithm is used as a child algorithm in the algorithms +:ref:`algm-EnggFocus` and :ref:`algm-EnggCalibrateFull`. + +Usage +----- + +.. include:: ../usagedata-note.txt + +**Example - apply Vanadium corrections on a sample workspace from an EnginX run file:** + +.. testcode:: ExVanadiumCorr + + # # To generate the pre-calculated features (integration and curves), for a new + # Vanadium run, and apply the corrections on a workspace: + # + # data_ws = Load('ENGINX00213855.nxs') + # van_ws = Load('ENGINX00236516.nxs') + # EnggVanadiumCorrections(Workspace = data_ws, VanadiumWorkspace = van_ws + # IntegrationWorkspace = 'integ_ws', + # CurvesWorkspace = 'curves_ws') + # + # # Now you can save the two pre-calculated features / workspaces: + # SaveNexus(InputWorkspace='integ_ws', + # Filename='ENGINX_precalculated_vanadium_run000236516_integration.nxs',) + # SaveNexus(InputWorkspace='curves_ws', + # Filename='ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs') + # + # # (not done here because the Vanadium run file has a large number of events) + # + # # Below we use the pre-calculated features that can be obtained with + # # the commands listed above. + + sample_ws = Load('ENGINX00213855.nxs') + integ_ws = LoadNexus('ENGINX_precalculated_vanadium_run000236516_integration.nxs') + curves_ws = LoadNexus('ENGINX_precalculated_vanadium_run000236516_bank_curves.nxs') + EnggVanadiumCorrections(Workspace = sample_ws, + IntegrationWorkspace = integ_ws, + CurvesWorkspace = curves_ws) + + # Should have one spectrum only + print "No. of spectra:", sample_ws.getNumberHistograms() + + # Print a few arbitrary integrated spectra + ws_idx = 400 + idx_count = 3 + integ_ws = Integration(sample_ws, StartWorkspaceIndex=ws_idx, + EndWorkspaceIndex=ws_idx+idx_count) + fmt = "For workspace index {0:d} the spectrum integration is {1:.3f}" + for i in range(idx_count): + print fmt.format(ws_idx+i, integ_ws.readY(i)[0]) + +.. testcleanup:: ExVanadiumCorr + + DeleteWorkspace(sample_ws) + DeleteWorkspace(integ_ws) + DeleteWorkspace(curves_ws) + +Output: + +.. testoutput:: ExVanadiumCorr + + No. of spectra: 2513 + For workspace index 400 the spectrum integration is 23.998 + For workspace index 401 the spectrum integration is 23.799 + For workspace index 402 the spectrum integration is 22.872 + +.. categories:: + +.. sourcelink:: diff --git a/Code/Mantid/docs/source/algorithms/IndirectCalibration-v1.rst b/Code/Mantid/docs/source/algorithms/IndirectCalibration-v1.rst index 1dff27bc7d3550a74bf48289b5258b167ad4c72a..6b7001cb2e8132ff1290d7027ccca210a6cd6a10 100644 --- a/Code/Mantid/docs/source/algorithms/IndirectCalibration-v1.rst +++ b/Code/Mantid/docs/source/algorithms/IndirectCalibration-v1.rst @@ -17,6 +17,9 @@ algorithm which are then merged into a single run using :ref:`MergeRuns <algm-MergeRuns>`, a flat background is then calculated and normalised to give the output workspace. +.. note:: + This algorithm only supports files containg histogram data. + Workflow -------- diff --git a/Code/Mantid/docs/source/algorithms/LoadDNSLegacy-v1.rst b/Code/Mantid/docs/source/algorithms/LoadDNSLegacy-v1.rst index f55151509fa5136112ef1d80c9da5b3025163072..c184bbe3e93614333c375e7dbf1f092be4a56d95 100644 --- a/Code/Mantid/docs/source/algorithms/LoadDNSLegacy-v1.rst +++ b/Code/Mantid/docs/source/algorithms/LoadDNSLegacy-v1.rst @@ -9,10 +9,17 @@ Description ----------- -Loads a DNS legacy .d_dat data file into a :ref:`Workspace2D <Workspace2D>` with -the given name. +.. warning:: -The loader rotates the detector bank in the position given in the data file. + This algorithm is being developed for a specific instrument. It might get changed or even + removed without a notification, should instrument scientists decide to do so. + +This algorithm loads a DNS legacy data file into a :ref:`Workspace2D <Workspace2D>`. Two workspaces will be created: + +- raw data workspace with the given name. +- workspace with normalization data (monitor counts or experiment duration by user's choice). The normalization workspace is named same as the data workspace, but has suffix "_NORM". + +The loader rotates the detector bank in the position given in the data file. No operations on the neutron counts are performed. Sample logs are dublicated for both, data and normalization workspaces. This algorithm only supports DNS instrument in its configuration before major upgrade. @@ -27,10 +34,9 @@ Usage datafile = 'dn134011vana.d_dat' # Load dataset - ws = LoadDNSLegacy(datafile, Polarisation='x') + ws = LoadDNSLegacy(datafile, Polarisation='x', Normalization='monitor') - print "This workspace has", ws.getNumDims(), "dimensions and has", \ - ws.getNumberHistograms(), "histograms." + print "This workspace has", ws.getNumDims(), "dimensions and has", ws.getNumberHistograms(), "histograms." Output: diff --git a/Code/Mantid/docs/source/algorithms/LoadEmptyVesuvio-v1.rst b/Code/Mantid/docs/source/algorithms/LoadEmptyVesuvio-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..164799f06880d8f23cd49ff385c65971e4c989ff --- /dev/null +++ b/Code/Mantid/docs/source/algorithms/LoadEmptyVesuvio-v1.rst @@ -0,0 +1,66 @@ +.. algorithm:: + +.. summary:: + +.. alias:: + +.. properties:: + +Description +----------- + +Loads an empty workspace containing the VESUVIO instrument using +:ref:`LoadEmptyInstrument <algm-LoadEmptyInstrument>` and updates the detector +positions using a PAR file using the :ref:`UpdateInstrumentFromFile +<algm-UpdateInstrumentFromFile>` algorithm. + +Usage +----- + +.. include:: ../usagedata-note.txt + +**Example - LoadDefaultDetectorPositions** + +.. testcode:: exLoadDefaultDetectorPositions + + evs_ws = LoadEmptyVesuvio() + + evs = evs_ws.getInstrument() + + sample = evs.getSample() + b_det_1 = evs_ws.getDetector(2) + + b_det_1_l1 = sample.getPos().distance(b_det_1.getPos()) + + print "First backscattering detector L1 = %.5fm" % (b_det_1_l1) + +Output: + +.. testoutput:: exLoadDefaultDetectorPositions + + First backscattering detector L1 = 0.67477m + +**Example - LoadWithPARFileDetectorPositions** + +.. testcode:: exLoadWithPARFileDetectorPositions + + evs_ws = LoadEmptyVesuvio(InstrumentParFile='IP0005.dat') + + evs = evs_ws.getInstrument() + + sample = evs.getSample() + b_det_1 = evs_ws.getDetector(2) + + b_det_1_l1 = sample.getPos().distance(b_det_1.getPos()) + + print "First backscattering detector L1 = %.5fm" % (b_det_1_l1) + +Output: + +.. testoutput:: exLoadWithPARFileDetectorPositions + + First backscattering detector L1 = 0.67080m + +.. categories:: + +.. sourcelink:: diff --git a/Code/Mantid/docs/source/concepts/EventWorkspace.rst b/Code/Mantid/docs/source/concepts/EventWorkspace.rst index 09b7e0428229fe7f16ed0f89627d2a2e02a235d9..b7bcce91b4b590366caf825176def99994d2de7b 100644 --- a/Code/Mantid/docs/source/concepts/EventWorkspace.rst +++ b/Code/Mantid/docs/source/concepts/EventWorkspace.rst @@ -121,6 +121,8 @@ You can go around this by forcing the parallel loop with a plain PARALLEL\_FOR() macro. **Make sure you do not read from the same spectrum in parallel!** +.. include:: WorkspaceNavigation.txt + .. categories:: Concepts \ No newline at end of file diff --git a/Code/Mantid/docs/source/concepts/MatrixWorkspace.rst b/Code/Mantid/docs/source/concepts/MatrixWorkspace.rst index 244e23d5a98a093c09d39ad795340c9a7ec3d75d..f885f4d67a0f20d0425e4ff794d1da29b5979992 100644 --- a/Code/Mantid/docs/source/concepts/MatrixWorkspace.rst +++ b/Code/Mantid/docs/source/concepts/MatrixWorkspace.rst @@ -22,17 +22,11 @@ Optionally: - A distribution flag - A list of 'masked' bins -Concrete Matrix Workspaces --------------------------- - -- WorkspaceSingleValue - Holds a single number (and X & error value, if - desired). Mainly used for workspace algebra, e.g. to divide all bins - in a 2D workspace by a single value. -- :ref:`Workspace2D <Workspace2D>` - A workspace for holding two - dimensional data in memory. This is the most commonly used workspace. -- :ref:`EventWorkspace <EventWorkspace>` - A workspace that retains the - individual neutron event data. +Documentation on the :ref:`CreateWorkspace <algm-CreateWorkspace>` +algorithm may also be useful. +.. include:: WorkspaceNavigation.txt + More information on working with them: `Interacting with Matrix Workspaces <http://www.mantidproject.org/Interacting_with_Workspaces>`__. diff --git a/Code/Mantid/docs/source/concepts/TableWorkspaces.rst b/Code/Mantid/docs/source/concepts/TableWorkspaces.rst index 73c30efa06ef589e8dd257c487775588d9765c14..bbc8852477d41300270dddac91e4ffb676a25d47 100644 --- a/Code/Mantid/docs/source/concepts/TableWorkspaces.rst +++ b/Code/Mantid/docs/source/concepts/TableWorkspaces.rst @@ -94,6 +94,8 @@ c\_plus\_plus\_type must be a copyable type and operators << and >> must be defined. There is also DECLARE\_TABLEPOINTERCOLUMN macro for declaring non-copyable types, but it has never been used. +.. include:: WorkspaceNavigation.txt + .. categories:: Concepts \ No newline at end of file diff --git a/Code/Mantid/docs/source/concepts/Workspace2D.rst b/Code/Mantid/docs/source/concepts/Workspace2D.rst index 6a7e2c28970dddde83d6b23a044a4a57d7a6e2dc..d9df4833ae25177c02ec41da13c220bb9ac04d0c 100644 --- a/Code/Mantid/docs/source/concepts/Workspace2D.rst +++ b/Code/Mantid/docs/source/concepts/Workspace2D.rst @@ -15,6 +15,11 @@ only contains bin information and does not contain the underlying event data. The :ref:`EventWorkspace <EventWorkspace>` presents itself as a histogram (with X,Y,E values) but preserves the underlying event data. +For more information on what a Workspace2D contains, see +:ref:`MatrixWorkspace <MatrixWorkspace>`. + +.. include:: WorkspaceNavigation.txt + .. categories:: Concepts \ No newline at end of file diff --git a/Code/Mantid/docs/source/concepts/WorkspaceGroup.rst b/Code/Mantid/docs/source/concepts/WorkspaceGroup.rst index 467a65ceb38f59aa63850d477e11c0a8355b7aff..d56e64137435d7f69492e4062d8f43632a04d043 100644 --- a/Code/Mantid/docs/source/concepts/WorkspaceGroup.rst +++ b/Code/Mantid/docs/source/concepts/WorkspaceGroup.rst @@ -21,6 +21,8 @@ Un-grouping Workspaces - Select the WorkspaceGroup and click "Ungroup". - Use the :ref:`UnGroupWorkspace <algm-UnGroupWorkspace>` algorithm. +.. include:: WorkspaceNavigation.txt + .. categories:: Concepts \ No newline at end of file diff --git a/Code/Mantid/docs/source/concepts/WorkspaceNavigation.txt b/Code/Mantid/docs/source/concepts/WorkspaceNavigation.txt new file mode 100644 index 0000000000000000000000000000000000000000..213a70a7a9db976f6e58f3cab2c4ac271520f723 --- /dev/null +++ b/Code/Mantid/docs/source/concepts/WorkspaceNavigation.txt @@ -0,0 +1,22 @@ +Other Information on Workspaces +------------------------------- + +- :ref:`Workspace <Workspace>` - Overview of workspaces, which include the following classes: + - :ref:`MatrixWorkspace <MatrixWorkspace>` - A base class that contains + among others: + + - WorkspaceSingleValue - Holds a single number (and X & error value, + if desired). Mainly used for workspace algebra, e.g. to divide all + bins in a 2D workspace by a single value. + - :ref:`Workspace2D <Workspace2D>` - A workspace for holding two + dimensional data in memory, this is the most commonly used + workspace. + - :ref:`EventWorkspace <EventWorkspace>` - A workspace that retains the + individual neutron event data. + + - :ref:`TableWorkspace <Table Workspaces>` - A workspace holding data in + rows of columns having a particular type (e.g. text, integer, ...). + - :ref:`WorkspaceGroup <WorkspaceGroup>` - A container for a collection of + workspaces. Algorithms given a group as input run sequentially on + each member of the group. + \ No newline at end of file diff --git a/Code/Mantid/docs/source/interfaces/Indirect_DataReduction.rst b/Code/Mantid/docs/source/interfaces/Indirect_DataReduction.rst index 5c1e69501dad60d74e3947921d8b11be843eceb8..68e7fce063e6b63ed5768f88663165e62eadeb84 100644 --- a/Code/Mantid/docs/source/interfaces/Indirect_DataReduction.rst +++ b/Code/Mantid/docs/source/interfaces/Indirect_DataReduction.rst @@ -79,6 +79,11 @@ Sum Files Load Log Files If selected the sample logs will be laoded from each of the run files. +Efixed + This option allows you to override the default fixed final energy for the + analyser and reflection number setting. This can be useful in correcting an + offset peak caused by the sample being slightly out of centre. + Grouping Provides option of how data should be grouped. diff --git a/Code/Mantid/instrument/DNS_Definition_PAonly.xml b/Code/Mantid/instrument/DNS_Definition_PAonly.xml index c34560606067404f7d69f1ccacc41a1f492441ea..15fab5107e83e6003b9de06370116d574f8369eb 100644 --- a/Code/Mantid/instrument/DNS_Definition_PAonly.xml +++ b/Code/Mantid/instrument/DNS_Definition_PAonly.xml @@ -22,13 +22,14 @@ </component> <type name="moderator" is="Source"></type> <!-- monitor --> - <component type="monitor" idlist="monitor"> + <!--<component type="monitor" idlist="monitor"> <location z="-0.229" /> </component> <type name="monitor" is="monitor"></type> <idlist idname="monitor"> <id val="-1"/> </idlist> + --> <!-- Sample position --> <component type="sample-position"> <location y="0.0" x="0.0" z="0.0" /> diff --git a/Code/Mantid/instrument/MolDyn_qmax_2_Parameters.xml b/Code/Mantid/instrument/MolDyn_simul_2_Parameters.xml similarity index 95% rename from Code/Mantid/instrument/MolDyn_qmax_2_Parameters.xml rename to Code/Mantid/instrument/MolDyn_simul_2_Parameters.xml index be6c1cb93aa18d97eab8733864883b24e8a9c3c4..92a2e684a2111ae6108333078b708b8a0a3d4bc5 100644 --- a/Code/Mantid/instrument/MolDyn_qmax_2_Parameters.xml +++ b/Code/Mantid/instrument/MolDyn_simul_2_Parameters.xml @@ -8,7 +8,7 @@ </parameter> <parameter name="analyser" type="string"> - <value val="qmax" /> + <value val="simul" /> </parameter> <parameter name="reflection" type="string"> diff --git a/Code/Mantid/instrument/MolDyn_qmax_4_Parameters.xml b/Code/Mantid/instrument/MolDyn_simul_4_Parameters.xml similarity index 95% rename from Code/Mantid/instrument/MolDyn_qmax_4_Parameters.xml rename to Code/Mantid/instrument/MolDyn_simul_4_Parameters.xml index 08f7a37ee470a72b45459674b25c2d4af45c1d92..9f6961a7554add7b9e63be5219003ea1dbe0dc27 100644 --- a/Code/Mantid/instrument/MolDyn_qmax_4_Parameters.xml +++ b/Code/Mantid/instrument/MolDyn_simul_4_Parameters.xml @@ -8,7 +8,7 @@ </parameter> <parameter name="analyser" type="string"> - <value val="qmax" /> + <value val="simul" /> </parameter> <parameter name="reflection" type="string"> diff --git a/Code/Mantid/scripts/CMakeLists.txt b/Code/Mantid/scripts/CMakeLists.txt index 476844cc55317cdb49146e20ed593a3f8ad830f3..7c00c508283118881ab92f0d7c8e6b079bc1f0f3 100644 --- a/Code/Mantid/scripts/CMakeLists.txt +++ b/Code/Mantid/scripts/CMakeLists.txt @@ -29,9 +29,9 @@ set ( TEST_PY_FILES test/RunDescriptorTest.py test/SansIsisGuiSettings.py test/SANSBatchModeTest.py + test/SANSCommandInterfaceTest.py test/SANSUtilityTest.py test/SANSIsisInstrumentTest.py - test/SANSCommandInterfaceTest.py test/SANSReductionStepsUserFileTest.py test/SettingsTest.py test/ReductionSettingsTest.py diff --git a/Code/Mantid/scripts/Engineering/EnggUtils.py b/Code/Mantid/scripts/Engineering/EnggUtils.py index ae1a64ddfe2982b00845c558ad07be055f581427..410ea6ed66cf3ddb0fd5231f8be0ce211f4596f3 100644 --- a/Code/Mantid/scripts/Engineering/EnggUtils.py +++ b/Code/Mantid/scripts/Engineering/EnggUtils.py @@ -4,12 +4,7 @@ from mantid.api import * import mantid.simpleapi as sapi -# numpy needed only for Vanadium calculations, at the moment -import numpy as np - ENGINX_BANKS = ['', 'North', 'South', '1', '2'] -# banks (or groups) to which the pixel-by-pixel correction should be applied -ENGINX_BANKS_FOR_PIXBYPIX_CORR = [1,2] def readInExpectedPeaks(filename, expectedGiven): """ @@ -186,6 +181,45 @@ def generateOutputParTable(name, difc, zero): tbl.addColumn('double', 'zero') tbl.addRow([float(difc), float(zero)]) +def applyVanadiumCorrections(self, ws, indices, vanWS, vanIntegWS, vanCurvesWS): + """ + Apply the EnggVanadiumCorrections algorithm on the workspace given, by using the algorithm + EnggVanadiumCorrections + + @param parent :: parent (Mantid) algorithm that wants to run this + @param ws :: workspace to correct (modified in place) + @param indices :: workspace indices that are being processed (those not included will be ignored) + @param vanWS :: workspace with data from a Vanadium run + @param vanIntegWS :: alternatively to vanWS, pre-calculated integration from Vanadium data + @param vanIntegWS :: alternatively to vanWS, pre-calculated bank curves from Vanadium data + """ + if vanWS and vanWS.getNumberHistograms() < len(indices): + raise ValueError("Inconsistency in inputs: the Vanadium workspace has less spectra (%d) than " + "the number of workspace indices to process (%d)"% + (vanWS.getNumberHistograms(),len(indices))) + elif vanIntegWS and vanCurvesWS: + # filter only indices from vanIntegWS (crop the table) + tbl = sapi.CreateEmptyTableWorkspace() + tbl.addColumn('double', 'Spectra Integration') + for i in indices: + tbl.addRow([vanIntegWS.cell(i,0)]) + vanIntegWS = tbl + + # These corrections rely on ToF<->Dspacing conversions, so they're done after the calibration step + # sapi.EnggVanadiumCorrections(Workspace=ws, VanadiumWorkspace=vanWS, + # IntegrationWorkspace=vanIntegWS, + # CurvesWorkspace=vanCurvesWS) + alg = self.createChildAlgorithm('EnggVanadiumCorrections') + if ws: + alg.setProperty('Workspace', ws) + if vanWS: + alg.setProperty('VanadiumWorkspace', vanWS) + if vanIntegWS: + alg.setProperty('IntegrationWorkspace', vanIntegWS) + if vanCurvesWS: + alg.setProperty('CurvesWorkspace', vanCurvesWS) + alg.execute() + def convertToDSpacing(parent, ws): """ Converts a workspace to dSpacing using 'ConvertUnits' as a child algorithm. @@ -336,373 +370,3 @@ def write_ENGINX_GSAS_iparam_file(output_file, difc, zero, ceria_run=241391, with open(output_file, 'w') as of: of.writelines(output_lines) - -# ---------------------------------------------------------------------------------------- -# Functions for Vanadium corrections follow. These could be converted into an algorithm -# that would be used as a child from EnginXFocus and EnginXCalibrate -# ---------------------------------------------------------------------------------------- - -def applyVanadiumCorrection(parent, ws, vanWS, precalcInteg=None): - """ - Vanadium correction as done for the EnginX instrument. This is in principle meant to be used - in EnginXFocus and EnginXCalibrateFull. The process consists of two steps: - 1. sensitivity correction - 2. pixel-by-pixel correction - - 1. is performed for very pixel/detector/spectrum: scale every spectrum/curve by a - scale factor proportional to the number of neutrons in that spectrum - - 2. Correct every pixel by dividing by a curve (spline) fitted on the summed spectra - (summing banks separately). - - The sums and fits are done in d-spacing. - - @param parent :: parent (Mantid) algorithm that wants to run this - @param ws :: workspace to work on, must be a Vanadium (V-Nb) run (in/out) - @param vanWS :: workspace with Vanadium data - @param precalcIntegration :: pre-calculated integral of every spectrum for the vanadium data - - @param curvesWS :: workspace with the fitting workspace(s) corresponding to the bank(s) - processed (3 spectra workspace for every bank). If several banks have been processed there - will be 3 spectra for each bank. - """ - if vanadiumWorkspaceIsPrecalculated(vanWS): - if not precalcInteg: - raise ValueError('A pre-calcuated vanadium curves workspace was passed instead of raw ' - 'Vanadium run data, but no spectra integration results were provided.') - - rows = precalcInteg.rowCount() - spectra = ws.getNumberHistograms() - if rows < spectra: - raise ValueError("The number of histograms in the input data workspace (%d) is bigger " - "than the number of rows (spectra) in the integration workspace (%d)"% - (rows, spectra)) - - applySensitivityCorrection(parent, ws, vanWS, precalcInteg) - - # apply this to both Engin-X banks, each with separate calculations - curvesDict = applyPixByPixCorrection(parent, ws, vanWS, ENGINX_BANKS_FOR_PIXBYPIX_CORR) - curvesWS = prepareCurvesWS(parent, curvesDict) - return curvesWS - -def applySensitivityCorrection(parent, ws, vanWS, precalcInteg): - """ - Applies the first step of the Vanadium corrections on the given workspace. - Operations are done in ToF - - @param parent :: parent (Mantid) algorithm that wants to run this - @param ws :: workspace (in/out) - @param vanWS :: workspace with Vanadium data - @param precalcIntegration :: pre-calculated integral of every spectrum for the vanadium data - """ - if not vanadiumWorkspaceIsPrecalculated(vanWS): - applySensitivityCorrectionFromRawData(parent, ws, vanWS) - else: - for i in range(0, ws.getNumberHistograms()): - scaleFactor = precalcInteg.cell(i,0)/vanWS.blocksize() - ws.setY(i, np.divide(ws.dataY(i), scaleFactor)) - - -def applySensitivityCorrectionFromRawData(parent, ws, vanWS): - """ - This does the real calculations behind applySensitivityCorrection() for when we are given raw - data from a Vanadium run. - """ - expectedDim = 'Time-of-flight' - dimType = vanWS.getXDimension().getName() - if expectedDim != dimType: - raise ValueError("This algorithm expects a workspace with %s X dimension, but " - "the X dimension of the input workspace is: '%s'" % (expectedDim, dimType)) - - integWS = integrateSpectra(parent, vanWS) - if 1 != integWS.blocksize() or integWS.getNumberHistograms() < ws.getNumberHistograms(): - raise RuntimeError("Error while integrating vanadium workspace, the Integration algorithm " - "produced a workspace with %d bins and %d spectra. The workspace " - "being integrated has %d spectra."% - (integWS.blocksize(), integWS.getNumberHistograms(), - vanWS.getNumberHistograms())) - - for i in range(0, ws.getNumberHistograms()): - scaleFactor = integWS.readY(i)[0] / vanWS.blocksize() - ws.setY(i, np.divide(ws.dataY(i), scaleFactor)) # DivideSpec(ws, scaleFactor) - -def applyPixByPixCorrection(parent, ws, vanWS, banks): - """ - Applies the second step of the Vanadium correction on the given workspace: pixel by pixel - divides by a curve fitted to the sum of the set of spectra of the corresponding bank. - - @param parent :: parent (Mantid) algorithm that wants to run this - @param ws :: workspace to work on / correct - @param banks :: list of banks to correct - normally would expect all banks: [1,2] - @param vanWS :: workspace with Vanadium data - - @param curves :: a dictionary with bank id's (numbers) as keys and fitting output workspace - as values - """ - if vanadiumWorkspaceIsPrecalculated(vanWS): - # No instrument -> precalculated curve(s) - curves = _precalcWStoDict(parent, vanWS) - else: - # Have to calculate curves. get one curve per bank, in d-spacing - curves = fitCurvesPerBank(parent, vanWS, banks) - - # divide the spectra by their corresponding bank curve - divideByCurves(parent, ws, curves) - - return curves - -def divideByCurves(parent, ws, curves): - """ - Expects a workspace in ToF units. All operations are done in-place (the workspace is - input/output). For every bank-curve pair, divides the corresponding spectra in the - workspace by the (simulated) fitted curve. The division is done in d-spacing (the - input workspace is converted to d-spacing inside this method, but results are converted - back to ToF before returning from this method). The curves workspace is expected in - d-spacing units (since it comes from fitting a sum of spectra for a bank or group of - detectors). - - This method is capable of dealing with workspaces with range and bin size different from - the range and bin size of the curves. It will rebin the curves workspace to match the - input 'ws' workspace (using the algorithm RebinToWorkspace). - - @param parent :: parent (Mantid) algorithm that wants to run this - @param ws :: workspace with (sample) spectra to divide by curves fitted to Vanadium spectra - @param curves :: dictionary of fitting workspaces (in d-spacing), one per bank. The keys are - the bank identifier and the values are their fitting workspaces. The fitting workspaces are - expected as returned by the algorithm 'Fit': 3 spectra: original data, simulated data with fit, - difference between original and simulated data. - """ - # Note that this division could use the algorithm 'Divide' - # This is simple and more efficient than using divide workspace, which requires - # cropping separate workspaces, dividing them separately, then appending them - # with AppendSpectra, etc. - ws = convertToDSpacing(parent, ws) - for b in curves: - # process all the spectra (indices) in one bank - fittedCurve = curves[b] - idxs = getWsIndicesForBank(ws, b) - - if not idxs: - pass - - # This RebinToWorkspace is required here: normal runs will have narrower range of X values, - # and possibly different bin size, as compared to (long) Vanadium runs. Same applies to short - # Ceria runs (for Calibrate -non-full) and even long Ceria runs (for Calibrate-Full). - rebinnedFitCurve = rebinToMatchWS(parent, fittedCurve, ws) - - for i in idxs: - # take values of the second spectrum of the workspace (fit simulation - fitted curve) - ws.setY(i, np.divide(ws.dataY(i), rebinnedFitCurve.readY(1))) - - # finally, convert back to ToF - ws = convertToToF(parent, ws) - -def fitCurvesPerBank(parent, vanWS, banks): - """ - Fits one curve to every bank (where for every bank the data fitted is the result of - summing up all the spectra of the bank). The fitting is done in d-spacing. - - @param parent :: parent (Mantid) algorithm that wants to run this - @param vanWS :: Vanadium run workspace to fit, expected in TOF units as they are archived - @param banks :: list of banks to consider which is normally all the banks of the instrument - - @returns dictionary of fit workspaces, with one per bank given in the inputs. These workspaces are - in d-spacing units. The bank identifiers are the keys, and the workspaces are the values. - """ - curves = {} - for b in banks: - indices = getWsIndicesForBank(vanWS, b) - if not indices: - # no indices at all for this bank, not interested in it, don't add it to the dictionary - # (as when doing Calibrate (not-full)) which does CropData() the original workspace - continue - - wsToFit = cropData(parent, vanWS, indices) - wsToFit = convertToDSpacing(parent, wsToFit) - wsToFit = sumSpectra(parent, wsToFit) - - fitWS = fitBankCurve(parent, wsToFit, b) - curves.update({b: fitWS}) - - return curves - -def fitBankCurve(parent, vanWS, bank): - """ - Fits a spline to a single-spectrum workspace (in d-spacing) - - @param parent :: parent (Mantid) algorithm that wants to run this - @param vanWS :: Vanadium workspace to fit (normally this contains spectra for a single bank) - @param bank :: instrument bank this is fitting is done for - - @returns fit workspace (MatrixWorkspace), with the same number of bins as the input - workspace, and the Y values simulated from the fitted curve - """ - expectedDim = 'd-Spacing' - dimType = vanWS.getXDimension().getName() - if expectedDim != dimType: - raise ValueError("This algorithm expects a workspace with %s X dimension, but " - "the X dimension of the input workspace is: '%s'" % (expectedDim, dimType)) - - if 1 != vanWS.getNumberHistograms(): - raise ValueError("The workspace does not have exactly one histogram. Inconsistency found.") - - # without these min/max parameters 'BSpline' would completely misbehave - xvec = vanWS.readX(0) - startX = min(xvec) - endX = max(xvec) - functionDesc = 'name=BSpline, Order=3, StartX=' + str(startX) +', EndX=' + str(endX) + ', NBreak=12' - fitAlg = parent.createChildAlgorithm('Fit') - fitAlg.setProperty('Function', functionDesc) - fitAlg.setProperty('InputWorkspace', vanWS) - # WorkspaceIndex is left to default '0' for 1D function fits - # StartX, EndX could in principle be left to default start/end of the spectrum, but apparently - # not safe for 'BSpline' - fitAlg.setProperty('CreateOutput', True) - fitAlg.execute() - - success = fitAlg.getProperty('OutputStatus').value - parent.log().information("Fitting Vanadium curve for bank %s, using function '%s', result: %s" % - (bank, functionDesc, success)) - - detailMsg = ("It seems that this algorithm failed to to fit a function to the summed " - "spectra of a bank. The function definiton was: '%s'") % functionDesc - - outParsPropName = 'OutputParameters' - try: - fitAlg.getProperty(outParsPropName).value - except RuntimeError: - raise RuntimeError("Could not find the parameters workspace expected in the output property " + - OutParsPropName + " from the algorithm Fit. It seems that this algorithm failed." + - detailMsg) - - outWSPropName = 'OutputWorkspace' - fitWS = None - try: - fitWS = fitAlg.getProperty(outWSPropName).value - except RuntimeError: - raise RuntimeError("Could not find the data workspace expected in the output property " + - outWSPropName + ". " + detailMsg) - - mtd['engg_van_ws_dsp'] = vanWS - mtd['engg_fit_ws_dsp'] = fitWS - - return fitWS - -def prepareCurvesWS(parent, curvesDict): - """ - Simply concantenates or appends fitting output workspaces as produced by the algorithm Fit (with 3 - spectra each). The groups of 3 spectra are added sorted by the bank ID (number). This could also - produce a workspace group with the individual workspaces in it, but the AppendSpectra solution - seems simpler. - - @param parent :: parent (Mantid) algorithm that wants to run this - @param curvesDict :: dictionary with fitting workspaces produced by 'Fit' - - @returns a workspace where all the input workspaces have been concatenated, with 3 spectra per - workspace / bank - """ - if 0 == len(curvesDict): - raise RuntimeError("Expecting a dictionary with fitting workspaces from 'Fit' but got an " - "empty dictionary") - if 1 == len(curvesDict): - return curvesDict.values()[0] - - keys = sorted(curvesDict) - ws = curvesDict[keys[0]] - for idx in range(1, len(keys)): - nextWS = curvesDict[keys[idx]] - ws = appendSpec(parent, ws, nextWS) - - return ws - -def vanadiumWorkspaceIsPrecalculated(ws): - """ - Is the workspace precalculated curve(s)? If not, it must be raw data from a Vanadium run - - @param ws :: workspace passed in an Engg algorithm property - - @returns True if the workspace seems to contain precalculated bank curves for Vanadium data - """ - inst = ws.getInstrument() - return not inst or not inst.getName() - -def _precalcWStoDict(parent, ws): - """ - Turn a workspace with one or more fitting results (3 spectra per curve fitted), that comes - with all the spectra appended, into a dictionary of individual fitting results. - - @param parent :: parent (Mantid) algorithm that wants to run this - @param ws workspace with fitting results (3 spectra per curve fitted) as provided by Fit - - @returns dictionary with every individual fitting result (3 spectra) - - """ - curves = {} - - if 0 != (ws.getNumberHistograms() % 3): - raise RuntimeError("A workspace without instrument definition has ben passed, so it is " - "expected to have fitting results, but it does not have a number of " - "histograms multiple of 3. Number of hsitograms found: %d"% - ws.getNumberHistograms()) - - for wi in range(0, ws.getNumberHistograms()/3): - indiv = cropData(parent, ws, [wi, wi+2]) - curves.update({wi: indiv}) - - return curves - -def appendSpec(parent, ws1, ws2): - """ - Uses the algorithm 'AppendSpectra' to append the spectra of ws1 and ws2 - - @param parent :: parent (Mantid) algorithm that wants to run this - @param ws1 :: first workspace - @param ws2 :: second workspace - - @returns workspace with the concatenation of the spectra ws1+ws2 - """ - alg = parent.createChildAlgorithm('AppendSpectra') - alg.setProperty('InputWorkspace1', ws1) - alg.setProperty('InputWorkspace2', ws2) - alg.execute() - - result = alg.getProperty('OutputWorkspace').value - return result - -def integrateSpectra(parent, ws): - """ - Integrates all the spectra or a workspace, and return the result. - Simply uses 'Integration' as a child algorithm. - - @param parent :: parent (Mantid) algorithm that wants to run this - @param ws :: workspace (MatrixWorkspace) with the spectra to integrate - - @returns integrated workspace, or result of integrating every spectra in the input workspace - """ - intAlg = parent.createChildAlgorithm('Integration') - intAlg.setProperty('InputWorkspace', ws) - intAlg.execute() - ws = intAlg.getProperty('OutputWorkspace').value - - return ws - -def rebinToMatchWS(parent, ws, targetWS): - """ - Rebins a workspace so that its bins match those of a 'target' workspace. This simply uses the - algorithm RebinToWorkspace as a child. In principle this method does not care about the units - of the input workspaces, as long as they are in the same units. - - @param parent :: parent (Mantid) algorithm that wants to run this - @param ws :: input workspace (MatrixWorkspace) to rebin (all spectra will be rebinnded) - @param targetWS :: workspace to match against, this fixes the data range, and number and width of the - bins for the workspace returned - - @returns ws rebinned to resemble targetWS - """ - reAlg = parent.createChildAlgorithm('RebinToWorkspace') - reAlg.setProperty('WorkspaceToRebin', ws) - reAlg.setProperty('WorkspaceToMatch', targetWS) - reAlg.execute() - reWS = reAlg.getProperty('OutputWorkspace').value - - return reWS diff --git a/Code/Mantid/scripts/Inelastic/IndirectBayes.py b/Code/Mantid/scripts/Inelastic/IndirectBayes.py index fc7b7df38f2da18415cbd7dc0ca8d1ae0487ee45..b4e6d00f3c0e6d82f618068e5ce953e8e19c5e77 100644 --- a/Code/Mantid/scripts/Inelastic/IndirectBayes.py +++ b/Code/Mantid/scripts/Inelastic/IndirectBayes.py @@ -1,9 +1,12 @@ -#pylint: disable=invalid-name -# Bayes routines -# Fortran programs use fixed length arrays whereas Python has variable lenght lists -# Input : the Python list is padded to Fortrans length using procedure PadArray -# Output : the Fortran numpy array is sliced to Python length using dataY = yout[:ny] -# +#pylint: disable=invalid-name,too-many-arguments,too-many-locals + +""" +Bayes routines +Fortran programs use fixed length arrays whereas Python has variable lenght lists +Input : the Python list is padded to Fortrans length using procedure PadArray +Output : the Fortran numpy array is sliced to Python length using dataY = yout[:ny] +""" + from IndirectImport import * if is_supported_f2py_platform(): QLr = import_f2py("QLres") @@ -41,7 +44,7 @@ def CalcErange(inWS,ns,erange,binWidth): bnorm = 1.0/binWidth #get data from input workspace - N,X,Y,E = GetXYE(inWS,ns,array_len) + _,X,Y,E = GetXYE(inWS,ns,array_len) Xdata = mtd[inWS].readX(0) #get all x values within the energy range @@ -56,7 +59,7 @@ def CalcErange(inWS,ns,erange,binWidth): Xin = Xin.reshape(len(Xin)/binWidth, binWidth) #sum and normalise values in bins - Xout = [sum(bin) * bnorm for bin in Xin] + Xout = [sum(bin_val) * bnorm for bin_val in Xin] #count number of bins nbins = len(Xout) @@ -85,7 +88,7 @@ def GetResNorm(resnormWS,ngrp): else: # constant values dtnorm = [] xscale = [] - for m in range(0,ngrp): + for _ in range(0,ngrp): dtnorm.append(1.0) xscale.append(1.0) dtn=PadArray(dtnorm,51) # pad for Fortran call @@ -156,12 +159,12 @@ def QLRun(program,samWS,resWS,resnormWS,erange,nbins,Fit,wfile,Loop,Plot,Save): StartTime(program) #expand fit options - elastic, background, width, resnorm = Fit + elastic, background, width, res_norm = Fit #convert true/false to 1/0 for fortran o_el = 1 if elastic else 0 o_w1 = 1 if width else 0 - o_res = 1 if resnorm else 0 + o_res = 1 if res_norm else 0 #fortran code uses background choices defined using the following numbers if background == 'Sloping': @@ -175,7 +178,6 @@ def QLRun(program,samWS,resWS,resnormWS,erange,nbins,Fit,wfile,Loop,Plot,Save): workdir = getDefaultWorkingDirectory() - facility = config['default.facility'] array_len = 4096 # length of array in Fortran CheckXrange(erange,'Energy') @@ -196,7 +198,7 @@ def QLRun(program,samWS,resWS,resnormWS,erange,nbins,Fit,wfile,Loop,Plot,Save): if Loop != True: nsam = 1 - nres,ntr = CheckHistZero(resWS) + nres = CheckHistZero(resWS)[0] if program == 'QL': if nres == 1: @@ -215,19 +217,17 @@ def QLRun(program,samWS,resWS,resnormWS,erange,nbins,Fit,wfile,Loop,Plot,Save): logger.information(' Erange : '+str(erange[0])+' to '+str(erange[1])) Wy,We = ReadWidthFile(width,wfile,totalNoSam) - dtn,xsc = ReadNormFile(resnorm,resnormWS,totalNoSam) + dtn,xsc = ReadNormFile(res_norm,resnormWS,totalNoSam) fname = samWS[:-4] + '_'+ prog probWS = fname + '_Prob' fitWS = fname + '_Fit' - datWS = fname + '_Data' wrks=os.path.join(workdir, samWS[:-4]) logger.information(' lptfile : '+wrks+'_'+prog+'.lpt') lwrk=len(wrks) wrks.ljust(140,' ') wrkr=resWS wrkr.ljust(140,' ') - wrk = [wrks, wrkr] # initialise probability list if program == 'QL': @@ -278,11 +278,9 @@ def QLRun(program,samWS,resWS,resnormWS,erange,nbins,Fit,wfile,Loop,Plot,Save): dataX = xout[:nd] dataX = np.append(dataX,2*xout[nd-1]-xout[nd-2]) yfit_list = np.split(yfit[:4*nd],4) - dataF0 = yfit_list[0] dataF1 = yfit_list[1] if program == 'QL': dataF2 = yfit_list[2] - dataF3 = yfit_list[3] dataG = np.zeros(nd) datX = dataX datY = yout[:nd] @@ -431,7 +429,7 @@ def read_ql_file(file_name, nl): #Q,AMAX,HWHM,BSCL,GSCL line = line_pointer.next() - Q, AMAX, HWHM, BSCL, GSCL = line + Q, AMAX, HWHM, _, _ = line q_data.append(Q) #A0,A1,A2,A4 @@ -441,7 +439,7 @@ def read_ql_file(file_name, nl): #parse peak data from block block_FWHM = [] block_amplitude = [] - for i in range(nl): + for _ in range(nl): #Amplitude,FWHM for each peak line = line_pointer.next() amp = AMAX*line[0] @@ -456,7 +454,7 @@ def read_ql_file(file_name, nl): block_FWHM_e = [] block_amplitude_e = [] - for i in range(nl): + for _ in range(nl): #Amplitude error,FWHM error for each peak #SIGIK line = line_pointer.next() @@ -549,7 +547,6 @@ def C2Fw(prog,sname): def SeBlock(a,first): #read Ascii block of Integers - line1 = a[first] first += 1 val = ExtractFloat(a[first]) #Q,AMAX,HWHM Q = val[0] @@ -561,13 +558,13 @@ def SeBlock(a,first): #read Ascii block of Integ first += 1 val = ExtractFloat(a[first]) #AI,FWHM first peak fw = [2.*HWHM*val[1]] - int = [AMAX*val[0]] + integer = [AMAX*val[0]] first += 1 val = ExtractFloat(a[first]) #SIG0 int0.append(val[0]) first += 1 val = ExtractFloat(a[first]) #SIG3K - int.append(AMAX*math.sqrt(math.fabs(val[0])+1.0e-20)) + integer.append(AMAX*math.sqrt(math.fabs(val[0])+1.0e-20)) first += 1 val = ExtractFloat(a[first]) #SIG1K fw.append(2.0*HWHM*math.sqrt(math.fabs(val[0])+1.0e-20)) @@ -577,17 +574,14 @@ def SeBlock(a,first): #read Ascii block of Integ val = ExtractFloat(a[first]) #SIG2K be.append(math.sqrt(math.fabs(val[0])+1.0e-20)) first += 1 - return first,Q,int0,fw,int,be #values as list + return first,Q,int0,fw,integer,be #values as list def C2Se(sname): - prog = 'QSe' outWS = sname+'_Result' asc = readASCIIFile(sname+'.qse') - lasc = len(asc) var = asc[3].split() #split line on spaces nspec = var[0] - ndat = var[1] var = ExtractInt(asc[6]) first = 7 Xout = [] @@ -603,8 +597,8 @@ def C2Se(sname): dataY = np.array([]) dataE = np.array([]) - for m in range(0,ns): - first,Q,int0,fw,it,be = SeBlock(asc,first) + for _ in range(0,ns): + first,Q,_,fw,it,be = SeBlock(asc,first) Xout.append(Q) Yf.append(fw[0]) Ef.append(fw[1]) @@ -694,12 +688,12 @@ def CheckBetSig(nbs): def QuestRun(samWS,resWS,nbs,erange,nbins,Fit,Loop,Plot,Save): StartTime('Quest') #expand fit options - elastic, background, width, resnorm = Fit + elastic, background, width, res_norm = Fit #convert true/false to 1/0 for fortran o_el = 1 if elastic else 0 o_w1 = 1 if width else 0 - o_res = 1 if resnorm else 0 + o_res = 1 if res_norm else 0 #fortran code uses background choices defined using the following numbers if background == 'Sloping': @@ -726,7 +720,7 @@ def QuestRun(samWS,resWS,nbs,erange,nbins,Fit,Loop,Plot,Save): efix = getEfixed(samWS) theta,Q = GetThetaQ(samWS) - nres,ntr = CheckHistZero(resWS) + nres = CheckHistZero(resWS)[0] if nres == 1: prog = 'Qst' # res file else: @@ -741,7 +735,6 @@ def QuestRun(samWS,resWS,nbs,erange,nbins,Fit,Loop,Plot,Save): wrks.ljust(140,' ') wrkr=resWS wrkr.ljust(140,' ') - wrk = [wrks, wrkr] Nbet,Nsig = nbs[0], nbs[1] eBet0 = np.zeros(Nbet) # set errors to zero eSig0 = np.zeros(Nsig) # set errors to zero @@ -754,7 +747,7 @@ def QuestRun(samWS,resWS,nbs,erange,nbins,Fit,Loop,Plot,Save): Ndat = nout[0] Imin = nout[1] Imax = nout[2] - Nb,Xb,Yb,Eb = GetXYE(resWS,0,array_len) + Nb,Xb,Yb,_ = GetXYE(resWS,0,array_len) numb = [nsam, nsp, ntc, Ndat, nbin, Imin, Imax, Nb, nrbin, Nbet, Nsig] reals = [efix, theta[m], rscl, bnorm] xsout,ysout,xbout,ybout,zpout=Que.quest(numb,Xv,Yv,Ev,reals,fitOp,\ @@ -881,9 +874,8 @@ def ResNormRun(vname,rname,erange,nbin,Plot='None',Save=False): CheckXrange(erange,'Energy') CheckAnalysers(vname,rname) nvan,ntc = CheckHistZero(vname) - theta,Q = GetThetaQ(vname) + theta = GetThetaQ(vname)[0] efix = getEfixed(vname) - nres,ntr = CheckHistZero(rname) print "begining erange calc" nout,bnorm,Xdat,Xv,Yv,Ev = CalcErange(vname,0,erange,nbin) print "end of erange calc" @@ -897,12 +889,11 @@ def ResNormRun(vname,rname,erange,nbin,Plot='None',Save=False): wrks.ljust(140,' ') # pad for fioxed Fortran length wrkr=rname wrkr.ljust(140,' ') - Nb,Xb,Yb,Eb = GetXYE(rname,0,array_len) + Nb,Xb,Yb,_ = GetXYE(rname,0,array_len) rscl = 1.0 xPar = np.array([theta[0]]) for m in range(1,nvan): xPar = np.append(xPar,theta[m]) - ePar = np.zeros(nvan) fname = vname[:-4] for m in range(0,nvan): logger.information('Group ' +str(m)+ ' at angle '+ str(theta[m])) diff --git a/Code/Mantid/scripts/Inelastic/IndirectCommon.py b/Code/Mantid/scripts/Inelastic/IndirectCommon.py index 7958e1c17730dcc5a9ff66326fd324de84b7ed0a..aeb8eaba49b603268dc898d9d87885027a899d66 100644 --- a/Code/Mantid/scripts/Inelastic/IndirectCommon.py +++ b/Code/Mantid/scripts/Inelastic/IndirectCommon.py @@ -105,7 +105,7 @@ def getWSprefix(wsname): return prefix -def getEfixed(workspace, detIndex=0): +def getEfixed(workspace): inst = mtd[workspace].getInstrument() if inst.hasParameter('Efixed'): @@ -115,7 +115,7 @@ def getEfixed(workspace, detIndex=0): analyser_name = inst.getStringParameter('analyser')[0] analyser_comp = inst.getComponentByName(analyser_name) - if analyser_comp.hasParameter('Efixed'): + if analyser_comp is not None and analyser_comp.hasParameter('Efixed'): return analyser_comp.getNumberParameter('EFixed')[0] raise ValueError('No Efixed parameter found') @@ -151,7 +151,7 @@ def createQaxis(inputWS): sample_pos = inst.getSample().getPos() beam_pos = sample_pos - inst.getSource().getPos() for i in range(0, num_hist): - efixed = getEfixed(inputWS, i) + efixed = getEfixed(inputWS) detector = workspace.getDetector(i) theta = detector.getTwoTheta(sample_pos, beam_pos) / 2 lamda = math.sqrt(81.787 / efixed) @@ -225,7 +225,7 @@ def ExtractFloat(data_string): Extract float values from an ASCII string """ values = data_string.split() - values = map(float, values) + values = [float(v) for v in values] return values @@ -234,7 +234,7 @@ def ExtractInt(data_string): Extract int values from an ASCII string """ values = data_string.split() - values = map(int, values) + values = [int(v) for v in values] return values diff --git a/Code/Mantid/scripts/Inelastic/IndirectDataAnalysis.py b/Code/Mantid/scripts/Inelastic/IndirectDataAnalysis.py index b3d509f99632d2d3992e173394ccc6e91568ba15..cadfab5dc92f4676cd5a4074e208b245459fb374 100644 --- a/Code/Mantid/scripts/Inelastic/IndirectDataAnalysis.py +++ b/Code/Mantid/scripts/Inelastic/IndirectDataAnalysis.py @@ -1,4 +1,5 @@ -#pylint: disable=invalid-name +#pylint: disable=invalid-name,too-many-locals,too-many-arguments + from IndirectImport import import_mantidplot MTD_PLOT = import_mantidplot() from IndirectCommon import * diff --git a/Code/Mantid/scripts/Inelastic/IndirectMuscat.py b/Code/Mantid/scripts/Inelastic/IndirectMuscat.py index 99cd2f96953229c0fdf83c426b27f4546ff04612..250969678520510747cf43dcf4cd344d1a230d4f 100644 --- a/Code/Mantid/scripts/Inelastic/IndirectMuscat.py +++ b/Code/Mantid/scripts/Inelastic/IndirectMuscat.py @@ -1,5 +1,8 @@ -#pylint: disable=invalid-name -# MUSIC : Version of Minus for MIDAS +#pylint: disable=invalid-name,too-many-arguments,too-many-locals + +""" +MUSIC : Version of Minus for MIDAS +""" from IndirectImport import * if is_supported_f2py_platform(): diff --git a/Code/Mantid/scripts/Inelastic/IndirectNeutron.py b/Code/Mantid/scripts/Inelastic/IndirectNeutron.py index 9633c691fe1375207654d3691f9466f9b4012569..400db9541563409f1ca03476540b4b14bc81777e 100644 --- a/Code/Mantid/scripts/Inelastic/IndirectNeutron.py +++ b/Code/Mantid/scripts/Inelastic/IndirectNeutron.py @@ -1,6 +1,9 @@ -#pylint: disable=invalid-name -#Force for ILL backscattering raw -# +#pylint: disable=invalid-name,too-many-arguments + +""" +Force for ILL backscattering raw +""" + from IndirectImport import * from mantid.simpleapi import * from mantid import config, logger, mtd, FileFinder @@ -70,8 +73,6 @@ def ReadIbackGroup(a,first): #read Ascii block of spec line1 = a[next] next += 1 val = ExtractInt(a[next]) - n1 = val[0] - ngrp = val[2] if line1.startswith('S'): error = '' else: @@ -122,7 +123,7 @@ def loadFile(path): handle.close() return asc - except: + except RuntimeError: error = 'ERROR *** Could not load ' + path sys.exit(error) diff --git a/Code/Mantid/scripts/SANS/ISISCommandInterface.py b/Code/Mantid/scripts/SANS/ISISCommandInterface.py index 24de1ea3c213c98079038b77e60480256b3092bb..c1e5ea3817d645db5fe59051203c2f3fb4733547 100644 --- a/Code/Mantid/scripts/SANS/ISISCommandInterface.py +++ b/Code/Mantid/scripts/SANS/ISISCommandInterface.py @@ -1198,6 +1198,18 @@ def IsValidWsForRemovingZeroErrors(input_workspace_name): else: return "" + +def check_if_event_workspace(file_name): + ''' + Checks if a file is associated with an event workspace. It tests if + the workspace can be loaded. + @param file_name: The file name to test + @returns true if the workspace is an event workspace otherwise false + ''' + result = su.can_load_as_event_workspace(filename = file_name) + print result + return result + ################################################################################ # Input check functions diff --git a/Code/Mantid/scripts/SANS/SANSUtility.py b/Code/Mantid/scripts/SANS/SANSUtility.py index ae5527a41627d947f96266ef5c0857970ce832eb..a7404cabbe77005da1faab20d69572782c207b8c 100644 --- a/Code/Mantid/scripts/SANS/SANSUtility.py +++ b/Code/Mantid/scripts/SANS/SANSUtility.py @@ -4,7 +4,7 @@ # SANS data reduction scripts ######################################################## from mantid.simpleapi import * -from mantid.api import IEventWorkspace, MatrixWorkspace, WorkspaceGroup +from mantid.api import IEventWorkspace, MatrixWorkspace, WorkspaceGroup, FileLoaderRegistry import mantid from mantid.kernel import time_duration import inspect @@ -14,16 +14,20 @@ import re import types sanslog = Logger("SANS") - ADDED_EVENT_DATA_TAG = '_added_event_data' - REG_DATA_NAME = '-add' + ADDED_EVENT_DATA_TAG + '[_1-9]*$' REG_DATA_MONITORS_NAME = '-add_monitors' + ADDED_EVENT_DATA_TAG + '[_1-9]*$' - ZERO_ERROR_DEFAULT = 1e6 - INCIDENT_MONITOR_TAG = '_incident_monitor' +# WORKAROUND FOR IMPORT ISSUE IN UBUNTU --- START +CAN_IMPORT_NXS = True +try: + import nxs +except ImportError: + CAN_IMPORT_NXS = False +# WORKAROUND FOR IMPORT ISSUE IN UBUNTU --- STOP + def deprecated(obj): """ Decorator to apply to functions or classes that we think are not being (or @@ -1007,6 +1011,40 @@ def convert_to_string_list(to_convert): output_string = "[" + ','.join("'"+element+"'" for element in string_list) + "]" return output_string +def can_load_as_event_workspace(filename): + ''' + Check if an file can be loaded into an event workspace + Currently we check if the file + 1. can be loaded with LoadEventNexus + 2. contains an "event_workspace" nexus group in its first level + Note that this assumes a specific directory structure for the nexus file. + @param filename: the name of the input file name + @returns true if the file can be loaded as an event workspace else false + ''' + is_event_workspace = False + + # Check if it can be loaded with LoadEventNexus + is_event_workspace = FileLoaderRegistry.canLoad("LoadEventNexus", filename) + + # Ubuntu does not provide NEXUS for python currently, need to hedge for that + if CAN_IMPORT_NXS: + if is_event_workspace == False: + # pylint: disable=bare-except + try: + # We only check the first entry in the root + # and check for event_eventworkspace in the next level + nxs_file =nxs.open(filename, 'r') + rootKeys = nxs_file.getentries().keys() + nxs_file.opengroup(rootKeys[0]) + nxs_file.opengroup('event_workspace') + is_event_workspace = True + except: + pass + finally: + nxs_file.close() + + return is_event_workspace + ############################################################################### ######################### Start of Deprecated Code ############################ ############################################################################### diff --git a/Code/Mantid/scripts/SANS/isis_reduction_steps.py b/Code/Mantid/scripts/SANS/isis_reduction_steps.py index b226e07a039109d557bcbefdca8512c47cfe39d8..c5cc2207efa0112ba6ae4835385bec9e69c9fdbd 100644 --- a/Code/Mantid/scripts/SANS/isis_reduction_steps.py +++ b/Code/Mantid/scripts/SANS/isis_reduction_steps.py @@ -17,13 +17,14 @@ from mantid.kernel import Logger sanslog = Logger("SANS") from mantid.simpleapi import * -from mantid.api import WorkspaceGroup, Workspace, IEventWorkspace, FileLoaderRegistry +from mantid.api import WorkspaceGroup, Workspace, IEventWorkspace from SANSUtility import (GetInstrumentDetails, MaskByBinRange, isEventWorkspace, getFilePathFromWorkspace, getWorkspaceReference, slice2histogram, getFileAndName, mask_detectors_with_masking_ws, check_child_ws_for_name_and_type_for_added_eventdata, extract_spectra, extract_child_ws_for_added_eventdata, load_monitors_for_multiperiod_event_data, - MaskWithCylinder, get_masked_det_ids, get_masked_det_ids_from_mask_file, INCIDENT_MONITOR_TAG) + MaskWithCylinder, get_masked_det_ids, get_masked_det_ids_from_mask_file, INCIDENT_MONITOR_TAG, + can_load_as_event_workspace) import isis_instrument import isis_reducer from reducer_singleton import ReductionStep @@ -140,7 +141,7 @@ class LoadRun(object): """ if self._period != self.UNSET_PERIOD: workspace = self._get_workspace_name(self._period) - if not FileLoaderRegistry.canLoad("LoadEventNexus", self._data_file): + if not can_load_as_event_workspace(self._data_file): extra_options['EntryNumber'] = self._period else: workspace = self._get_workspace_name() diff --git a/Code/Mantid/scripts/test/SANSCommandInterfaceTest.py b/Code/Mantid/scripts/test/SANSCommandInterfaceTest.py index 500266b3e4a929c4eba81ef06cc796dcddc3692c..127e5066098f9dcb4142c1ea9e535424d3dc594c 100644 --- a/Code/Mantid/scripts/test/SANSCommandInterfaceTest.py +++ b/Code/Mantid/scripts/test/SANSCommandInterfaceTest.py @@ -4,7 +4,9 @@ import isis_instrument as instruments import ISISCommandInterface as command_iface from reducer_singleton import ReductionSingleton import isis_reduction_steps as reduction_steps - +from mantid.simpleapi import * +from mantid.kernel import DateAndTime +import random class SANSCommandInterfaceGetAndSetTransmissionSettings(unittest.TestCase): def test_that_gets_transmission_monitor(self): @@ -192,5 +194,38 @@ class SANSCommandInterfaceGetAndSetTransmissionSettings(unittest.TestCase): # Assert self.assertEqual(0, len(ReductionSingleton().transmission_calculator.mask_files), 'The transmission mask list should be empty.') +class TestEventWorkspaceCheck(unittest.TestCase): + def _create_file_name(self, name): + temp_save_dir = config['defaultsave.directory'] + if (temp_save_dir == ''): + temp_save_dir = os.getcwd() + return os.path.join(temp_save_dir, name + '.nxs') + + def addSampleLogEntry(self, log_name, ws, start_time, extra_time_shift): + number_of_times = 10 + for i in range(0, number_of_times): + + val = random.randrange(0, 10, 1) + date = DateAndTime(start_time) + date += int(i*1e9) + date += int(extra_time_shift*1e9) + AddTimeSeriesLog(ws, Name=log_name, Time=date.__str__().strip(), Value=val) + + def _clean_up(self, file_name): + if os.path.exists(file_name): + os.remove(file_name) + + def test_that_histogram_workspace_is_detected(self): + # Arrange + ws = CreateSampleWorkspace() + self.addSampleLogEntry('proton_charge', ws, "2010-01-01T00:00:00", 0.0) + file_name = self._create_file_name('dummy') + SaveNexus(Filename= file_name, InputWorkspace=ws) + # Act + result = command_iface.check_if_event_workspace(file_name) + self.assertFalse(result) + # Clean Up + self._clean_up(file_name) + if __name__ == "__main__": unittest.main()