diff --git a/Framework/API/test/MultipleFilePropertyTest.h b/Framework/API/test/MultipleFilePropertyTest.h index d374d704f0a0a1c43b3db3e4155edb94342b23da..797b8cb32db42561afcc3fa592bccc120c09ff8a 100644 --- a/Framework/API/test/MultipleFilePropertyTest.h +++ b/Framework/API/test/MultipleFilePropertyTest.h @@ -397,6 +397,37 @@ public: TS_ASSERT_EQUALS(fileNames[6][2], dummyFile("TSC00004.raw")); } + void test_multipleFiles_shortForm_addRanges() { + MultipleFileProperty p("Filename"); + p.setValue("TSC1-2+4-5.raw"); + std::vector<std::vector<std::string>> fileNames = p(); + + TS_ASSERT_EQUALS(fileNames[0][0], dummyFile("TSC00001.raw")); + TS_ASSERT_EQUALS(fileNames[0][1], dummyFile("TSC00002.raw")); + TS_ASSERT_EQUALS(fileNames[0][2], dummyFile("TSC00004.raw")); + TS_ASSERT_EQUALS(fileNames[0][3], dummyFile("TSC00005.raw")); + } + + void test_multipleFiles_shortForm_addSingleToRange() { + MultipleFileProperty p("Filename"); + p.setValue("TSC2+4-5.raw"); + std::vector<std::vector<std::string>> fileNames = p(); + + TS_ASSERT_EQUALS(fileNames[0][0], dummyFile("TSC00002.raw")); + TS_ASSERT_EQUALS(fileNames[0][1], dummyFile("TSC00004.raw")); + TS_ASSERT_EQUALS(fileNames[0][2], dummyFile("TSC00005.raw")); + } + + void test_multipleFiles_shortForm_rangeToSingle() { + MultipleFileProperty p("Filename"); + p.setValue("TSC1-2+5.raw"); + std::vector<std::vector<std::string>> fileNames = p(); + + TS_ASSERT_EQUALS(fileNames[0][0], dummyFile("TSC00001.raw")); + TS_ASSERT_EQUALS(fileNames[0][1], dummyFile("TSC00002.raw")); + TS_ASSERT_EQUALS(fileNames[0][2], dummyFile("TSC00005.raw")); + } + void test_multipleFiles_longForm_commaList() { MultipleFileProperty p("Filename"); p.setValue("TSC1.raw,TSC2.raw,TSC3.raw,TSC4.raw,TSC5.raw"); diff --git a/Framework/Kernel/src/MultiFileNameParser.cpp b/Framework/Kernel/src/MultiFileNameParser.cpp index bed6ddf43b7e8fc73c8b259abaf1b6d3ccdb9c36..b313fccc33f10f5ecd0b58cf2d6b11f44fb18f6b 100644 --- a/Framework/Kernel/src/MultiFileNameParser.cpp +++ b/Framework/Kernel/src/MultiFileNameParser.cpp @@ -10,10 +10,10 @@ #include <numeric> #include <sstream> -#include <boost/regex.hpp> #include <boost/algorithm/string.hpp> -#include <boost/lexical_cast.hpp> #include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/regex.hpp> namespace Mantid { namespace Kernel { @@ -39,14 +39,18 @@ const std::string STEP_RANGE = "(" + SINGLE + COLON + SINGLE + COLON + SINGLE + ")"; const std::string ADD_LIST = "(" + SINGLE + "(" + PLUS + SINGLE + ")+" + ")"; const std::string ADD_RANGE = "(" + SINGLE + MINUS + SINGLE + ")"; +const std::string ADD_RANGES = "(" + ADD_RANGE + PLUS + ADD_RANGE + ")"; +const std::string ADD_SINGLE_TO_RANGE = "(" + SINGLE + PLUS + ADD_RANGE + ")"; +const std::string ADD_RANGE_TO_SINGLE = "(" + ADD_RANGE + PLUS + SINGLE + ")"; const std::string ADD_STEP_RANGE = "(" + SINGLE + MINUS + SINGLE + COLON + SINGLE + ")"; -const std::string ANY = "(" + ADD_STEP_RANGE + "|" + ADD_RANGE + "|" + - ADD_LIST + "|" + STEP_RANGE + "|" + RANGE + "|" + - SINGLE + ")"; +const std::string ANY = "(" + ADD_STEP_RANGE + "|" + ADD_RANGES + "|" + + ADD_SINGLE_TO_RANGE + "|" + ADD_RANGE_TO_SINGLE + "|" + + ADD_RANGE + "|" + ADD_LIST + "|" + STEP_RANGE + "|" + + RANGE + "|" + SINGLE + ")"; const std::string LIST = "(" + ANY + "(" + COMMA + ANY + ")*" + ")"; -} +} // namespace Regexs ///////////////////////////////////////////////////////////////////////////// // Forward declarations. @@ -84,7 +88,7 @@ struct RangeContainsRun { std::string toString(const RunRangeList &runRangeList); std::string &accumulateString(std::string &output, std::pair<unsigned int, unsigned int> runRange); -} +} // namespace ///////////////////////////////////////////////////////////////////////////// // Scoped, global functions. @@ -508,6 +512,31 @@ parseToken(std::vector<std::vector<unsigned int>> &parsedRuns, else if (matchesFully(token, Regexs::ADD_RANGE)) { runs = generateRange(rangeDetails[0], rangeDetails[1], 1, true); } + // E.g. "2018-2020+2022-2023" + else if (matchesFully(token, Regexs::ADD_RANGES)) { + const auto lhs = generateRange(rangeDetails[0], rangeDetails[1], 1, true); + const auto rhs = generateRange(rangeDetails[2], rangeDetails[3], 1, true); + runs.resize(1); + auto it = std::back_inserter(runs.front()); + std::copy(lhs.front().cbegin(), lhs.front().cend(), it); + std::copy(rhs.front().cbegin(), rhs.front().cend(), it); + } + // E.g. "2018+2020-2023" + else if (matchesFully(token, Regexs::ADD_SINGLE_TO_RANGE)) { + runs.resize(1); + runs.front().emplace_back(rangeDetails[0]); + const auto rhs = generateRange(rangeDetails[1], rangeDetails[2], 1, true); + auto it = std::back_inserter(runs.front()); + std::copy(rhs.front().cbegin(), rhs.front().cend(), it); + } + // E.g. "2018-2020+2023" + else if (matchesFully(token, Regexs::ADD_RANGE_TO_SINGLE)) { + runs.resize(1); + const auto lhs = generateRange(rangeDetails[0], rangeDetails[1], 1, true); + auto it = std::back_inserter(runs.front()); + std::copy(lhs.front().cbegin(), lhs.front().cend(), it); + runs.front().emplace_back(rangeDetails[2]); + } // E.g. "2012-2020:4". else if (matchesFully(token, Regexs::ADD_STEP_RANGE)) { runs = diff --git a/Framework/Kernel/test/MultiFileNameParserTest.h b/Framework/Kernel/test/MultiFileNameParserTest.h index 8780310f36c6c9326028c07e0e0ea7f2f3870562..32038147bfbd0fee1d20aa96afe9431743c8c541 100644 --- a/Framework/Kernel/test/MultiFileNameParserTest.h +++ b/Framework/Kernel/test/MultiFileNameParserTest.h @@ -170,10 +170,39 @@ public: TS_ASSERT_EQUALS(result[9][0], 4); } + void test_singlePlusAddRange() { + ParsedRuns result = parseMultiRunString("1+3-6"); + + TS_ASSERT_EQUALS(result[0][0], 1); + TS_ASSERT_EQUALS(result[0][1], 3); + TS_ASSERT_EQUALS(result[0][2], 4); + TS_ASSERT_EQUALS(result[0][3], 5); + TS_ASSERT_EQUALS(result[0][4], 6); + } + + void test_addRangePlusSingle() { + ParsedRuns result = parseMultiRunString("1-3+5"); + + TS_ASSERT_EQUALS(result[0][0], 1); + TS_ASSERT_EQUALS(result[0][1], 2); + TS_ASSERT_EQUALS(result[0][2], 3); + TS_ASSERT_EQUALS(result[0][3], 5); + } + void test_nothingReturnedWhenPassedEmptyString() { TS_ASSERT_EQUALS(parseMultiRunString("").size(), 0); } + void test_sumTwoAddRanges() { + ParsedRuns result = parseMultiRunString("1-2+4-6"); + + TS_ASSERT_EQUALS(result[0][0], 1); + TS_ASSERT_EQUALS(result[0][1], 2); + TS_ASSERT_EQUALS(result[0][2], 4); + TS_ASSERT_EQUALS(result[0][3], 5); + TS_ASSERT_EQUALS(result[0][4], 6); + } + void test_errorThrownWhenPassedUnexpectedChar() { std::string message = "Non-numeric or otherwise unaccetable character(s) detected."; diff --git a/docs/source/api/python/mantid/api/MultipleFileProperty.rst b/docs/source/api/python/mantid/api/MultipleFileProperty.rst index ea3f3588339e7e8696110df8811c021bb96deee5..3c1f1656f333a5b27046e3f601ee5d9f7d321407 100644 --- a/docs/source/api/python/mantid/api/MultipleFileProperty.rst +++ b/docs/source/api/python/mantid/api/MultipleFileProperty.rst @@ -25,21 +25,21 @@ The syntax for multi file loading involves the use of several context-sensitive operators. Here is a run-down of those operators with some simple examples: -+--------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ -| Name | Usage | Description | Example Input | Example Result | -+====================+=============================+============================================================================================+===================+======================================+ -| List | ``<run>,<run>`` | Used to list runs | ``INST1,2,3.ext`` | Load runs 1, 2 and 3 | -+--------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ -| Plus | ``<run>+<run>`` | Used to specify which runs that are to be loaded and then summed together | ``INST1+2+3.ext`` | Load and sum runs 1, 2 and 3 | -+--------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ -| Range | ``<run>:<run>`` | Used to specify a range of runs to load | ``INST1:4.ext`` | Load runs 1, 2, 3 and 4 | -+--------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ -| Stepped Range | ``<run>:<run>:<step_size>`` | Used to specify a ''stepped'' range of runs to load | ``INST1:5:2.ext`` | Load runs 1, 3 and 5 | -+--------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ -| Added Range | ``<run>-<run>`` | Used to specify a range of runs that are to be loaded and then summed together | ``INST1-4.ext`` | Load and sum runs 1, 2, 3 and 4 | -+--------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ -|Stepped Added Range | ``<run>-<run>:<step_size>`` | Used to specify a ''stepped'' range of runs that are to be loaded and then summed together | ``INST1-5:2.ext`` | Load and sum runs 1, 3 and 5 | -+--------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ ++---------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ +| Name | Usage | Description | Example Input | Example Result | ++=====================+=============================+============================================================================================+===================+======================================+ +| List | ``<run>,<run>`` | Used to list runs | ``INST1,2,3.ext`` | Load runs 1, 2 and 3 | ++---------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ +| Plus | ``<run>+<run>`` | Used to specify which runs that are to be loaded and then summed together | ``INST1+2+3.ext`` | Load and sum runs 1, 2 and 3 | ++---------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ +| Range | ``<run>:<run>`` | Used to specify a range of runs to load | ``INST1:4.ext`` | Load runs 1, 2, 3 and 4 | ++---------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ +| Stepped Range | ``<run>:<run>:<step_size>`` | Used to specify a ''stepped'' range of runs to load | ``INST1:5:2.ext`` | Load runs 1, 3 and 5 | ++---------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ +| Added Range | ``<run>-<run>`` | Used to specify a range of runs that are to be loaded and then summed together | ``INST1-4.ext`` | Load and sum runs 1, 2, 3 and 4 | ++---------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ +| Stepped Added Range | ``<run>-<run>:<step_size>`` | Used to specify a ''stepped'' range of runs that are to be loaded and then summed together | ``INST1-5:2.ext`` | Load and sum runs 1, 3 and 5 | ++---------------------+-----------------------------+--------------------------------------------------------------------------------------------+-------------------+--------------------------------------+ Optional Info ------------- @@ -86,6 +86,11 @@ The basic syntax outlined above can be combined in a variety of ways: # For TOSCA, adds together run 1 (found in c:/files/) and run 2 (found in c:/store/). Load(Filename='c:/files/TSC1.raw+c:/store/TSC2.raw', OutputWorkspace='Files') + # For TOSCA, adds together runs 1, 4, 5 and 6. + Load(Filename='TSC1+4-5.raw', OutputWorkspace='Files') + + # For IRIS, adds together runs 1, 2, 3, 7, 8 and 9. + Load(Filename='IRS1-3+7-9.raw', OutputWorkspace='Files') Load Dialog =========== diff --git a/docs/source/release/v3.12.0/framework.rst b/docs/source/release/v3.12.0/framework.rst index 0bc285ad4401f3a706713bd88e87025c69978dec..57547b3035e2db3bf27f063cd79e9f36a72f62bb 100644 --- a/docs/source/release/v3.12.0/framework.rst +++ b/docs/source/release/v3.12.0/framework.rst @@ -65,6 +65,7 @@ Core Functionality - Added new classes ``ConfigObserver`` for listening for changes to any configuration property and ``ConfigPropertyObserver`` for listening to changes to an individual config property of interest. - Fixed the calculation of scattering length and scattering length squared for :py:obj:`Material <mantid.kernel.Material>` - Fixed the behaviour of ``UpdateInstrumentDefinitions.OnStartup`` in the :ref:`properties file <Properties File>`. It was not being used correctly for using the updated ``Facilities.xml`` file. +- ``MultiFileProperty`` now accepts complex summation ranges for run numbers, such as ``111-113+115`` and ``111-115+123-132``. Performance -----------