Mantid
Loading...
Searching...
No Matches
SaveOpenGenieAscii.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
11#include "MantidAPI/Run.h"
18
19#include <fstream>
20#include <vector>
21
22namespace Mantid::DataHandling {
23
24using namespace Kernel;
25using namespace Mantid::API;
26
27// Register the algorithm into the AlgorithmFactory
29
30
34void SaveOpenGenieAscii::init() {
35 declareProperty(
36 std::make_unique<API::WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Kernel::Direction::Input),
37 "The name of the workspace containing the data you wish to save");
38
39 // Declare required parameters, filename with ext {.his} and input
40 // workspace
41 const std::vector<std::string> exts{".his", ".txt", ""};
42 declareProperty(std::make_unique<API::FileProperty>("Filename", "", API::FileProperty::Save, exts),
43 "The filename to use for the saved data");
44 declareProperty("IncludeHeader", true, "Whether to include the header lines (default: true)");
45 std::vector<std::string> header{"ENGIN-X Format"};
46 declareProperty("OpenGenieFormat", "ENGIN-X Format", std::make_shared<Kernel::StringListValidator>(header),
47 "The format required to successfully load the file to "
48 "OpenGenie: ENGIN-X Format (default)");
49}
50
55 // Retrieve the input workspace
56 m_inputWS = getProperty("InputWorkspace");
57
59
60 // Its better to reserve over and the number of logs gives us a good
61 // estimate as some logs are not included whilst some other params are.
62 m_outputVector.reserve(m_inputWS->run().getLogData().size());
63
64 // Whilst this doesn't weigh in the processing
65 // required it breaks down the algorithm nicely for the moment
66 const int numOfSteps = 6;
67 Progress progressBar(this, 0.0, 1.0, numOfSteps);
68
69 const std::string formatType = getProperty("OpenGenieFormat");
70 // If we had a basic format this is where the specialization would go
71 if (formatType == "ENGIN-X Format") {
72 progressBar.report("Generating ENGINX header");
74 }
75
76 // Store empty but required field
77 progressBar.report("Storing empty fields");
79
80 // store common workspace properties
81 progressBar.report("Processing workspace information");
83
84 // store x, y, e to vector
85 progressBar.report("Processing workspace data");
89
90 progressBar.report("Processing log data");
92
93 std::ofstream outStream;
94 openFileStream(outStream);
95
96 progressBar.report("Writing to file");
97 writeDataToFile(outStream);
98 outStream.close();
99
100 // Indicate we have finished
101 progressBar.report();
102}
103
108 // Bank number
110
111 // Spectrum numbers
112 addToOutputBuffer("spec_no", m_stringType, "1");
113
114 // Par file that was used in the calibration
115 // This can be set to none at the moment as it does not affect the analysis
116 addToOutputBuffer("parameter_file", m_stringType, "None.par");
117 addToOutputBuffer("user_name", m_stringType, "NotSet");
118
119 // xunit & xlabel put in OpenGenie format
120 const std::string xunitsVal = "Time-of-Flight (\\\\gms)";
121 addToOutputBuffer("xunits", m_stringType, xunitsVal);
122 addToOutputBuffer("xlabel", m_stringType, xunitsVal);
123
124 // yunit & ylabel put in OpenGenie format
125 const std::string yunitsVal = "Neutron counts / \\\\gms";
126 addToOutputBuffer("yunits", m_stringType, yunitsVal);
127 addToOutputBuffer("ylabel", m_stringType, yunitsVal);
128}
129
138void SaveOpenGenieAscii::calculateXYZDelta(const std::string &unit, const Kernel::Property *values) {
139 // Cast to TimeSeries so we can use the min/max value methods
140 const auto positionValues = static_cast<const TimeSeriesProperty<double> *>(values);
141
142 const double deltaValue = positionValues->maxValue() - positionValues->minValue();
143
144 addToOutputBuffer('d' + unit, m_floatType, std::to_string(deltaValue));
145}
146
155template <typename T> void SaveOpenGenieAscii::convertWorkspaceData(const T &histoData, const char &axis) {
156 // Padding to apply after 10 data values
157 const std::string newLineStr = "\r\n ";
158 // Bank number - force to 1 at the moment
159 const std::string outputType = "GXRealarray\r\n 1";
160
161 // First 4 spaces for correct padding
162 std::string outputString(" ");
163 int valueCount(0);
164
165 // Working on primitive type so don't take ref
166 for (const auto val : histoData) {
167 if (valueCount % 10 == 0) {
168 outputString += newLineStr;
169 }
170 valueCount++;
171 outputString += std::to_string(val) + ' ';
172 }
173
174 // Have to put the number of values (second member of pair)
175 // followed by a space then a new line then the data into a string
176 auto outDataString = std::to_string(valueCount) + " \r\n" + std::move(outputString);
177 addToOutputBuffer(std::string(1, axis), outputType, outDataString);
178}
179
185 const auto &detectorIds = m_inputWS->getSpectrum(0).getDetectorIDs();
186 const std::string firstDetectorId = std::to_string(*detectorIds.cbegin());
187
188 if (firstDetectorId.length() != 6) {
189 g_log.warning("Could not determine bank ID as detector ID in ENGIN-X "
190 "workspace did not match expected format. You will need"
191 "manually specify the bank in OpenGenie");
192 return;
193 }
194
195 // ENGIN-X format is 1X001, 1X002, 1X003...etc. for detectors
196 // where X = 0 is bank 1. X = 1 is bank 2.
197 const int bankNumber = firstDetectorId[1] == '0' ? 1 : 2;
198 addToOutputBuffer("bank", m_intType, std::to_string(bankNumber));
199}
200
208 // Maps Mantid log names -> Genie save file name / type
209 const std::unordered_map<std::string, std::pair<std::string, std::string>> mantidGenieLogMapping = {
210 {"x", std::make_pair("x_pos", m_floatType)},
211 {"y", std::make_pair("y_pos", m_floatType)},
212 {"z", std::make_pair("z_pos", m_floatType)},
213 {"gd_prtn_chrg", std::make_pair("microamps", m_floatType)}};
214
215 const std::vector<Property *> &logData = m_inputWS->run().getLogData();
216
217 for (const auto &logEntry : logData) {
218 const std::string &logName = logEntry->name();
219
220 // Check this log value is known to us
221 const auto foundMapping = mantidGenieLogMapping.find(logName);
222 if (foundMapping == mantidGenieLogMapping.cend()) {
223 continue;
224 }
225
226 // Second member of map is the OpenGenie Name / Type as a pair
227 // First member of pair is name, second member of pair is the type
228 const std::string outName = foundMapping->second.first;
229 const std::string outType = foundMapping->second.second;
230 std::string outValue;
231
232 // Calculate dx/dy/dz
233 if (outName == "x_pos" || outName == "y_pos" || outName == "z_pos") {
234 calculateXYZDelta(foundMapping->first, logEntry);
235 } else if (outName == "microamps") {
236 // From reverse engineering the scripts the effective time is
237 // the microamps * 50 - what 50 represents is not documented
238 const std::string effectiveTime = std::to_string(std::stod(logEntry->value()) * 50.);
239 addToOutputBuffer("effective_time", m_floatType, effectiveTime);
240 }
241
242 if (dynamic_cast<ITimeSeriesProperty *>(logEntry)) {
243 outValue = std::to_string(m_inputWS->run().getTimeAveragedValue(logName));
244 } else {
245 outValue = logEntry->value();
246 }
247
248 addToOutputBuffer(outName, outType, outValue);
249 }
250}
251
258 const size_t nSpectra = m_inputWS->getNumberHistograms();
259
260 if (m_inputWS->blocksize() == 0 || nSpectra == 0)
261 throw std::runtime_error("Trying to save an empty workspace");
262 else if (nSpectra > 1) {
263 throw std::runtime_error("Workspace has multiple spectra. This algorithm "
264 "can only save focused workspaces.");
265 } else if (!m_inputWS->isHistogramData()) {
266 throw std::runtime_error("This algorithm cannot save workspaces with event "
267 "data, please convert to histogram data first.");
268 }
269}
270
276void SaveOpenGenieAscii::openFileStream(std::ofstream &stream) {
277 // Retrieve the filename from the properties
278 const std::string filename = getProperty("Filename");
279 // Open file as binary so it doesn't convert CRLF to LF on UNIX
280 stream.open(filename, std::ofstream::binary | std::ofstream::out);
281 if (!stream) {
282 g_log.error("Unable to create file: " + filename);
283 throw Exception::FileError("Unable to create file: ", filename);
284 }
285}
286
293 const std::string floatVal = "999.000";
294 addToOutputBuffer("eurotherm", m_floatType, floatVal);
295 addToOutputBuffer("eurotherm_error", m_floatType, floatVal);
296
297 addToOutputBuffer("load", m_floatType, floatVal);
298 addToOutputBuffer("load_error", m_floatType, floatVal);
299 addToOutputBuffer("macro_strain", m_floatType, floatVal);
300 addToOutputBuffer("macro_strain_error", m_floatType, floatVal);
301 addToOutputBuffer("theta_pos", m_floatType, floatVal);
302 addToOutputBuffer("theta_pos_error", m_floatType, floatVal);
303
304 addToOutputBuffer("pos", m_floatType, floatVal);
305 addToOutputBuffer("pos_error", m_floatType, floatVal);
306 addToOutputBuffer("x_pos_error", m_floatType, floatVal);
307 addToOutputBuffer("y_pos_error", m_floatType, floatVal);
308 addToOutputBuffer("z_pos_error", m_floatType, floatVal);
309}
310
316 // Run Number
317 addToOutputBuffer("run_no", m_stringType, std::to_string(m_inputWS->getRunNumber()));
318 // Workspace title
319 addToOutputBuffer("title", m_stringType, m_inputWS->getTitle());
320 // Instrument name
321 addToOutputBuffer("inst_name", m_stringType, m_inputWS->getInstrument()->getName());
322 // Number of bins
324 // L1 (Source to sample distance)
325 const auto &specInfo = m_inputWS->spectrumInfo();
326 addToOutputBuffer("l1", m_floatType, std::to_string(specInfo.l1()));
327 // L2 (Sample to spectrum distance)
328 addToOutputBuffer("l2", m_floatType, std::to_string(specInfo.l2(0)));
329 // Unsigned scattering angle 2theta
330 const double two_theta = specInfo.twoTheta(0) * 180 / M_PI; // Convert to deg
331 addToOutputBuffer("twotheta", m_floatType, std::to_string(two_theta));
332}
333
340void SaveOpenGenieAscii::writeDataToFile(std::ofstream &outfile) {
341 // Write header
342 if (getProperty("IncludeHeader")) {
343 outfile << "# Open Genie ASCII File #\r\n"
344 << "# label \r\n"
345 << "GXWorkspace\r\n"
346 << m_outputVector.size() << "\r\n";
347 }
348
349 // Sort by parameter name
350 std::sort(m_outputVector.begin(), m_outputVector.end(),
351 [](const OutputBufferEntry &t1, const OutputBufferEntry &t2) { return std::get<0>(t1) < std::get<0>(t2); });
352
353 for (const auto &outTuple : m_outputVector) {
354 // Format is 2 spaces followed by parameter name, newline
355 // 4 spaces then the type name, newline
356 // 4 spaces and value(s), newline
357
358 // The parameter name must be surrounded with quotes
359 // If the type is a string the value must be wrapped in quotes
360
361 // First the parameter name
362 outfile << " " << '"' << std::get<0>(outTuple) << '"' << "\r\n";
363
364 const std::string &outputType = std::get<1>(outTuple);
365 outfile << " " << outputType << "\r\n";
366
367 // Then the data values - the formatting depends on data type
368 outfile << " ";
369 if (outputType == m_stringType) {
370 outfile << '"' << std::get<2>(outTuple) << '"';
371 } else {
372 outfile << std::get<2>(outTuple);
373 }
374 outfile << "\r\n";
375 }
376}
377
378} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
int64_t nSpectra
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
@ Save
to specify a file to write to, the file may or may not exist
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A property class for workspaces.
void determineEnginXBankId()
Determines the ENGIN-X bank from the detectors IDs present.
API::MatrixWorkspace_const_sptr m_inputWS
Workspace to save.
std::tuple< std::string, std::string, std::string > OutputBufferEntry
Typedef of a tuple containing the name, type and value as strings.
void getSampleLogs()
Parses and stores appropriate output logs into the output buffer.
std::vector< OutputBufferEntry > m_outputVector
Output buffer which holds the tuples to be written.
const std::string m_stringType
Output type - String.
const std::string m_intType
Output type - Integer.
void applyEnginxFormat()
Adds ENGINX related data which is required for OpenGenie.
void storeEmptyFields()
Stores fields that aren't found in the WS but required by OpenGenie.
const std::string m_floatType
Output type - Float.
void inputValidation()
Validates that workspace is focused and not empty.
void openFileStream(std::ofstream &stream)
Attempts to open the user specified file path as an output stream.
void writeDataToFile(std::ofstream &outfile)
sorts and writes out the data portion of the file
void calculateXYZDelta(const std::string &unit, const Kernel::Property *values)
Calculate delta x/y/z from the log files for ENGINX.
void storeWorkspaceInformation()
Stores parameters from the workspace which are required for OpenGenie.
void addToOutputBuffer(const std::string &outName, const std::string &outType, const std::string &outVal)
void convertWorkspaceData(const T &histoData, const char &axis)
Converts XYE data to a tuple containing the OPENGENIE string and store it into the output buffer.
Records the filename and the description of failure.
Definition Exception.h:98
A non-templated interface to a TimeSeriesProperty.
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Base class for properties.
Definition Property.h:94
A specialised Property class for holding a series of time-value pairs.
TYPE maxValue() const
Returns the maximum value found in the series.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53