Commit da5f26fa authored by Karl Palmen's avatar Karl Palmen
Browse files

Merge remote-tracking branch 'origin/master' into re #16748

Merge to ensure CrystalFieldFunction is available and up to date.
parent b956b115
[flake8]
ignore = E114,E115,E116,E121,E123,E126,E133,E2,E704,W503,F403,F405,F999
ignore = E114,E115,E116,E121,E123,E126,E133,E2,E704,E722,E741,E743,W503,F403,F405,F999
exclude =
.git,
buildconfig,
......
......@@ -128,6 +128,7 @@ set ( SRC_FILES
src/ScriptBuilder.cpp
src/ScriptRepository.cpp
src/ScriptRepositoryFactory.cpp
src/SingleCountValidator.cpp
src/SpectraAxis.cpp
src/SpectraAxisValidator.cpp
src/SpectrumDetectorMapping.cpp
......@@ -321,6 +322,7 @@ set ( INC_FILES
inc/MantidAPI/ScriptBuilder.h
inc/MantidAPI/ScriptRepository.h
inc/MantidAPI/ScriptRepositoryFactory.h
inc/MantidAPI/SingleCountValidator.h
inc/MantidAPI/SingleValueParameter.h
inc/MantidAPI/SingleValueParameterParser.h
inc/MantidAPI/SpectraAxis.h
......@@ -352,10 +354,10 @@ set ( TEST_FILES
AlgorithmFactoryTest.h
AlgorithmHasPropertyTest.h
AlgorithmHistoryTest.h
AlgorithmMPITest.h
AlgorithmManagerTest.h
AlgorithmPropertyTest.h
AlgorithmProxyTest.h
AlgorithmMPITest.h
AlgorithmTest.h
AnalysisDataServiceTest.h
AsynchronousTest.h
......@@ -394,6 +396,7 @@ set ( TEST_FILES
IFunction1DSpectrumTest.h
IFunction1DTest.h
IFunctionMDTest.h
IFunctionTest.h
ILatticeFunctionTest.h
IMDWorkspaceTest.h
ISpectrumTest.h
......@@ -438,6 +441,7 @@ set ( TEST_FILES
SampleValidatorTest.h
ScopedWorkspaceTest.h
ScriptBuilderTest.h
SingleCountValidatorTest.h
SpectraAxisTest.h
SpectraAxisValidatorTest.h
SpectrumDetectorMappingTest.h
......
......@@ -108,8 +108,15 @@ public:
/// To query whether algorithm is a child. A proxy is always at top level,
/// returns false
bool isChild() const override { return m_isChild; }
void setAlwaysStoreInADS(const bool) override {}
void setChild(const bool val) override { m_isChild = val; }
void setChild(const bool val) override {
m_isChild = val;
setAlwaysStoreInADS(!val);
}
void setAlwaysStoreInADS(const bool val) override {
m_setAlwaysStoreInADS = val;
}
bool getAlwaysStoreInADS() const { return m_setAlwaysStoreInADS; }
/// Proxies only manage parent algorithms
void enableHistoryRecordingForChild(const bool) override{};
void setRethrows(const bool rethrow) override;
......@@ -193,6 +200,7 @@ private:
/// (default = true)
bool m_rethrow; ///< Whether or not to rethrow exceptions.
bool m_isChild; ///< Is this a child algo
bool m_setAlwaysStoreInADS; ///< If this will save in ADS
/// Temporary holder of external observers wishing to subscribe
mutable std::vector<const Poco::AbstractObserver *> m_externalObservers;
......
......@@ -175,6 +175,7 @@ public:
virtual size_t groupOfDetectorID(const detid_t detID) const;
protected:
size_t numberOfDetectorGroups() const;
/// Called as the first operation of most public methods.
virtual void populateIfNotLoaded() const;
......
......@@ -48,8 +48,6 @@ public:
virtual bool isOptional() const = 0;
/// Will the workspace be locked when starting an algorithm?
virtual bool isLocking() const = 0;
/// Returns the direction of the workspace property
//-virtual const unsigned int direction() const = 0;
/// Virtual destructor
virtual ~IWorkspaceProperty() = default;
/// Sets a flag indicating whether this is the master rank in MPI builds.
......
......@@ -558,8 +558,7 @@ protected:
/// be overloaded.
virtual void init(const std::size_t &NVectors, const std::size_t &XLength,
const std::size_t &YLength) = 0;
virtual void init(const std::size_t &NVectors,
const HistogramData::Histogram &histogram) = 0;
virtual void init(const HistogramData::Histogram &histogram) = 0;
/// Invalidates the commons bins flag. This is generally called when a method
/// could allow the X values to be changed.
......
#ifndef MANTID_API_SINGLECOUNTVALIDATOR_H_
#define MANTID_API_SINGLECOUNTVALIDATOR_H_
#include "MantidAPI/MatrixWorkspaceValidator.h"
namespace Mantid {
namespace API {
/** SingleCountValidator : This validator checks that there is only a single
entry per spectrum, the counts, so no Time-of-Flight data. Warning: only the
first bin of the workspace is checked, for performance reasons.
Copyright &copy; 2017 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 MANTID_API_DLL SingleCountValidator : public MatrixWorkspaceValidator {
public:
explicit SingleCountValidator(const bool &mustBeSingleCounts = true);
/// Gets the type of the validator
std::string getType() const { return "single_count"; }
/// Clone the current state
Kernel::IValidator_sptr clone() const override;
private:
/// Check for validity
std::string checkValidity(const MatrixWorkspace_sptr &ws) const override;
/// A flag indicating whether this validator requires that the workspace be
/// contain only single counts or not
const bool m_mustBeSingleCount;
};
} // namespace API
} // namespace Mantid
#endif /* MANTID_API_SINGLECOUNTVALIDATOR_H_ */
......@@ -104,6 +104,8 @@ public:
std::string value() const override;
bool isValueSerializable() const override;
std::string getDefault() const override;
std::string setValue(const std::string &value) override;
......
......@@ -139,6 +139,14 @@ template <typename TYPE> std::string WorkspaceProperty<TYPE>::value() const {
return m_workspaceName;
}
/** Returns true if the workspace is in the ADS or there is none.
* @return true if the string returned by value() is valid
*/
template<typename TYPE>
bool WorkspaceProperty<TYPE>::isValueSerializable() const {
return !m_workspaceName.empty() || !this->m_value;
}
/** Get the value the property was initialised with -its default value
* @return The default value
*/
......@@ -158,16 +166,7 @@ std::string WorkspaceProperty<TYPE>::setValue(const std::string &value) {
if (Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::autoTrim()) {
boost::trim(m_workspaceName);
}
// Try and get the workspace from the ADS, but don't worry if we can't
try {
Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::m_value =
AnalysisDataService::Instance().retrieveWS<TYPE>(m_workspaceName);
} catch (Kernel::Exception::NotFoundError &) {
// Set to null property if not found
this->clear();
// the workspace name is not reset here, however.
}
retrieveWorkspaceFromADS();
return isValid();
}
......@@ -181,9 +180,8 @@ std::string WorkspaceProperty<TYPE>::setDataItem(
const boost::shared_ptr<Kernel::DataItem> value) {
boost::shared_ptr<TYPE> typed = boost::dynamic_pointer_cast<TYPE>(value);
if (typed) {
std::string wsName = typed->getName();
if (this->direction() == Kernel::Direction::Input && !wsName.empty()) {
m_workspaceName = wsName;
if (this->direction() == Kernel::Direction::Input) {
m_workspaceName = typed->getName();
}
Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::m_value = typed;
} else {
......@@ -251,12 +249,14 @@ template <typename TYPE> std::string WorkspaceProperty<TYPE>::isValid() const {
return Kernel::PropertyWithValue<boost::shared_ptr<TYPE>>::isValid();
}
/** Indicates if the object is still pointing to the same workspace, using the
* workspace name
/** Indicates if the object is still pointing to the same workspace
* @return true if the value is the same as the initial value or false
* otherwise
*/
template <typename TYPE> bool WorkspaceProperty<TYPE>::isDefault() const {
if (m_initialWSName.empty()) {
return m_workspaceName.empty() && !this->m_value;
}
return m_initialWSName == m_workspaceName;
}
......
......@@ -91,7 +91,7 @@ Algorithm::Algorithm()
m_log("Algorithm"), g_log(m_log), m_groupSize(0), m_executeAsync(nullptr),
m_notificationCenter(nullptr), m_progressObserver(nullptr),
m_isInitialized(false), m_isExecuted(false), m_isChildAlgorithm(false),
m_recordHistoryForChild(false), m_alwaysStoreInADS(false),
m_recordHistoryForChild(false), m_alwaysStoreInADS(true),
m_runningAsync(false), m_running(false), m_rethrow(false),
m_isAlgStartupLoggingEnabled(true), m_startChildProgress(0.),
m_endChildProgress(0.), m_algorithmID(this), m_singleGroup(-1),
......@@ -140,7 +140,10 @@ bool Algorithm::isChild() const { return m_isChildAlgorithm; }
* @param isChild :: True - the algorithm is a child algorithm. False - this
* is a full managed algorithm.
*/
void Algorithm::setChild(const bool isChild) { m_isChildAlgorithm = isChild; }
void Algorithm::setChild(const bool isChild) {
m_isChildAlgorithm = isChild;
this->setAlwaysStoreInADS(!isChild);
}
/**
* Change the state of the history recording flag. Only applicable for
......@@ -568,9 +571,8 @@ bool Algorithm::execute() {
linkHistoryWithLastChild();
}
// Put any output workspaces into the AnalysisDataService - if this is not
// a child algorithm
if (!isChild() || m_alwaysStoreInADS)
// Put the output workspaces into the AnalysisDataService - if requested
if (m_alwaysStoreInADS)
this->store();
// RJT, 19/3/08: Moved this up from below the catch blocks
......@@ -897,6 +899,7 @@ void Algorithm::initializeFromProxy(const AlgorithmProxy &proxy) {
setLoggingOffset(proxy.getLoggingOffset());
setAlgStartupLogging(proxy.getAlgStartupLogging());
setChild(proxy.isChild());
setAlwaysStoreInADS(proxy.getAlwaysStoreInADS());
}
/** Fills History, Algorithm History and Algorithm Parameters
......@@ -1245,7 +1248,7 @@ bool Algorithm::doCallProcessGroups(
if (completed) {
// in the base processGroups each individual exec stores its outputs
if (!m_usingBaseProcessGroups && (!isChild() || m_alwaysStoreInADS))
if (!m_usingBaseProcessGroups && m_alwaysStoreInADS)
this->store();
// Get how long this algorithm took to run
......
......@@ -22,7 +22,8 @@ AlgorithmProxy::AlgorithmProxy(Algorithm_sptr alg)
m_categorySeparator(alg->categorySeparator()), m_alias(alg->alias()),
m_summary(alg->summary()), m_version(alg->version()), m_alg(alg),
m_isExecuted(), m_isLoggingEnabled(true), m_loggingOffset(0),
m_isAlgStartupLoggingEnabled(true), m_rethrow(false), m_isChild(false) {
m_isAlgStartupLoggingEnabled(true), m_rethrow(false), m_isChild(false),
m_setAlwaysStoreInADS(true) {
if (!alg) {
throw std::logic_error("Unable to create a proxy algorithm.");
}
......@@ -252,7 +253,7 @@ void AlgorithmProxy::createConcreteAlg(bool initOnly) {
* Clean up when the real algorithm stops
*/
void AlgorithmProxy::stopped() {
if (!isChild())
if (m_setAlwaysStoreInADS)
dropWorkspaceReferences();
m_isExecuted = m_alg->isExecuted();
m_alg.reset();
......
#include "MantidAPI/DeprecatedAlgorithm.h"
#include "MantidAPI/AlgorithmFactory.h"
#include "MantidKernel/DateAndTimeHelpers.h"
#include "MantidKernel/DateAndTimeHelpers.h"
#include "MantidKernel/Logger.h"
#include "MantidTypes/Core/DateAndTimeHelpers.h"
#include <sstream>
namespace Mantid {
......@@ -43,7 +42,7 @@ void DeprecatedAlgorithm::deprecatedDate(const std::string &date) {
// TODO warn people that it wasn't set
return;
}
if (!Kernel::DateAndTimeHelpers::stringIsISO8601(date)) {
if (!Types::Core::DateAndTimeHelpers::stringIsISO8601(date)) {
// TODO warn people that it wasn't set
return;
}
......
......@@ -189,6 +189,14 @@ void checkDetectorInfoSize(const Instrument &instr,
*/
void ExperimentInfo::setInstrument(const Instrument_const_sptr &instr) {
m_spectrumInfoWrapper = nullptr;
// Detector IDs that were previously dropped because they were not part of the
// instrument may now suddenly be valid, so we have to reinitialize the
// detector grouping. Also the index corresponding to specific IDs may have
// changed.
if (sptr_instrument !=
(instr->isParametrized() ? instr->baseInstrument() : instr))
invalidateAllSpectrumDefinitions();
if (instr->isParametrized()) {
sptr_instrument = instr->baseInstrument();
// We take a *copy* of the ParameterMap since we are modifying it by setting
......@@ -200,12 +208,6 @@ void ExperimentInfo::setInstrument(const Instrument_const_sptr &instr) {
m_parmap = boost::make_shared<ParameterMap>();
}
m_parmap->setInstrument(sptr_instrument.get());
// Detector IDs that were previously dropped because they were not part of the
// instrument may now suddenly be valid, so we have to reinitialize the
// detector grouping. Also the index corresponding to specific IDs may have
// changed.
invalidateAllSpectrumDefinitions();
}
/** Get a shared pointer to the parametrized instrument associated with this
......@@ -484,6 +486,14 @@ void ExperimentInfo::setNumberOfDetectorGroups(const size_t count) const {
m_spectrumInfoWrapper = nullptr;
}
/** Returns the number of detector groups.
*
* For MatrixWorkspace this is equal to getNumberHistograms() (after
*initialization). */
size_t ExperimentInfo::numberOfDetectorGroups() const {
return m_spectrumDefinitionNeedsUpdate.size();
}
/** Sets the detector grouping for the spectrum with the given `index`.
*
* This method should not need to be called explicitly. Groupings are updated
......@@ -491,16 +501,12 @@ void ExperimentInfo::setNumberOfDetectorGroups(const size_t count) const {
void ExperimentInfo::setDetectorGrouping(
const size_t index, const std::set<detid_t> &detIDs) const {
SpectrumDefinition specDef;
// Wrap translation in check for detector count as an optimization of
// otherwise slow failures via exceptions.
if (detectorInfo().size() > 0) {
for (const auto detID : detIDs) {
try {
const size_t detIndex = detectorInfo().indexOf(detID);
specDef.add(detIndex);
} catch (std::out_of_range &) {
// Silently strip bad detector IDs
}
for (const auto detID : detIDs) {
try {
const size_t detIndex = detectorInfo().indexOf(detID);
specDef.add(detIndex);
} catch (std::out_of_range &) {
// Silently strip bad detector IDs
}
}
m_spectrumInfo->setSpectrumDefinition(index, std::move(specDef));
......
......@@ -1397,14 +1397,18 @@ void IFunction::unfixParameter(const std::string &name) {
/// @param isDefault :: If true fix them by default
void IFunction::fixAll(bool isDefault) {
for (size_t i = 0; i < nParams(); ++i) {
fix(i, isDefault);
if (isActive(i)) {
fix(i, isDefault);
}
}
}
/// Free all parameters
void IFunction::unfixAll() {
for (size_t i = 0; i < nParams(); ++i) {
unfix(i);
if (isFixed(i)) {
unfix(i);
}
}
}
......
......@@ -106,7 +106,7 @@ const Indexing::IndexInfo &MatrixWorkspace::indexInfo() const {
*
* Used for setting spectrum number and detector ID information of spectra */
void MatrixWorkspace::setIndexInfo(const Indexing::IndexInfo &indexInfo) {
if (m_isInitialized && (indexInfo.storageMode() != storageMode()))
if (indexInfo.storageMode() != storageMode())
throw std::invalid_argument("MatrixWorkspace::setIndexInfo: "
"Parallel::StorageMode in IndexInfo does not "
"match storage mode in workspace");
......@@ -117,15 +117,12 @@ void MatrixWorkspace::setIndexInfo(const Indexing::IndexInfo &indexInfo) {
"does not match number of histograms in "
"workspace");
for (size_t i = 0; i < getNumberHistograms(); ++i) {
getSpectrum(i)
.setSpectrumNo(static_cast<specnum_t>(indexInfo.spectrumNumber(i)));
}
setStorageMode(indexInfo.storageMode());
*m_indexInfo = indexInfo;
m_indexInfo = Kernel::make_unique<Indexing::IndexInfo>(indexInfo);
m_indexInfoNeedsUpdate = false;
if (!m_indexInfo->spectrumDefinitions())
buildDefaultSpectrumDefinitions();
// Fails if spectrum definitions contain invalid indices.
rebuildDetectorIDGroupings();
// This sets the SpectrumDefinitions for the SpectrumInfo, which may seem
// counterintuitive at first -- why would setting IndexInfo modify internals
// of SpectrumInfo? However, logically it would not make sense to assign
......@@ -141,10 +138,6 @@ void MatrixWorkspace::setIndexInfo(const Indexing::IndexInfo &indexInfo) {
// are thus assigned by IndexInfo, which acts at a highler level and is
// typically used at construction time of a workspace, i.e., there is no data
// in histograms yet which would need to be regrouped.
// Fails if spectrum definitions contain invalid indices.
rebuildDetectorIDGroupings();
// Internally clears the flags that require spectrum definition updates (set
// by rebuildDetectorIDGrouping).
setSpectrumDefinitions(m_indexInfo->spectrumDefinitions());
}
......@@ -244,8 +237,16 @@ void MatrixWorkspace::initialize(const std::size_t &NVectors,
void MatrixWorkspace::initialize(const std::size_t &NVectors,
const HistogramData::Histogram &histogram) {
Indexing::IndexInfo indices(NVectors);
// Empty SpectrumDefinitions to indicate no default mapping to detectors.
indices.setSpectrumDefinitions(std::vector<SpectrumDefinition>(NVectors));
return initialize(indices, histogram);
}
void MatrixWorkspace::initialize(const Indexing::IndexInfo &indexInfo,
const HistogramData::Histogram &histogram) {
// Check validity of arguments
if (NVectors == 0 || histogram.x().empty()) {
if (indexInfo.size() == 0 || histogram.x().empty()) {
throw std::out_of_range(
"All arguments to init must be positive and non-zero");
}
......@@ -253,32 +254,16 @@ void MatrixWorkspace::initialize(const std::size_t &NVectors,
// Bypass the initialization if the workspace has already been initialized.
if (m_isInitialized)
return;
setNumberOfDetectorGroups(NVectors);
m_indexInfo = Kernel::make_unique<Indexing::IndexInfo>(NVectors);
// Invoke init() method of the derived class inside a try/catch clause
try {
this->init(NVectors, histogram);
} catch (std::runtime_error &) {
throw;
}
setStorageMode(indexInfo.storageMode());
setNumberOfDetectorGroups(indexInfo.size());
init(histogram);
setIndexInfo(indexInfo);
// Indicate that this workspace has been initialized to prevent duplicate
// attempts.
m_isInitialized = true;
}
void MatrixWorkspace::initialize(const Indexing::IndexInfo &indexInfo,
const HistogramData::Histogram &histogram) {
initialize(indexInfo.size(), histogram);
// Reopen initialization since setIndexInfo needs to disable some consistency
// checks that prevent setting an incompatible IndexInfo after initialization.
m_isInitialized = false;
setIndexInfo(indexInfo);
m_isInitialized = true;
}
//---------------------------------------------------------------------------------------
/** Set the title of the workspace
*
......@@ -1967,13 +1952,18 @@ void MatrixWorkspace::buildDefaultSpectrumDefinitions() {
"the number of spectra in the workspace is not equal to the number of "
"detectors in the instrument.");
std::vector<SpectrumDefinition> specDefs(m_indexInfo->size());
size_t specIndex = 0;
size_t globalSpecIndex = 0;
for (size_t detIndex = 0; detIndex < detInfo.size(); ++detIndex) {
for (size_t time = 0; time < detInfo.scanCount(detIndex); ++time) {
if (m_indexInfo->isOnThisPartition(
Indexing::GlobalSpectrumIndex(globalSpecIndex++)))
specDefs[specIndex++].add(detIndex, time);
if (!detInfo.isScanning() && (numberOfSpectra == m_indexInfo->size())) {
for (size_t i = 0; i < numberOfSpectra; ++i)
specDefs[i].add(i);
} else {
size_t specIndex = 0;
size_t globalSpecIndex = 0;
for (size_t detIndex = 0; detIndex < detInfo.size(); ++detIndex) {
for (size_t time = 0; time < detInfo.scanCount(detIndex); ++time) {
if (m_indexInfo->isOnThisPartition(
Indexing::GlobalSpectrumIndex(globalSpecIndex++)))
specDefs[specIndex++].add(detIndex, time);
}
}
}
m_indexInfo->setSpectrumDefinitions(std::move(specDefs));
......@@ -1983,25 +1973,37 @@ void MatrixWorkspace::rebuildDetectorIDGroupings() {
const auto &detInfo = detectorInfo();
const auto &allDetIDs = detInfo.detectorIDs();
const auto &specDefs = m_indexInfo->spectrumDefinitions();
for (size_t i = 0; i < m_indexInfo->size(); ++i) {
const auto size = static_cast<int64_t>(m_indexInfo->size());
std::atomic<bool> parallelException{false};
std::string error;
#pragma omp parallel for
for (int64_t i = 0; i < size; ++i) {
auto &spec = getSpectrum(i);
// Prevent setting flags that require spectrum definition updates
spec.setMatrixWorkspace(nullptr, i);
spec.setSpectrumNo(static_cast<specnum_t>(m_indexInfo->spectrumNumber(i)));
std::set<detid_t> detIDs;
for (const auto &index : (*specDefs)[i]) {
const size_t detIndex = index.first;
const size_t timeIndex = index.second;
if (detIndex >= allDetIDs.size())
throw std::invalid_argument("MatrixWorkspace: SpectrumDefinition "
"contains an out-of-range detector index, "
"i.e., the spectrum definition does not "
"match the instrument in the workspace.");
if (timeIndex >= detInfo.scanCount(detIndex))
throw std::invalid_argument(
"MatrixWorkspace: SpectrumDefinition contains an out-of-range time "
"index for a detector, i.e., the spectrum definition does not "
"match the instrument in the workspace.");
detIDs.insert(allDetIDs[detIndex]);
if (detIndex >= allDetIDs.size()) {
parallelException = true;
error = "MatrixWorkspace: SpectrumDefinition contains an out-of-range "
"detector index, i.e., the spectrum definition does not match "
"the instrument in the workspace.";
} else if (timeIndex >= detInfo.scanCount(detIndex)) {
parallelException = true;
error = "MatrixWorkspace: SpectrumDefinition contains an out-of-range "
"time index for a detector, i.e., the spectrum definition does "
"not match the instrument in the workspace.";
} else {
detIDs.insert(allDetIDs[detIndex]);
}
}
getSpectrum(i).setDetectorIDs(std::move(detIDs));
spec.setDetectorIDs(std::move(detIDs));
}
if (parallelException)
throw std::invalid_argument(error);
}
} // namespace API
......
#include "MantidAPI/SingleCountValidator.h"
namespace Mantid {
namespace API {
/** Constructor
*
* @param mustBeSingleCount :: Flag indicating whether the check is that a
* workspace should contain single counts only (true, default) or should not
* contain single counts (false).
*/
SingleCountValidator::SingleCountValidator(const bool &mustBeSingleCount)
: MatrixWorkspaceValidator(), m_mustBeSingleCount(mustBeSingleCount) {}
/// Clone the current state
Kernel::IValidator_sptr SingleCountValidator::clone() const {
return boost::make_shared<SingleCountValidator>(*this);
}
/** Checks if the workspace contains a single counts when it should not and
* vice-versa. For perofrmance reasons this takes the first spectrum size only,
* instead of relying on MatrixWorkspace::blocksize().
* @param ws The workspace to validate
* @return A user level description if a problem exists, otherwise an empty
* string
*/
std::string
SingleCountValidator::checkValidity(const MatrixWorkspace_sptr &ws) const {
auto blockSize = ws->histogram(0).size();
if (m_mustBeSingleCount) {
if (blockSize == 1)
return "";