Mantid
Loading...
Searching...
No Matches
SaveReflectometryAscii.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 +
11#include "MantidAPI/Run.h"
20
21#include <Poco/File.h>
22#include <boost/lexical_cast.hpp>
23#include <cmath>
24#include <iomanip>
25#include <limits>
26#include <map>
27#include <memory>
28#include <stdexcept>
29
30namespace Mantid::DataHandling {
31
32using namespace Kernel;
33using namespace API;
34
35// Register the algorithm into the algorithm factory
36DECLARE_ALGORITHM(SaveReflectometryAscii)
37
38
40 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::Input),
41 "The name of the workspace containing the data you want to save.");
42 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Save), "The output filename");
43 std::vector<std::string> extension = {".mft", ".txt", ".dat", ".lam", "custom"};
44 declareProperty("FileExtension", ".mft", std::make_shared<StringListValidator>(extension),
45 "Choose the file extension according to the file format.");
46 auto mft = std::make_unique<VisibleWhenProperty>("FileExtension", IS_EQUAL_TO, "mft");
47 auto cus = std::make_unique<VisibleWhenProperty>("FileExtension", IS_EQUAL_TO, "custom");
48 declareProperty(std::make_unique<ArrayProperty<std::string>>("LogList"), "List of logs to write to file.");
49 setPropertySettings("LogList", std::make_unique<VisibleWhenProperty>(std::move(mft), std::move(cus), OR));
50 declareProperty("WriteHeader", false, "Whether to write header lines.");
51 setPropertySettings("WriteHeader", std::make_unique<VisibleWhenProperty>("FileExtension", IS_EQUAL_TO, "custom"));
52 std::vector<std::string> separator = {"comma", "space", "tab"};
53 declareProperty("WriteResolution", true,
54 "Whether to compute resolution values and write them as fourth "
55 "data column.");
56 setPropertySettings("WriteResolution", std::make_unique<VisibleWhenProperty>("FileExtension", IS_EQUAL_TO, "custom"));
57 declareProperty("Separator", "tab", std::make_shared<StringListValidator>(separator),
58 "The separator used for splitting data columns.");
59 setPropertySettings("Separator", std::make_unique<VisibleWhenProperty>("FileExtension", IS_EQUAL_TO, "custom"));
60 declareProperty("Theta", 0.0, "The angle (in deg) used to calculate wavelength from momentum exchange.");
61 setPropertySettings("Theta", std::make_unique<VisibleWhenProperty>("FileExtension", IS_EQUAL_TO, ".lam"));
62}
63
65std::map<std::string, std::string> SaveReflectometryAscii::validateInputs() {
66 std::map<std::string, std::string> issues;
67 m_filename = getPropertyValue("Filename");
68 m_ext = getPropertyValue("FileExtension");
69 if (m_ext != "custom" && m_filename.find(m_ext) > m_filename.size()) {
70 // second check avoids appending redundant extension in case it is already specified
71 m_filename.append(m_ext);
72 }
73 m_ws = getProperty("InputWorkspace");
74 if (!m_ws) {
76 AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(getPropertyValue("InputWorkspace"));
77 if (!group)
78 issues["InputWorkspace"] = "Must be a MatrixWorkspace";
79 } else {
80 try {
81 m_ws->y(0).size();
82 } catch (std::range_error &) {
83 issues["InputWorkspace"] = "Workspace does not contain data";
84 }
85 }
86 if (m_ext == ".lam") {
87 m_theta = M_PI * static_cast<double>(getProperty("Theta")) / 180.0;
88 if (m_theta == 0.0) {
89 issues["Theta"] = "The theta angle necessary to calculate wavelength is not defined.";
90 }
91 }
92 return issues;
93}
94
97 m_file << std::scientific;
98 m_file << std::setprecision(std::numeric_limits<double>::digits10);
99 const auto points = m_ws->points(0);
100 const auto &yData = m_ws->y(0);
101 const auto &eData = m_ws->e(0);
102 for (size_t i = 0; i < m_ws->y(0).size(); ++i) {
103 outputval(points[i], true);
104 outputval(yData[i]);
105 outputval(eData[i]);
106 if (includeQResolution()) {
107 if (m_ws->hasDx(0))
108 outputval(m_ws->dx(0)[i]);
109 else
110 outputval(points[i] * ((points[1] + points[0]) / points[1]));
111 }
112 if (m_ext == ".lam") {
113 // the final column contains wavelength calculated using momentum exchange (first column)
114 outputval(4 * M_PI * sin(m_theta) / points[i]);
115 }
116 m_file << '\n';
117 }
118}
119
122 if (m_ext == "custom") {
123 const std::string sepOption = getProperty("Separator");
124 if (sepOption == "comma")
125 m_sep = ',';
126 if (sepOption == "space")
127 m_sep = ' ';
128 }
129}
130
133 // Always include the resolution for txt format
134 if (m_ext == ".txt")
135 return true;
136 // Only include the resolution for the Custom format if the option is set
137 if (m_ext == "custom" && getProperty("WriteResolution"))
138 return true;
139 // Only include the resolution for MFT if the workspace contains it
140 if ((m_ext == ".mft" || m_ext == ".lam") && m_ws->hasDx(0))
141 return true;
142
143 return false;
144}
145
150template <typename T> void SaveReflectometryAscii::outputval(const T &val, bool firstColumn) {
151
152 if constexpr (std::is_floating_point<T>::value) {
153 if (std::isinf(val))
154 return outputval("inf", firstColumn);
155 if (std::isnan(val))
156 return outputval("nan", firstColumn);
157 }
158
159 if (m_ext == "custom" || m_ext == ".txt") {
160 if (!firstColumn)
161 m_file << m_sep;
162 m_file << val;
163 } else {
164 m_file << std::setw(28) << val;
165 }
166}
167
169std::string SaveReflectometryAscii::sampleLogValue(const std::string &logName) {
170 auto run = m_ws->run();
171 try {
172 return boost::lexical_cast<std::string>(run.getLogData(logName)->value());
173 } catch (Exception::NotFoundError &) {
174 return "Not defined";
175 }
176}
177
179std::string SaveReflectometryAscii::sampleLogUnit(const std::string &logName) {
180 auto run = m_ws->run();
181 try {
182 return " " + boost::lexical_cast<std::string>(run.getLogData(logName)->units());
183 } catch (Exception::NotFoundError &) {
184 return "";
185 }
186}
187
192void SaveReflectometryAscii::writeInfo(const std::string &logName, const std::string &logNameFixed) {
193 const std::string logValue = sampleLogValue(logName);
194 const std::string logUnit = sampleLogUnit(logName);
195 if (!logNameFixed.empty()) {
196 // The logName corresponds to an existing header line of given name
197 m_file << logNameFixed;
198 } else {
199 // The user provided a log name which is not part of the defined header
200 m_file << logName;
201 }
202 m_file << " : " << logValue << logUnit << '\n';
203}
204
207 m_file << std::setfill(' ');
208 auto fileType = "MFT\n";
209 if (m_ext == ".lam")
210 fileType = "LAM\n";
211 m_file << fileType;
212 std::vector<std::string> logs{"instrument.name", "user.namelocalcontact", "title", "start_time", "end_time"};
213 writeInfo("instrument.name", "Instrument");
214 writeInfo("user.namelocalcontact", "User-local contact");
215 writeInfo("title", "Title");
216 writeInfo("", "Subtitle");
217 writeInfo("start_time", "Start date + time");
218 writeInfo("end_time", "End date + time");
219 writeInfo("", "Theta 1 + dir + ref numbers");
220 writeInfo("", "Theta 2 + dir + ref numbers");
221 writeInfo("", "Theta 3 + dir + ref numbers");
222 const std::vector<std::string> logList = getProperty("LogList");
223 int nLogs = 0;
224 for (const auto &log : logList) {
225 if (find(logs.cbegin(), logs.cend(), log) == logs.end()) { // do not repeat a log
226 writeInfo(log);
227 ++nLogs;
228 }
229 }
230 // Write "Parameter : Not defined" 9 times minus the number of new user logs
231 for (auto i = nLogs; i < 9; ++i)
232 writeInfo("", "Parameter ");
233 m_file << "Number of file format : "
234 << "40\n";
235 m_file << "Number of data points : " << m_ws->y(0).size() << '\n';
236 m_file << '\n';
237 outputval("q", true);
238 outputval("refl");
239 outputval("refl_err");
240 if (includeQResolution())
241 outputval("q_res (FWHM)");
242 if (m_ext == ".lam")
243 outputval("wavelength");
244 m_file << "\n";
245}
246
248void SaveReflectometryAscii::checkFile(const std::string &filename) {
249 if (Poco::File(filename).exists()) {
250 g_log.warning("File already exists and will be overwritten");
251 try {
252 Poco::File(filename).remove();
253 } catch (...) { // maybe we do not have the permission to delete the file
254 g_log.error("Error deleting file " + filename);
255 }
256 }
257 m_file.open(filename);
258 if (!m_file.is_open()) {
259 g_log.error("Unable to create file: " + filename);
260 }
261 g_log.information("Filename: " + filename);
262}
263
267 separator();
268 if ((getProperty("WriteHeader") && m_ext == "custom") || m_ext == ".mft" || m_ext == ".lam")
269 header();
270 else if (m_ext == ".dat")
271 m_file << m_ws->y(0).size() << "\n";
272 data();
273 m_file.close();
274}
275
278 try {
280 AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>(getPropertyValue("InputWorkspace"));
281 if (!group)
282 return false;
283 for (auto i : group->getAllItems()) {
284 if (i->getName().empty())
285 g_log.warning("InputWorkspace must have a name, skip");
286 else {
287 const auto ws = std::dynamic_pointer_cast<MatrixWorkspace>(i);
288 if (!ws)
289 g_log.warning("WorkspaceGroup must contain MatrixWorkspaces, skip");
290 else {
291 try {
292 ws->y(0).size();
293 } catch (std::range_error &) {
294 throw(std::runtime_error("InputWorkspace does not contain data"));
295 }
296 m_group.emplace_back(ws);
297 m_wsName.emplace_back(i->getName()); // since we lost names
298 }
299 }
300 }
301 if (group->isEmpty())
302 g_log.warning("WorkspaceGroup does not contain MatrixWorkspaces");
303 const std::string filename = getPropertyValue("Filename");
304 if (filename.empty())
305 throw(std::runtime_error("Provide a file name"));
306 m_ext = getPropertyValue("FileExtension");
307 return true;
308 } catch (...) {
309 return false;
310 }
311}
312
315 const std::string filename = getPropertyValue("Filename");
316 for (auto i = 0u; i < m_group.size(); ++i) {
317 m_ws = m_group[i]->clone();
318 std::string ending{""};
319 try {
320 ending = filename.substr(filename.find("."));
321 } catch (...) {
322 }
323 if (ending.empty())
324 ending = m_ext;
325 m_filename = filename.substr(0, filename.find(".")) + m_wsName[i] + ending;
326 this->exec();
327 }
328 return true;
329}
330
331} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
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
Kernel::Logger & g_log
Definition: Algorithm.h:451
@ Save
to specify a file to write to, the file may or may not exist
Definition: FileProperty.h:49
Class to hold a set of workspaces.
A property class for workspaces.
Saves a file of desired (mft, txt, dat or custom) Ascii format from a 2D workspace.
bool checkGroups() override
Check if input workspace is a group.
void checkFile(const std::string &filename)
Check file validity.
std::ofstream m_file
The output file stream.
std::vector< std::string > m_wsName
Names of the workspaces in a group.
std::vector< API::MatrixWorkspace_const_sptr > m_group
Input workspace group.
API::MatrixWorkspace_const_sptr m_ws
Input workspace.
double m_theta
The angle used to calculate wavelength from momentum exchange, in rad.
void writeInfo(const std::string &logName, const std::string &logNameFixed="")
Write one header line.
void exec() override
Algorithm execution for single MatrixWorkspaces.
std::string sampleLogValue(const std::string &logName)
Retrieve sample log value.
void outputval(const T &val, bool firstColumn=false)
Print a value to file.
std::string sampleLogUnit(const std::string &logName)
Retrieve sample log unit.
bool processGroups() override
Algorithm execution for WorkspaceGroups.
std::map< std::string, std::string > validateInputs() override
Cross-check properties with each other.
bool includeQResolution() const
Whether the Q resolution should be included in the output.
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
Exception for when an item is not found in a collection.
Definition: Exception.h:145
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
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< const WorkspaceGroup > WorkspaceGroup_const_sptr
shared pointer to Mantid::API::WorkspaceGroup, pointer to const version
bool exists(::NeXus::File &file, const std::string &name)
Based on the current group in the file, does the named sub-entry exist?
@ Input
An input workspace.
Definition: Property.h:53