17#include "MantidHistogramData/HistogramMath.h"
25#include <boost/algorithm/string.hpp>
26#include <boost/regex.hpp>
27#include <boost/tokenizer.hpp>
34using namespace Kernel;
39 : m_columnSep(), m_separatorIndex(), m_comment(), m_baseCols(0), m_specNo(0), m_lastBins(0), m_curBins(0),
40 m_spectraStart(), m_spectrumIDcount(0), m_lineNo(0), m_spectra(), m_curSpectra(nullptr) {}
49 const std::string &filePath = descriptor.
filename();
50 const size_t filenameLength = filePath.size();
54 if (filenameLength > 12 ? (filePath.compare(filenameLength - 12, 12,
"_runinfo.xml") == 0)
55 :
false || filenameLength > 6 ? (filePath.compare(filenameLength - 6, 6,
".peaks") == 0)
56 :
false || filenameLength > 10 ? (filePath.compare(filenameLength - 10, 10,
".integrate") == 0)
59 }
else if (descriptor.
isAscii()) {
96 m_curSpectra = std::make_unique<DataObjects::Histogram1D>(HistogramData::Histogram::XMode::Points,
97 HistogramData::Histogram::YMode::Counts);
100 std::list<std::string> columns;
104 while (getline(file, line)) {
105 std::string templine = line;
107 boost::trim(templine);
108 if (templine.empty()) {
118 const size_t numSpectra =
m_spectra.size();
122 }
catch (std::exception &e) {
123 std::ostringstream msg;
124 msg <<
"Failed to create a Workspace2D from the data found in this file. "
127 throw std::runtime_error(msg.str());
132 }
catch (std::exception &e) {
133 std::ostringstream msg;
134 msg <<
"Failed to write read data into the output Workspace2D. Error: " << e.what();
135 throw std::runtime_error(msg.str());
139 return localWorkspace;
143 bool isDistribution =
false;
144 const bool distributionFlag =
getProperty(
"ForceDistributionTrue");
145 if (distributionFlag) {
146 isDistribution =
true;
152 while (std::getline(file, newLine)) {
153 if (newLine.find(
"Distribution=true") != std::string::npos) {
154 isDistribution =
true;
159 return isDistribution;
178 std::list<std::string> names;
179 std::list<std::string> types;
180 std::list<std::string> data;
182 while (getline(file, line)) {
185 std::list<std::string> columns;
205 if (colTypes != colNames) {
211 if (colNames > 0 && colNames == colTypes && colTypes == colData) {
215 ws = std::make_shared<DataObjects::TableWorkspace>();
217 auto itName = names.begin();
218 auto itTypes = types.begin();
219 for (
size_t i = 0; i < colNames; i++) {
220 std::string
name = *itName;
221 std::string type = *itTypes;
225 ws->addColumn(type,
name);
232 auto itTypes = types.begin();
233 for (
auto itData = data.begin(); itData != data.end(); itData++) {
236 std::string type = *itTypes;
240 }
else if (type ==
"int") {
241 int num = boost::lexical_cast<int>(*itData);
243 }
else if (type ==
"uint") {
244 uint32_t num = boost::lexical_cast<uint32_t>(*itData);
246 }
else if (type ==
"long64") {
247 auto num = boost::lexical_cast<int64_t>(*itData);
249 }
else if (type ==
"size_t") {
250 size_t num = boost::lexical_cast<size_t>(*itData);
252 }
else if (type ==
"float") {
253 float num = boost::lexical_cast<float>(*itData);
255 }
else if (type ==
"double") {
256 double num = boost::lexical_cast<double>(*itData);
258 }
else if (type ==
"bool") {
259 bool val = (itData->at(0) ==
't');
261 }
else if (type ==
"V3D") {
263 std::stringstream ss(*itData);
267 throw std::runtime_error(
"unknown column data type " + type);
279 file.clear(file.eofbit);
281 }
catch (std::exception &ex) {
284 g_log.
warning() <<
"Error while trying to read ascii file as table, "
285 "continuing to load as matrix workspace.\n"
286 << ex.what() <<
"\n";
292 file.seekg(0, std::ios::beg);
303 if (std::isdigit(line.at(0)) || line.at(0) ==
'-' || line.at(0) ==
'+') {
305 if (cols > 4 || cols < 0) {
309 ": Sets of values must have between 1 and 3 delimiters");
310 }
else if (cols == 1) {
324 ": Inconsistent inclusion of spectra IDs. All spectra must have "
325 "IDs or all spectra must not have IDs. "
326 "Check for blank lines, as they symbolize the end of one spectra "
327 "and the start of another. Also check for spectra IDs with no "
330 const std::string singleNumber = columns.front();
332 m_curSpectra->setSpectrumNo(boost::lexical_cast<int>(singleNumber));
333 }
catch (boost::bad_lexical_cast &) {
337 m_spectrumAxis.emplace_back(boost::lexical_cast<double>(singleNumber));
338 }
catch (boost::bad_lexical_cast &) {
339 throw std::runtime_error(
"Unable to read as spectrum ID (int) nor as "
340 "spectrum axis value (double)" +
353 ": Unexpected character found at beginning of line. Lines must either "
354 "be a single integer, a list of numeric values, blank, or a text line "
355 "beginning with the specified comment indicator: " +
360 ": Unknown format at line. Lines must either be a single integer, a "
361 "list of numeric values, blank, or a text line beginning with the "
362 "specified comment indicator: " +
379 for (
size_t i = 0; i < numSpectra; ++i) {
380 localWorkspace->setSharedX(i,
m_spectra[i].sharedX());
381 localWorkspace->setSharedY(i,
m_spectra[i].sharedY());
385 localWorkspace->setSharedE(i,
m_spectra[i].sharedE());
388 localWorkspace->setSharedDx(i,
m_spectra[i].sharedDx());
390 localWorkspace->getSpectrum(i).setSpectrumNo(
m_spectra[i].getSpectrumNo());
392 localWorkspace->getSpectrum(i).setSpectrumNo(
static_cast<specnum_t>(i) + 1);
395 localWorkspace->replaceAxis(1, std::make_unique<NumericAxis>(
m_spectrumAxis));
411 std::vector<double> values;
424 if (std::isdigit(line.at(0)) || line.at(0) ==
'-' || line.at(0) ==
'+') {
429 if (cols > 4 || cols < 1) {
432 throw std::runtime_error(
"Sets of values must have between 1 and 3 delimiters. Found " +
434 }
else if (cols != 1) {
437 }
catch (boost::bad_lexical_cast &) {
450 throw std::runtime_error(
"No valid data in file, check separator "
451 "settings or number of columns per bin.");
455 file.seekg(0, std::ios_base::beg);
456 for (
size_t i = 0; i <
m_lineNo; i++) {
473 const size_t rowsToMatch(5);
477 size_t matchingRows = 0;
479 size_t blankRows = 0;
482 std::vector<double> values;
483 while (getline(file, line) && matchingRows < rowsToMatch) {
487 std::list<std::string> columns;
504 if (std::isdigit(line.at(0)) || line.at(0) ==
'-' || line.at(0) ==
'+') {
508 if (lineCols > 4 || lineCols < 1) {
514 }
else if (lineCols != 1) {
517 }
catch (boost::bad_lexical_cast &) {
537 if (blankRows >= rowsToMatch) {
544 if (numCols == 0 && lineCols != 1) {
552 if (lineCols == numCols || lineCols == 1) {
567 file.clear(file.eofbit);
571 if (numCols > 4 || numCols < 2) {
572 throw std::runtime_error(
"No valid data in file, check separator "
573 "settings or number of columns per bin.");
583 file.seekg(0, std::ios::beg);
585 numToSkip = row - validRows;
589 while (
m_lineNo <
static_cast<size_t>(numToSkip) && getline(file, line)) {
592 g_log.
information() <<
"Skipped " << numToSkip <<
" line(s) of header information()\n";
605 histo.resize(histo.size() + 1);
607 histo.mutableX().back() = values[0];
608 histo.mutableY().back() = values[1];
616 histo.mutableE().back() = values[2];
621 histo.mutableE().back() = values[2];
622 m_curDx.emplace_back(values[3]);
638 throw std::runtime_error(
"Number of data columns not consistent throughout file");
653 throw std::runtime_error(
"Inconsistent inclusion of spectra IDs. All "
654 "spectra must have IDs or all spectra must not "
656 " Check for blank lines, as they symbolize the "
657 "end of one spectra and the start of another.");
672 throw std::runtime_error(
"Number of bins per spectra not consistant.");
685 m_curSpectra = std::make_unique<DataObjects::Histogram1D>(HistogramData::Histogram::XMode::Points,
686 HistogramData::Histogram::YMode::Counts);
703 return ((line.empty() && header) || line.at(0) ==
m_comment.at(0));
713 return (!(std::isdigit(line.at(0)) || line.at(0) ==
'-' || line.at(0) ==
'+') && line.at(0) !=
m_comment.at(0));
723 boost::split(columns, str, boost::is_any_of(
m_columnSep), boost::token_compress_on);
724 return static_cast<int>(columns.size());
733 values.resize(columns.size());
735 for (
auto value : columns) {
737 boost::to_lower(
value);
741 double nan = std::numeric_limits<double>::quiet_NaN();
744 values[i] = boost::lexical_cast<double>(
value);
755 const std::vector<std::string> exts{
".dat",
".txt",
".csv",
""};
757 "The name of the text file to read, including its full or "
758 "relative path. The file extension must be .txt, .dat, or "
761 "The name of the workspace that will be created, "
762 "filled with the read-in data and stored in the [[Analysis "
765 const int numSpacers = 7;
766 std::string spacers[numSpacers][2] = {
767 {
"Automatic",
",\t:; "}, {
"CSV",
","}, {
"Tab",
"\t"}, {
"Space",
" "}, {
"Colon",
":"}, {
"SemiColon",
";"},
768 {
"UserDefined",
"UserDefined"}};
770 std::array<std::string, numSpacers> sepOptions;
771 int sepOptionsIndex = 0;
773 for (
const auto &spacer : spacers) {
774 const auto &option = spacer[0];
775 m_separatorIndex.insert(std::pair<std::string, std::string>(option, spacer[1]));
776 sepOptions[sepOptionsIndex++] = option;
779 declareProperty(
"Separator",
"Automatic", std::make_shared<StringListValidator>(sepOptions),
780 "The separator between data columns in the data file. The "
781 "possible values are \"CSV\", \"Tab\", "
782 "\"Space\", \"SemiColon\", \"Colon\" or a user defined "
783 "value. (default: Automatic selection from comma,"
784 " tab, space, semicolon or colon.).");
787 "If present, will override any specified choice given to Separator.");
790 std::make_unique<VisibleWhenProperty>(
"Separator",
IS_EQUAL_TO,
"UserDefined"));
793 "Character(s) found front of "
794 "comment lines. Cannot contain "
795 "numeric characters");
798 units.insert(units.begin(),
"Dimensionless");
799 declareProperty(
"Unit",
"Energy", std::make_shared<StringListValidator>(units),
800 "The unit to assign to the X axis (anything known to the "
801 "[[Unit Factory]] or \"Dimensionless\")");
803 auto mustBePosInt = std::make_shared<BoundedValidator<int>>();
804 mustBePosInt->setLower(0);
806 "If given, skip this number of lines at the start of the file.");
808 "(default: false) If true, the loaded workspace is set to Distribution=true. If true, "
809 "the Distribution flag, which may be in the file header, is ignored.");
818 std::ifstream file(filename.c_str());
820 g_log.
error(
"Unable to open file: " + filename);
832 if (!custom.empty()) {
836 else if (choice !=
"UserDefined") {
842 g_log.
notice() <<
"\"UserDefined\" has been selected, but no custom "
843 "separator has been entered."
844 " Using default instead.\n";
850 if (!boost::regex_match(
m_columnSep.begin(),
m_columnSep.end(), boost::regex(
"[^0-9e+-]+", boost::regex::perl))) {
851 throw std::invalid_argument(
"Separators cannot contain numeric characters, "
852 "plus signs, hyphens or 'e'");
855 std::string tempcomment =
getProperty(
"CommentIndicator");
857 if (!boost::regex_match(tempcomment.begin(), tempcomment.end(),
858 boost::regex(
"[^0-9e" +
m_columnSep +
"+-]+", boost::regex::perl))) {
859 throw std::invalid_argument(
"Comment markers cannot contain numeric "
860 "characters, plus signs, hyphens,"
861 " 'e' or the selected separator character");
871 }
catch (std::exception &e) {
872 g_log.
error() <<
"Failed to read as ASCII this file: '" << filename <<
", error description: " << e.what() <<
'\n';
873 throw std::runtime_error(
"Failed to recognize this file as an ASCII file, "
878 outputWS->mutableRun().addProperty(
"Filename", filename);
double value
The value of the point.
#define DECLARE_FILELOADER_ALGORITHM(classname)
DECLARE_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro when wri...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
@ Load
allowed here which will be passed to the algorithm
TableRow represents a row in a TableWorkspace.
A property class for workspaces.
void processHeader(std::ifstream &file)
Process the header information.
void newSpectra()
check and configure flags and values relating to starting a new spectra
void init() override
Declare properties.
int splitIntoColumns(std::list< std::string > &columns, const std::string &str) const
Split the data into columns.
std::string m_columnSep
The column separator.
std::unique_ptr< DataObjects::Histogram1D > m_curSpectra
std::map< std::string, std::string > m_separatorIndex
Map the separator options to their string equivalents.
std::vector< double > m_curDx
void setcolumns(std::ifstream &file, std::string &line, std::list< std::string > &columns)
Check the start of the file for the first data set, then set the number of columns that should be exp...
std::vector< DataObjects::Histogram1D > m_spectra
bool skipLine(const std::string &line, bool header=false) const
Return true if the line is to be skipped.
void checkLineColumns(const size_t &cols) const
Check if the file has been found to inconsistently include spectra IDs.
int confidence(Kernel::FileDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
bool setDistribution(std::ifstream &file)
void fillInputValues(std::vector< double > &values, const std::list< std::string > &columns) const
Fill the given vector with the data values.
const std::string name() const override
The name of the algorithm.
void parseLine(const std::string &line, std::list< std::string > &columns)
Check the start of the file for the first data set, then set the number of columns that hsould be exp...
virtual API::Workspace_sptr readTable(std::ifstream &file)
Read the data from the file into a table workspace.
void exec() override
Execute the algorithm.
LoadAscii2()
Default constructor.
void writeToWorkspace(API::MatrixWorkspace_sptr &localWorkspace, const size_t &numSpectra) const
Construct the workspace.
std::vector< double > m_spectrumAxis
bool badLine(const std::string &line) const
Return true if the line doesn't start with a valid character.
void inconsistantIDCheck() const
Check if the file has been found to incosistantly include spectra IDs.
void addToCurrentSpectra(const std::list< std::string > &columns)
Check if the file has been found to inconsistently include spectra IDs.
virtual API::Workspace_sptr readData(std::ifstream &file)
Read the data from the file.
Records the filename and the description of failure.
Exception for when an item is not found in a collection.
Defines a wrapper around an open file.
const std::string & filename() const
Access the filename.
static bool isAscii(const std::string &filename, const size_t nbytes=256)
Returns true if the file is considered ascii.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
void notice(const std::string &msg)
Logs at notice level.
void error(const std::string &msg)
Logs at error level.
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
void readPrinted(std::istream &)
Read data from a stream in the format returned by printSelf ("[x,y,z]").
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< TableWorkspace > TableWorkspace_sptr
shared pointer to Mantid::DataObjects::TableWorkspace
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
int32_t specnum_t
Typedef for a spectrum Number.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
@ Output
An output workspace.