Mantid
Loading...
Searching...
No Matches
LoadAscii2.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8#include "MantidAPI/Axis.h"
12#include "MantidAPI/Run.h"
13#include "MantidAPI/TableRow.h"
17#include "MantidHistogramData/HistogramMath.h"
23
24// String utilities
25#include <boost/algorithm/string.hpp>
26#include <boost/regex.hpp>
27#include <boost/tokenizer.hpp>
28
29#include <fstream>
30
31namespace Mantid::DataHandling {
33
34using namespace Kernel;
35using namespace API;
36
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) {}
41
49 const std::string &filePath = descriptor.filename();
50 const size_t filenameLength = filePath.size();
51
52 // Avoid some known file types that have different loaders
53 int confidence(0);
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)
57 : false) {
58 confidence = 0;
59 } else if (descriptor.isAscii()) {
60 confidence = 10; // Low so that others may try
61 }
62 return confidence;
63}
64
65//--------------------------------------------------------------------------
66// Protected methods
67//--------------------------------------------------------------------------
68
77 // it's probably more stirct versus version 1, but then this is a format
78 // change and we don't want any bad data getting into the workspace
79 // there is still flexibility, but the format should just make more sense in
80 // general
81
82 // if the file appears to be a table workspace then read it as such
83 auto ws = readTable(file);
84 if (ws) {
85 return ws;
86 }
87
88 m_baseCols = 0;
89 m_specNo = 0;
90 m_lastBins = 0;
91 m_curBins = 0;
92 m_spectraStart = true;
94
95 m_spectra.clear();
96 m_curSpectra = std::make_unique<DataObjects::Histogram1D>(HistogramData::Histogram::XMode::Points,
97 HistogramData::Histogram::YMode::Counts);
98 std::string line;
99
100 std::list<std::string> columns;
101
102 setcolumns(file, line, columns);
103
104 while (getline(file, line)) {
105 std::string templine = line;
106 m_lineNo++;
107 boost::trim(templine);
108 if (templine.empty()) {
109 // the line is empty, treat as a break before a new spectra
110 newSpectra();
111 } else if (!skipLine(templine)) {
112 parseLine(templine, columns);
113 }
114 }
115
116 newSpectra();
117
118 const size_t numSpectra = m_spectra.size();
119 MatrixWorkspace_sptr localWorkspace;
120 try {
121 localWorkspace = WorkspaceFactory::Instance().create("Workspace2D", numSpectra, m_lastBins, m_lastBins);
122 } catch (std::exception &e) {
123 std::ostringstream msg;
124 msg << "Failed to create a Workspace2D from the data found in this file. "
125 "Error: "
126 << e.what();
127 throw std::runtime_error(msg.str());
128 }
129
130 try {
131 writeToWorkspace(localWorkspace, numSpectra);
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());
136 }
137 m_curSpectra.reset();
138 localWorkspace->setDistribution(setDistribution(file));
139 return localWorkspace;
140}
141
142bool LoadAscii2::setDistribution(std::ifstream &file) {
143 bool isDistribution = false;
144 const bool distributionFlag = getProperty("ForceDistributionTrue");
145 if (distributionFlag) {
146 isDistribution = true;
147 } else {
148 std::string newLine;
149 // reset to start of ifstream
150 file.clear();
151 file.seekg(0);
152 while (std::getline(file, newLine)) {
153 if (newLine.find("Distribution=true") != std::string::npos) {
154 isDistribution = true;
155 break;
156 }
157 }
158 }
159 return isDistribution;
160}
161
167
169 // We need to see two rows commented out
170 // the first with column names
171 // the second with column types
172 // Then we need data, with the same number of columns as the first two lines
173
174 try {
175 size_t colNames = 0;
176 size_t colTypes = 0;
177 std::string line;
178 std::list<std::string> names;
179 std::list<std::string> types;
180 std::list<std::string> data;
181
182 while (getline(file, line)) {
183 boost::trim(line);
184
185 std::list<std::string> columns;
186
187 if (!line.empty()) {
188 // if line starts with a comment
189 if (line.at(0) == m_comment.at(0)) {
190 // remove the comment character
191 line.erase(0, 1);
192 size_t lineCols = this->splitIntoColumns(columns, line);
193 if (colNames == 0) {
194 colNames = lineCols;
195 names = columns;
196 continue;
197 }
198 if (colTypes == 0) {
199 colTypes = lineCols;
200 types = columns;
201 continue;
202 }
203 }
204
205 if (colTypes != colNames) {
206 // no point going further, the types and names differ in quantity
207 break;
208 }
209
210 size_t colData = this->splitIntoColumns(data, line);
211 if (colNames > 0 && colNames == colTypes && colTypes == colData) {
212 // we seem to have a table workspace
213 // if we have no already created a workspace
214 if (!ws) {
215 ws = std::make_shared<DataObjects::TableWorkspace>();
216 // create the columns
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;
222 // trim the strings
223 boost::trim(name);
224 boost::trim(type);
225 ws->addColumn(type, name);
226 itName++;
227 itTypes++;
228 }
229 }
230 // add the data
231 TableRow row = ws->appendRow();
232 auto itTypes = types.begin();
233 for (auto itData = data.begin(); itData != data.end(); itData++) {
234 // direct assignment only works for strings, we ill need to handle
235 // the other data types here
236 std::string type = *itTypes;
237 boost::trim(type);
238 if (type == "str") {
239 row << *itData;
240 } else if (type == "int") {
241 int num = boost::lexical_cast<int>(*itData);
242 row << num;
243 } else if (type == "uint") {
244 uint32_t num = boost::lexical_cast<uint32_t>(*itData);
245 row << num;
246 } else if (type == "long64") {
247 auto num = boost::lexical_cast<int64_t>(*itData);
248 row << num;
249 } else if (type == "size_t") {
250 size_t num = boost::lexical_cast<size_t>(*itData);
251 row << num;
252 } else if (type == "float") {
253 float num = boost::lexical_cast<float>(*itData);
254 row << num;
255 } else if (type == "double") {
256 double num = boost::lexical_cast<double>(*itData);
257 row << num;
258 } else if (type == "bool") {
259 bool val = (itData->at(0) == 't');
260 row << val;
261 } else if (type == "V3D") {
262 V3D val;
263 std::stringstream ss(*itData);
264 val.readPrinted(ss);
265 row << val;
266 } else {
267 throw std::runtime_error("unknown column data type " + type);
268 }
269 itTypes++;
270 }
271 }
272 }
273 }
274 // if the file does not have more than rowsToCheck, it will
275 // stop
276 // and raise the EndOfFile, this may cause problems for small workspaces.
277 // In this case clear the flag
278 if (file.eof()) {
279 file.clear(file.eofbit);
280 }
281 } catch (std::exception &ex) {
282 // log and squash the error, so we can still try to load the file as a
283 // matrix workspace
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";
287 // clear any workspace that we started loading
288 ws.reset();
289 }
290
291 // Seek the file pointer back to the start.
292 file.seekg(0, std::ios::beg);
293 return ws;
294}
295
302void LoadAscii2::parseLine(const std::string &line, std::list<std::string> &columns) {
303 if (std::isdigit(line.at(0)) || line.at(0) == '-' || line.at(0) == '+') {
304 const int cols = splitIntoColumns(columns, line);
305 if (cols > 4 || cols < 0) {
306 // there were more separators than there should have been, which isn't
307 // right, or something went rather wrong
308 throw std::runtime_error("Line " + std::to_string(m_lineNo) +
309 ": Sets of values must have between 1 and 3 delimiters");
310 } else if (cols == 1) {
311 // a size of 1 is a spectra ID as long as there are no alphabetic
312 // characters in it. Signifies the start of a new spectra if it wasn't
313 // preceeded with a blank line
314 newSpectra();
315
316 // at this point both vectors should be the same size (or the ID counter
317 // should be 0, but as we're here then that's out the window),
318 if (m_spectra.size() == m_spectrumIDcount) {
320 } else {
321 // if not then they've ommitted IDs in the the file previously and just
322 // decided to include one (which is wrong and confuses everything)
323 throw std::runtime_error("Line " + std::to_string(m_lineNo) +
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 "
328 "associated bins.");
329 }
330 const std::string singleNumber = columns.front();
331 try {
332 m_curSpectra->setSpectrumNo(boost::lexical_cast<int>(singleNumber));
333 } catch (boost::bad_lexical_cast &) {
334 // the single column number is not the spectrum ID, maybe it is the
335 // spectrum axis value
336 try {
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)" +
341 singleNumber);
342 }
343 }
344 } else {
346
347 checkLineColumns(cols);
348
349 addToCurrentSpectra(columns);
350 }
351 } else if (badLine(line)) {
352 throw std::runtime_error("Line " + std::to_string(m_lineNo) +
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: " +
356 m_comment + ".");
357 } else {
358 // strictly speaking this should never be hit, but just being sure
359 throw std::runtime_error("Line " + std::to_string(m_lineNo) +
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: " +
363 m_comment + ".");
364 }
365}
366
372void LoadAscii2::writeToWorkspace(API::MatrixWorkspace_sptr &localWorkspace, const size_t &numSpectra) const {
373 try {
374 localWorkspace->getAxis(0)->unit() = UnitFactory::Instance().create(getProperty("Unit"));
375 } catch (Exception::NotFoundError &) {
376 // Asked for dimensionless workspace (obviously not in unit factory)
377 }
378
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());
382 // if E or DX are ommitted they're implicitly initalised as 0
383 if (m_baseCols == 4 || m_baseCols == 3) {
384 // E in file
385 localWorkspace->setSharedE(i, m_spectra[i].sharedE());
386 }
387 // DX could be NULL
388 localWorkspace->setSharedDx(i, m_spectra[i].sharedDx());
389 if (m_spectrumIDcount != 0) {
390 localWorkspace->getSpectrum(i).setSpectrumNo(m_spectra[i].getSpectrumNo());
391 } else {
392 localWorkspace->getSpectrum(i).setSpectrumNo(static_cast<specnum_t>(i) + 1);
393 }
394 if (!m_spectrumAxis.empty()) {
395 localWorkspace->replaceAxis(1, std::make_unique<NumericAxis>(m_spectrumAxis));
396 }
397 }
398}
399
409void LoadAscii2::setcolumns(std::ifstream &file, std::string &line, std::list<std::string> &columns) {
410 m_lineNo = 0;
411 std::vector<double> values;
412 // processheader will also look for a base number of columns, to save time
413 // here if possible
414 // but if the user specifies a number of lines to skip that check won't happen
415 // in processheader
416 processHeader(file);
417 if (m_baseCols == 0 || m_baseCols > 4 || m_baseCols < 2) {
418 // first find the first data set and set that as the template for the number
419 // of data columns we expect from this file
420 while (getline(file, line) && (m_baseCols == 0 || m_baseCols > 4 || m_baseCols < 2)) {
421 // std::string line = line;
422 boost::trim(line);
423 if (!line.empty()) {
424 if (std::isdigit(line.at(0)) || line.at(0) == '-' || line.at(0) == '+') {
425 const int cols = splitIntoColumns(columns, line);
426 // we might have the first set of values but there can't be more than
427 // 3 commas if it is
428 // int values = std::count(line.begin(), line.end(), ',');
429 if (cols > 4 || cols < 1) {
430 // there were more separators than there should have been, which
431 // isn't right, or something went rather wrong
432 throw std::runtime_error("Sets of values must have between 1 and 3 delimiters. Found " +
433 std::to_string(cols) + ".");
434 } else if (cols != 1) {
435 try {
436 fillInputValues(values, columns);
437 } catch (boost::bad_lexical_cast &) {
438 continue;
439 }
440 // a size of 1 is most likely a spectra ID so ignore it, a value of
441 // 2, 3 or 4 is a valid data set
442 m_baseCols = cols;
443 }
444 }
445 }
446 }
447 // make sure some valid data has been found to set the amount of columns,
448 // and the file isn't at EOF
449 if (m_baseCols > 4 || m_baseCols < 2 || file.eof()) {
450 throw std::runtime_error("No valid data in file, check separator "
451 "settings or number of columns per bin.");
452 }
453
454 // start from the top again, this time filling in the list
455 file.seekg(0, std::ios_base::beg);
456 for (size_t i = 0; i < m_lineNo; i++) {
457 getline(file, line);
458 }
459 }
460}
461
466void LoadAscii2::processHeader(std::ifstream &file) {
467 // Most files will have some sort of header. If we've haven't been told how
468 // many lines to
469 // skip then try and guess
470 int numToSkip = getProperty("SkipNumLines");
471 if (numToSkip == EMPTY_INT()) {
472 size_t numCols = 0;
473 const size_t rowsToMatch(5);
474 // Have a guess where the data starts. Basically say, when we have say
475 // "rowsToMatch" lines of pure numbers
476 // in a row then the line that started block is the top of the data
477 size_t matchingRows = 0;
478 int validRows = 0;
479 size_t blankRows = 0;
480 int row = 0;
481 std::string line;
482 std::vector<double> values;
483 while (getline(file, line) && matchingRows < rowsToMatch) {
484 ++row;
485 boost::trim(line);
486
487 std::list<std::string> columns;
488 size_t lineCols = 0;
489
490 if (!line.empty()) {
491 if (badLine(line)) {
492 matchingRows = 0;
493 validRows = 0;
494 continue;
495 }
496
497 // a skipped line is a valid non-data line this shouldn't be counted as
498 // a matching line
499 // but neither should it reset the matching counter
500 if (skipLine(line, true)) {
501 ++validRows;
502 continue;
503 }
504 if (std::isdigit(line.at(0)) || line.at(0) == '-' || line.at(0) == '+') {
505 lineCols = this->splitIntoColumns(columns, line);
506 // we might have the first set of values but there can't be more than
507 // 3 delimiters if it is
508 if (lineCols > 4 || lineCols < 1) {
509 // there were more separators than there should have been,
510 // which isn't right, or something went rather wrong
511 matchingRows = 0;
512 validRows = 0;
513 continue;
514 } else if (lineCols != 1) {
515 try {
516 fillInputValues(values, columns);
517 } catch (boost::bad_lexical_cast &) {
518 matchingRows = 0;
519 validRows = 0;
520 continue;
521 }
522 // a size of 1 is most likely a spectra ID so ignore it, a value of
523 // 2, 3 or 4 is a valid data set
524 }
525 } else {
526 // line wasn't valid
527 matchingRows = 0;
528 validRows = 0;
529 continue;
530 }
531 } else {
532 // an empty line is legitimate but make sure there aren't too many in
533 // sucession as we need to see data
534 ++matchingRows;
535 ++validRows;
536 ++blankRows;
537 if (blankRows >= rowsToMatch) {
538 matchingRows = 1;
539 validRows = 1;
540 }
541 continue;
542 }
543
544 if (numCols == 0 && lineCols != 1) {
545 numCols = lineCols;
546 }
547
548 // to reduce the chance of finding problems later,
549 // the concurrent data should also have the same nubmer of columns
550 // if the data has a different number of columns to the previous lines
551 // start the coutner again assuming those previous lines were header info
552 if (lineCols == numCols || lineCols == 1) {
553 // line is valid increment the counter
554 ++matchingRows;
555 ++validRows;
556 } else {
557 numCols = lineCols;
558 matchingRows = 1;
559 validRows = 1;
560 }
561 }
562 // if the file does not have more than rowsToMatch + skipped lines, it will
563 // stop
564 // and raise the EndOfFile, this may cause problems for small workspaces.
565 // In this case clear the flag
566 if (file.eof()) {
567 file.clear(file.eofbit);
568 }
569
570 // save some time in setcolumns as we've found the base columns
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.");
574 }
575 m_baseCols = numCols;
576 // Seek the file pointer back to the start.
577 // NOTE: Originally had this as finding the stream position of the data and
578 // then moving the file pointer
579 // back to the start of the data. This worked when a file was read on the
580 // same platform it was written
581 // but failed when read on a different one due to underlying differences in
582 // the stream translation.
583 file.seekg(0, std::ios::beg);
584 // We've read the header plus a number of validRows
585 numToSkip = row - validRows;
586 }
587 m_lineNo = 0;
588 std::string line;
589 while (m_lineNo < static_cast<size_t>(numToSkip) && getline(file, line)) {
590 ++m_lineNo;
591 }
592 g_log.information() << "Skipped " << numToSkip << " line(s) of header information()\n";
593}
594
599void LoadAscii2::addToCurrentSpectra(const std::list<std::string> &columns) {
600 std::vector<double> values(m_baseCols, 0.);
601 m_spectraStart = false;
602 fillInputValues(values, columns);
603 // add X and Y
604 auto histo = m_curSpectra->histogram();
605 histo.resize(histo.size() + 1);
606
607 histo.mutableX().back() = values[0];
608 histo.mutableY().back() = values[1];
609
610 // check for E and DX
611 switch (m_baseCols) {
612 // if only 2 columns X and Y in file, E = 0 is implicit when constructing
613 // workspace, omit DX
614 case 3: {
615 // E in file, include it, omit DX
616 histo.mutableE().back() = values[2];
617 break;
618 }
619 case 4: {
620 // E and DX in file, include both
621 histo.mutableE().back() = values[2];
622 m_curDx.emplace_back(values[3]);
623 break;
624 }
625 }
626 m_curSpectra->setHistogram(histo);
627 m_curBins++;
628}
629
634void LoadAscii2::checkLineColumns(const size_t &cols) const {
635 // a size of 2, 3 or 4 is a valid data set, but first see if it's the same as
636 // the first observed one
637 if (m_baseCols != cols) {
638 throw std::runtime_error("Number of data columns not consistent throughout file");
639 }
640}
641
646 // we need to do a check regarding spectra ids before doing anything else
647 // is this the first bin in the spectra? if not this check has already been
648 // done for this spectra
649 // If the ID vector is completly empty then it's ok we're assigning them later
650 // if there are equal or less IDs than there are spectra, then there's been no
651 // ID assigned to this spectra and there should be
652 if (m_spectraStart && m_spectrumIDcount != 0 && !(m_spectra.size() < m_spectrumIDcount)) {
653 throw std::runtime_error("Inconsistent inclusion of spectra IDs. All "
654 "spectra must have IDs or all spectra must not "
655 "have IDs."
656 " Check for blank lines, as they symbolize the "
657 "end of one spectra and the start of another.");
658 }
659}
660
665 if (!m_spectraStart) {
666 if (m_lastBins == 0) {
668 m_curBins = 0;
669 } else if (m_lastBins == m_curBins) {
670 m_curBins = 0;
671 } else {
672 throw std::runtime_error("Number of bins per spectra not consistant.");
673 }
674
675 if (m_curSpectra) {
676 size_t specSize = m_curSpectra->size();
677 if (specSize > 0 && specSize == m_lastBins) {
678 if (m_curSpectra->x().size() == m_curDx.size())
679 m_curSpectra->setPointStandardDeviations(std::move(m_curDx));
680 m_spectra.emplace_back(*m_curSpectra);
681 }
682 m_curSpectra.reset();
683 }
684
685 m_curSpectra = std::make_unique<DataObjects::Histogram1D>(HistogramData::Histogram::XMode::Points,
686 HistogramData::Histogram::YMode::Counts);
687 m_curDx.clear();
688 m_spectraStart = true;
689 }
690}
691
698bool LoadAscii2::skipLine(const std::string &line, bool header) const {
699 // Comments are skipped, Empty actually means something and shouldn't be
700 // skipped
701 // just checking the comment's first character should be ok as comment
702 // characters can't be numeric at all, so they can't really be confused
703 return ((line.empty() && header) || line.at(0) == m_comment.at(0));
704}
705
711bool LoadAscii2::badLine(const std::string &line) const {
712 // Empty or comment
713 return (!(std::isdigit(line.at(0)) || line.at(0) == '-' || line.at(0) == '+') && line.at(0) != m_comment.at(0));
714}
715
722int LoadAscii2::splitIntoColumns(std::list<std::string> &columns, const std::string &str) const {
723 boost::split(columns, str, boost::is_any_of(m_columnSep), boost::token_compress_on);
724 return static_cast<int>(columns.size());
725}
726
732void LoadAscii2::fillInputValues(std::vector<double> &values, const std::list<std::string> &columns) const {
733 values.resize(columns.size());
734 int i = 0;
735 for (auto value : columns) {
736 boost::trim(value);
737 boost::to_lower(value);
738 if (value == "nan" || value == "1.#qnan") // ignores nans (not a number) and
739 // replaces them with a nan
740 {
741 double nan = std::numeric_limits<double>::quiet_NaN(); //(0.0/0.0);
742 values[i] = nan;
743 } else {
744 values[i] = boost::lexical_cast<double>(value);
745 }
746 ++i;
747 }
748}
749
750//--------------------------------------------------------------------------
751// Private methods
752//--------------------------------------------------------------------------
755 const std::vector<std::string> exts{".dat", ".txt", ".csv", ""};
756 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, exts),
757 "The name of the text file to read, including its full or "
758 "relative path. The file extension must be .txt, .dat, or "
759 ".csv");
760 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "", Direction::Output),
761 "The name of the workspace that will be created, "
762 "filled with the read-in data and stored in the [[Analysis "
763 "Data Service]].");
764
765 const int numSpacers = 7;
766 std::string spacers[numSpacers][2] = {
767 {"Automatic", ",\t:; "}, {"CSV", ","}, {"Tab", "\t"}, {"Space", " "}, {"Colon", ":"}, {"SemiColon", ";"},
768 {"UserDefined", "UserDefined"}};
769 // For the ListValidator
770 std::array<std::string, numSpacers> sepOptions;
771 int sepOptionsIndex = 0;
772
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;
777 }
778
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.).");
785
786 declareProperty(std::make_unique<PropertyWithValue<std::string>>("CustomSeparator", "", Direction::Input),
787 "If present, will override any specified choice given to Separator.");
788
789 setPropertySettings("CustomSeparator",
790 std::make_unique<VisibleWhenProperty>("Separator", IS_EQUAL_TO, "UserDefined"));
791
792 declareProperty("CommentIndicator", "#",
793 "Character(s) found front of "
794 "comment lines. Cannot contain "
795 "numeric characters");
796
797 std::vector<std::string> units = UnitFactory::Instance().getKeys();
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\")");
802
803 auto mustBePosInt = std::make_shared<BoundedValidator<int>>();
804 mustBePosInt->setLower(0);
805 declareProperty("SkipNumLines", EMPTY_INT(), mustBePosInt,
806 "If given, skip this number of lines at the start of the file.");
807 declareProperty("ForceDistributionTrue", false,
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.");
810}
811
816 m_lineNo = 0;
817 std::string filename = getProperty("Filename");
818 std::ifstream file(filename.c_str());
819 if (!file) {
820 g_log.error("Unable to open file: " + filename);
821 throw Exception::FileError("Unable to open file: ", filename);
822 }
823
824 std::string sepOption = getProperty("Separator");
825 m_columnSep = m_separatorIndex[sepOption];
826
827 std::string choice = getPropertyValue("Separator");
828 std::string custom = getPropertyValue("CustomSeparator");
829 std::string sep;
830 // If the custom separator property is not empty, then we use that under any
831 // circumstance.
832 if (!custom.empty()) {
833 sep = custom;
834 }
835 // Else if the separator drop down choice is not UserDefined then we use that.
836 else if (choice != "UserDefined") {
837 auto it = m_separatorIndex.find(choice);
838 sep = it->second;
839 }
840 // If we still have nothing, then we are forced to use a default.
841 if (sep.empty()) {
842 g_log.notice() << "\"UserDefined\" has been selected, but no custom "
843 "separator has been entered."
844 " Using default instead.\n";
845 sep = ",";
846 }
847 m_columnSep = sep;
848
849 // e + and - are included as they're part of the scientific notation
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'");
853 }
854
855 std::string tempcomment = getProperty("CommentIndicator");
856
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");
862 }
863 m_comment = tempcomment;
864
865 // Process the header information.
866 // processHeader(file);
867 // Read the data
869 try {
870 rd = readData(file);
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, "
874 "cannot continue.");
875 }
876 MatrixWorkspace_sptr outputWS = std::dynamic_pointer_cast<MatrixWorkspace>(rd);
877 if (outputWS) {
878 outputWS->mutableRun().addProperty("Filename", filename);
879 }
880 setProperty("OutputWorkspace", rd);
881}
882} // namespace Mantid::DataHandling
double value
The value of the point.
Definition: FitMW.cpp:51
#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.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
@ Load
allowed here which will be passed to the algorithm
Definition: FileProperty.h:52
TableRow represents a row in a TableWorkspace.
Definition: TableRow.h:39
A property class for workspaces.
void processHeader(std::ifstream &file)
Process the header information.
Definition: LoadAscii2.cpp:466
void newSpectra()
check and configure flags and values relating to starting a new spectra
Definition: LoadAscii2.cpp:664
void init() override
Declare properties.
Definition: LoadAscii2.cpp:754
int splitIntoColumns(std::list< std::string > &columns, const std::string &str) const
Split the data into columns.
Definition: LoadAscii2.cpp:722
std::string m_columnSep
The column separator.
Definition: LoadAscii2.h:88
std::unique_ptr< DataObjects::Histogram1D > m_curSpectra
Definition: LoadAscii2.h:107
std::map< std::string, std::string > m_separatorIndex
Map the separator options to their string equivalents.
Definition: LoadAscii2.h:97
std::vector< double > m_curDx
Definition: LoadAscii2.h:108
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...
Definition: LoadAscii2.cpp:409
std::vector< DataObjects::Histogram1D > m_spectra
Definition: LoadAscii2.h:106
bool skipLine(const std::string &line, bool header=false) const
Return true if the line is to be skipped.
Definition: LoadAscii2.cpp:698
void checkLineColumns(const size_t &cols) const
Check if the file has been found to inconsistently include spectra IDs.
Definition: LoadAscii2.cpp:634
int confidence(Kernel::FileDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
Definition: LoadAscii2.cpp:48
bool setDistribution(std::ifstream &file)
Definition: LoadAscii2.cpp:142
void fillInputValues(std::vector< double > &values, const std::list< std::string > &columns) const
Fill the given vector with the data values.
Definition: LoadAscii2.cpp:732
const std::string name() const override
The name of the algorithm.
Definition: LoadAscii2.h:40
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...
Definition: LoadAscii2.cpp:302
virtual API::Workspace_sptr readTable(std::ifstream &file)
Read the data from the file into a table workspace.
Definition: LoadAscii2.cpp:166
void exec() override
Execute the algorithm.
Definition: LoadAscii2.cpp:815
LoadAscii2()
Default constructor.
Definition: LoadAscii2.cpp:38
void writeToWorkspace(API::MatrixWorkspace_sptr &localWorkspace, const size_t &numSpectra) const
Construct the workspace.
Definition: LoadAscii2.cpp:372
std::vector< double > m_spectrumAxis
Definition: LoadAscii2.h:109
bool badLine(const std::string &line) const
Return true if the line doesn't start with a valid character.
Definition: LoadAscii2.cpp:711
void inconsistantIDCheck() const
Check if the file has been found to incosistantly include spectra IDs.
Definition: LoadAscii2.cpp:645
void addToCurrentSpectra(const std::list< std::string > &columns)
Check if the file has been found to inconsistently include spectra IDs.
Definition: LoadAscii2.cpp:599
virtual API::Workspace_sptr readData(std::ifstream &file)
Read the data from the file.
Definition: LoadAscii2.cpp:76
Records the filename and the description of failure.
Definition: Exception.h:98
Exception for when an item is not found in a collection.
Definition: Exception.h:145
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.
Definition: Logger.cpp:95
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
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...
Class for 3D vectors.
Definition: V3D.h:34
void readPrinted(std::istream &)
Read data from a stream in the format returned by printSelf ("[x,y,z]").
Definition: V3D.cpp:364
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Definition: Workspace_fwd.h:20
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.
Definition: EmptyValues.h:25
int32_t specnum_t
Typedef for a spectrum Number.
Definition: IDTypes.h:16
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54