24#include <boost/regex.hpp>
25#include <boost/tokenizer.hpp>
33using namespace Kernel;
38 : m_separatorIndex(), m_nBins(0), m_sep(), m_writeDX(false), m_writeID(false), m_isCommonBins(false),
39 m_writeSpectrumAxisValue(false), m_ws() {}
44 "The name of the workspace containing the data you want to save to a "
48 "The filename of the output Ascii file.");
50 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
51 mustBePositive->setLower(1);
52 auto mustBeZeroGreater = std::make_shared<BoundedValidator<int>>();
53 mustBeZeroGreater->setLower(0);
55 "The starting workspace index. Ignored for Table Workspaces.");
57 "The ending workspace index. Ignored for Table Workspaces.");
59 "List of workspace indices to save. Ignored for Table Workspaces.");
62 "If true, the values will be "
63 "written to the file in "
64 "scientific notation.");
66 "If true, the error on X will be written as the fourth "
67 "column. Ignored for Table Workspaces.");
69 "If false, the spectrum No will not be written for "
70 "single-spectrum workspaces. "
71 "It is always written for workspaces with multiple spectra, "
72 "unless spectrum axis value is written. Ignored for Table Workspaces.");
74 declareProperty(
"CommentIndicator",
"#",
"Character(s) to put in front of comment lines.");
77 std::string spacers[6][2] = {{
"CSV",
","}, {
"Tab",
"\t"}, {
"Space",
" "},
78 {
"Colon",
":"}, {
"SemiColon",
";"}, {
"UserDefined",
"UserDefined"}};
79 std::vector<std::string> sepOptions;
80 for (
auto &spacer : spacers) {
81 std::string option = spacer[0];
82 m_separatorIndex.insert(std::pair<std::string, std::string>(option, spacer[1]));
83 sepOptions.emplace_back(option);
86 declareProperty(
"Separator",
"CSV", std::make_shared<StringListValidator>(sepOptions),
87 "The separator between data columns in the data file. The "
88 "possible values are \"CSV\", \"Tab\", "
89 "\"Space\", \"SemiColon\", \"Colon\" or \"UserDefined\".");
92 "If present, will override any specified choice given to Separator.");
95 std::make_unique<VisibleWhenProperty>(
"Separator",
IS_EQUAL_TO,
"UserDefined"));
98 "If true, put column headers into file. Even if false, a header"
99 "is automatically added if the workspace is Distribution = true.");
102 "A comma separated list that defines data that describes "
103 "each spectrum in a workspace. The valid options for this "
104 "are: SpectrumNumber,Q,Angle. Ignored for Table Workspaces.");
106 declareProperty(
"AppendToFile",
false,
"If true, don't overwrite the file. Append to the end of it. ");
109 "If true, ensure that more than one xspectra is used. "
110 "Ignored for Table Workspaces.");
113 "Write the spectrum axis value if requested. Ignored for "
114 "Table Workspaces.");
117 "List of logs to write to the file header. Ignored for Table "
120 declareProperty(
"OneSpectrumPerFile",
false,
"If true, each spectrum will be saved to an individual file");
129 m_ws = std::dynamic_pointer_cast<const MatrixWorkspace>(ws);
133 const bool writeHeader =
getProperty(
"ColumnHeader");
134 const bool appendToFile =
getProperty(
"AppendToFile");
144 if (!custom.empty()) {
149 else if (choice !=
"UserDefined") {
155 g_log.
notice() <<
"\"UserDefined\" has been selected, but no custom "
156 "separator has been entered."
157 " Using default instead.";
168 throw std::runtime_error(
"SaveAscii does not now how to save this workspace type, " + ws->getName());
172 std::vector<int> spec_list =
getProperty(
"SpectrumList");
173 const int spec_min =
getProperty(
"WorkspaceIndexMin");
174 const int spec_max =
getProperty(
"WorkspaceIndexMax");
180 if (!metaDataString.empty()) {
183 if (containsSpectrumNumber) {
185 m_ws->getSpectrumToWorkspaceIndexMap();
186 }
catch (
const std::runtime_error &) {
187 throw std::runtime_error(
"SpectrumNumber is present in "
188 "SpectrumMetaData but the workspace does not "
189 "have a SpectrumAxis.");
195 if (!containsSpectrumNumber) {
197 m_metaData.insert(firstIter,
"spectrumnumber");
202 auto spectrumAxis =
m_ws->getAxis(1);
204 m_axisProxy = std::make_unique<AxisHelper::BinEdgeAxisProxy>(spectrumAxis);
206 m_axisProxy = std::make_unique<AxisHelper::AxisProxy>(spectrumAxis);
211 if (!boost::regex_match(
m_sep.begin(),
m_sep.end(), boost::regex(
"[^0-9e+-]+", boost::regex::perl))) {
212 throw std::invalid_argument(
"Separators cannot contain numeric characters, "
213 "plus signs, hyphens or 'e'");
216 if (comment.at(0) ==
m_sep.at(0) ||
217 !boost::regex_match(comment.begin(), comment.end(),
218 boost::regex(
"[^0-9e" +
m_sep +
"+-]+", boost::regex::perl))) {
219 throw std::invalid_argument(
"Comment markers cannot contain numeric "
220 "characters, plus signs, hyphens,"
221 " 'e' or the selected separator character");
227 auto nSpectra =
static_cast<int>(
m_ws->getNumberHistograms());
233 if (spec_min >=
nSpectra || spec_max >=
nSpectra || spec_min < 0 || spec_max < 0 || spec_min > spec_max) {
234 throw std::invalid_argument(
"Inconsistent spectra interval");
236 for (
int i = spec_min; i <= spec_max; i++) {
243 if (!spec_list.empty()) {
244 for (
auto &spec : spec_list) {
246 throw std::invalid_argument(
"Inconsistent spectra list");
255 for (
int i = 0; i <
nSpectra; i++) {
261 throw std::runtime_error(
"Trying to save an empty workspace");
264 const bool oneSpectrumPerFile =
getProperty(
"OneSpectrumPerFile");
273 const bool isDistribution =
m_ws->isDistribution();
274 auto idxIt = idx.begin();
275 while (idxIt != idx.end()) {
276 std::string currentFilename;
277 if (oneSpectrumPerFile)
280 currentFilename = filename;
282 std::ofstream file(currentFilename, (appendToFile ? std::ios::app : std::ios::out));
289 file.precision(prec);
292 file << std::scientific;
294 const std::vector<std::string> logList =
getProperty(
"LogList");
295 if (!logList.empty()) {
298 if (writeHeader || isDistribution) {
299 file << comment <<
" X " <<
m_sep <<
" Y " <<
m_sep <<
" E";
301 file <<
" " <<
m_sep <<
" DX";
303 file <<
" Distribution=" << (isDistribution ?
"true" :
"false");
308 if (oneSpectrumPerFile) {
313 while (idxIt != idx.end()) {
320 file.unsetf(std::ios_base::floatfield);
335 extPosition = filename.find(ext);
336 if (extPosition != std::string::npos)
339 if (extPosition == std::string::npos)
340 extPosition = filename.size();
342 std::ostringstream ss;
343 ss << std::string(filename, 0, extPosition) <<
"_" << workspaceIndex;
344 auto axis =
m_ws->getAxis(1);
345 if (axis->isNumeric()) {
346 auto binEdgeAxis =
dynamic_cast<BinEdgeAxis *
>(axis);
348 ss <<
"_" << binEdgeAxis->
label(workspaceIndex) << axis->unit()->label().ascii();
350 ss <<
"_" << axis->getValue(workspaceIndex) << axis->unit()->label().ascii();
351 }
else if (axis->isText())
352 ss <<
"_" << axis->label(workspaceIndex);
353 ss << std::string(filename, extPosition);
372 file <<
" " <<
m_sep <<
" ";
377 auto pointDeltas =
m_ws->pointStandardDeviations(0);
378 auto points0 =
m_ws->points(0);
379 auto pointsSpec =
m_ws->points(wsIndex);
380 bool hasDx =
m_ws->hasDx(0);
381 for (
int bin = 0; bin <
m_nBins; bin++) {
383 file << points0[bin];
386 file << pointsSpec[bin];
389 file <<
m_ws->y(wsIndex)[bin];
392 file <<
m_ws->e(wsIndex)[bin];
396 file << pointDeltas[bin];
398 g_log.
information(
"SaveAscii2: WriteXError is requested but there are no Dx data in the workspace");
412 const std::vector<std::string> validMetaData{
"spectrumnumber",
"q",
"angle"};
413 boost::to_lower(inputString);
415 for (
const auto &input : stringVector) {
416 if (std::find(validMetaData.begin(), validMetaData.end(), input) == validMetaData.end()) {
417 throw std::runtime_error(input +
" is not recognised as a possible input "
418 "for SpectrumMetaData.\n Valid inputs "
419 "are: SpectrumNumber, Q, Angle.");
431 std::vector<std::string> qValues;
432 const auto nHist =
m_ws->getNumberHistograms();
433 const auto &spectrumInfo =
m_ws->spectrumInfo();
434 for (
size_t i = 0; i < nHist; i++) {
435 double theta(0.0),
efixed(0.0);
436 if (!spectrumInfo.isMonitor(i)) {
437 theta = 0.5 * spectrumInfo.twoTheta(i);
439 std::shared_ptr<const Geometry::IDetector> detector(&spectrumInfo.detector(i),
NoDeleting());
441 }
catch (std::runtime_error &) {
450 auto qValueStr = boost::lexical_cast<std::string>(qValue);
451 qValues.emplace_back(qValueStr);
460 std::vector<std::string> spectrumNumbers;
461 const size_t nHist =
m_ws->getNumberHistograms();
462 for (
size_t i = 0; i < nHist; i++) {
463 const auto specNum =
m_ws->getSpectrum(i).getSpectrumNo();
465 spectrumNumbers.emplace_back(specNumStr);
474 std::vector<std::string> angles;
475 const size_t nHist =
m_ws->getNumberHistograms();
476 const auto &spectrumInfo =
m_ws->spectrumInfo();
477 for (
size_t i = 0; i < nHist; i++) {
478 const auto two_theta = spectrumInfo.twoTheta(i);
479 constexpr double rad2deg = 180. / M_PI;
480 const auto angleInDeg = two_theta *
rad2deg;
481 const auto angleInDegStr = boost::lexical_cast<std::string>(angleInDeg);
482 angles.emplace_back(angleInDegStr);
492 if (metaDataType ==
"spectrumnumber")
494 if (metaDataType ==
"q")
496 if (metaDataType ==
"angle")
502 return std::find(vector.cbegin(), vector.cend(), toFind) != vector.cend();
506 bool appendToFile,
bool writeHeader,
int prec,
bool scientific,
507 const std::string &comment) {
509 std::ofstream file(filename.c_str(), (appendToFile ? std::ios::app : std::ios::out));
516 file.precision(prec);
519 file << std::scientific;
522 const auto columnCount = tws->columnCount();
525 file << comment <<
" ";
526 for (
size_t colIndex = 0; colIndex < columnCount; colIndex++) {
527 file << tws->getColumn(colIndex)->name() <<
" ";
528 if (colIndex < columnCount - 1) {
529 file <<
m_sep <<
" ";
534 file << comment <<
" ";
535 for (
size_t colIndex = 0; colIndex < columnCount; colIndex++) {
536 file << tws->getColumn(colIndex)->type() <<
" ";
537 if (colIndex < columnCount - 1) {
538 file <<
m_sep <<
" ";
543 g_log.
warning(
"Please note that files written without headers cannot be "
544 "reloaded back into Mantid with LoadAscii.");
548 const auto rowCount = tws->rowCount();
550 for (
size_t rowIndex = 0; rowIndex < rowCount; rowIndex++) {
551 for (
size_t colIndex = 0; colIndex < columnCount; colIndex++) {
552 tws->getColumn(colIndex)->print(rowIndex, file);
553 if (colIndex < columnCount - 1) {
561 file.unsetf(std::ios_base::floatfield);
572 auto run =
m_ws->run();
574 std::string sampleLogValue =
"";
576 sampleLogValue = boost::lexical_cast<std::string>(run.getLogData(logName)->value());
579 sampleLogValue =
"Not defined";
582 std::string sampleLogUnit =
"";
584 sampleLogUnit = boost::lexical_cast<std::string>(run.getLogData(logName)->units());
588 return std::pair(sampleLogValue, sampleLogUnit);
598 for (
const auto &logName : logList) {
600 auto logValue = boost::replace_all_copy(readLog.second,
",",
";");
601 outputFile << logName <<
m_sep << readLog.first <<
m_sep << logValue <<
'\n';
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
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.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Stores numeric values that are assumed to be bin edge values.
std::string label(const std::size_t &index) const override
Returns a text label which shows the value at the given bin index.
@ Save
to specify a file to write to, the file may or may not exist
Helper class for reporting progress from algorithms.
A property class for workspaces.
bool findElementInUnorderedStringVector(const std::vector< std::string > &vector, const std::string &toFind)
API::MatrixWorkspace_const_sptr m_ws
void populateAngleMetaData()
Populate the map with the Angle for each spectrum in the workspace.
void populateSpectrumNumberMetaData()
Populate the map with the SpectrumNumber for each Spectrum in the workspace.
void writeSpectrum(const int &wsIndex, std::ofstream &file)
Writes a spectrum to the file using a workspace index.
std::map< std::string, std::vector< std::string > > m_metaDataMap
void writeTableWorkspace(const API::ITableWorkspace_const_sptr &tws, const std::string &filename, bool appendToFile, bool writeHeader, int prec, bool scientific, const std::string &comment)
void writeFileHeader(const std::vector< std::string > &logList, std::ofstream &file)
Write file header.
std::pair< std::string, std::string > sampleLogValueUnit(const std::string &logName)
Retrieve sample log value and its unit.
std::vector< std::string > stringListToVector(std::string &inputString)
Converts a comma separated list to a vector of strings Also ensures all strings are valid input.
bool m_writeSpectrumAxisValue
SaveAscii2()
Default constructor.
void populateAllMetaData()
Populate all required meta data in the meta data map.
const std::vector< std::string > m_asciiExts
void exec() override
Overwrites Algorithm method.
std::vector< std::string > m_metaData
std::string createSpectrumFilename(size_t workspaceIndex)
Create the filename used for the export of a specific spectrum.
void init() override
Overwrites Algorithm method.
std::map< std::string, std::string > m_separatorIndex
Map the separator options to their string equivalents.
void populateQMetaData()
Populate the map with the Q values associated with each spectrum in the workspace.
std::unique_ptr< AxisHelper::AxisProxy > m_axisProxy
Void deleter for shared pointers.
Support for a property that holds an array of values.
Records the filename and the description of failure.
Exception for when an item is not found in a collection.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
void notice(const std::string &msg)
Logs at notice 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.
void setAutoTrim(const bool &setting)
Sets if the property is set to automatically trim string unput values of whitespace.
static double convertToElasticQ(const double theta, const double efixed)
Convert to ElasticQ from Energy.
std::shared_ptr< const ITableWorkspace > ITableWorkspace_const_sptr
shared pointer to Mantid::API::ITableWorkspace (const version)
std::shared_ptr< const Workspace > Workspace_const_sptr
shared pointer to Mantid::API::Workspace (const version)
template DLLExport std::vector< std::string > splitStringIntoVector< std::string >(std::string listString)
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.