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 <Poco/File.h>
20#include <Poco/Path.h>
21
22#include <fstream>
23#include <vector>
24
25namespace Mantid::DataHandling {
26
27using namespace Kernel;
28using namespace Mantid::API;
29
30// Register the algorithm into the AlgorithmFactory
32
33
37void SaveOpenGenieAscii::init() {
38 declareProperty(
39 std::make_unique<API::WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Kernel::Direction::Input),
40 "The name of the workspace containing the data you wish to save");
41
42 // Declare required parameters, filename with ext {.his} and input
43 // workspace
44 const std::vector<std::string> exts{".his", ".txt", ""};
45 declareProperty(std::make_unique<API::FileProperty>("Filename", "", API::FileProperty::Save, exts),
46 "The filename to use for the saved data");
47 declareProperty("IncludeHeader", true, "Whether to include the header lines (default: true)");
48 std::vector<std::string> header{"ENGIN-X Format"};
49 declareProperty("OpenGenieFormat", "ENGIN-X Format", std::make_shared<Kernel::StringListValidator>(header),
50 "The format required to successfully load the file to "
51 "OpenGenie: ENGIN-X Format (default)");
52}
53
58 // Retrieve the input workspace
59 m_inputWS = getProperty("InputWorkspace");
60
62
63 // Its better to reserve over and the number of logs gives us a good
64 // estimate as some logs are not included whilst some other params are.
65 m_outputVector.reserve(m_inputWS->run().getLogData().size());
66
67 // Whilst this doesn't weigh in the processing
68 // required it breaks down the algorithm nicely for the moment
69 const int numOfSteps = 6;
70 Progress progressBar(this, 0.0, 1.0, numOfSteps);
71
72 const std::string formatType = getProperty("OpenGenieFormat");
73 // If we had a basic format this is where the specialization would go
74 if (formatType == "ENGIN-X Format") {
75 progressBar.report("Generating ENGINX header");
77 }
78
79 // Store empty but required field
80 progressBar.report("Storing empty fields");
82
83 // store common workspace properties
84 progressBar.report("Processing workspace information");
86
87 // store x, y, e to vector
88 progressBar.report("Processing workspace data");
92
93 progressBar.report("Processing log data");
95
96 std::ofstream outStream;
97 openFileStream(outStream);
98
99 progressBar.report("Writing to file");
100 writeDataToFile(outStream);
101 outStream.close();
102
103 // Indicate we have finished
104 progressBar.report();
105}
106
111 // Bank number
113
114 // Spectrum numbers
115 addToOutputBuffer("spec_no", m_stringType, "1");
116
117 // Par file that was used in the calibration
118 // This can be set to none at the moment as it does not affect the analysis
119 addToOutputBuffer("parameter_file", m_stringType, "None.par");
120 addToOutputBuffer("user_name", m_stringType, "NotSet");
121
122 // xunit & xlabel put in OpenGenie format
123 const std::string xunitsVal = "Time-of-Flight (\\\\gms)";
124 addToOutputBuffer("xunits", m_stringType, xunitsVal);
125 addToOutputBuffer("xlabel", m_stringType, xunitsVal);
126
127 // yunit & ylabel put in OpenGenie format
128 const std::string yunitsVal = "Neutron counts / \\\\gms";
129 addToOutputBuffer("yunits", m_stringType, yunitsVal);
130 addToOutputBuffer("ylabel", m_stringType, yunitsVal);
131}
132
141void SaveOpenGenieAscii::calculateXYZDelta(const std::string &unit, const Kernel::Property *values) {
142 // Cast to TimeSeries so we can use the min/max value methods
143 const auto positionValues = static_cast<const TimeSeriesProperty<double> *>(values);
144
145 const double deltaValue = positionValues->maxValue() - positionValues->minValue();
146
147 addToOutputBuffer('d' + unit, m_floatType, std::to_string(deltaValue));
148}
149
158template <typename T> void SaveOpenGenieAscii::convertWorkspaceData(const T &histoData, const char &axis) {
159 // Padding to apply after 10 data values
160 const std::string newLineStr = "\r\n ";
161 // Bank number - force to 1 at the moment
162 const std::string outputType = "GXRealarray\r\n 1";
163
164 // First 4 spaces for correct padding
165 std::string outputString(" ");
166 int valueCount(0);
167
168 // Working on primitive type so don't take ref
169 for (const auto val : histoData) {
170 if (valueCount % 10 == 0) {
171 outputString += newLineStr;
172 }
173 valueCount++;
174 outputString += std::to_string(val) + ' ';
175 }
176
177 // Have to put the number of values (second member of pair)
178 // followed by a space then a new line then the data into a string
179 auto outDataString = std::to_string(valueCount) + " \r\n" + std::move(outputString);
180 addToOutputBuffer(std::string(1, axis), outputType, outDataString);
181}
182
188 const auto &detectorIds = m_inputWS->getSpectrum(0).getDetectorIDs();
189 const std::string firstDetectorId = std::to_string(*detectorIds.cbegin());
190
191 if (firstDetectorId.length() != 6) {
192 g_log.warning("Could not determine bank ID as detector ID in ENGIN-X "
193 "workspace did not match expected format. You will need"
194 "manually specify the bank in OpenGenie");
195 return;
196 }
197
198 // ENGIN-X format is 1X001, 1X002, 1X003...etc. for detectors
199 // where X = 0 is bank 1. X = 1 is bank 2.
200 const int bankNumber = firstDetectorId[1] == '0' ? 1 : 2;
201 addToOutputBuffer("bank", m_intType, std::to_string(bankNumber));
202}
203
211 // Maps Mantid log names -> Genie save file name / type
212 const std::unordered_map<std::string, std::pair<std::string, std::string>> mantidGenieLogMapping = {
213 {"x", std::make_pair("x_pos", m_floatType)},
214 {"y", std::make_pair("y_pos", m_floatType)},
215 {"z", std::make_pair("z_pos", m_floatType)},
216 {"gd_prtn_chrg", std::make_pair("microamps", m_floatType)}};
217
218 const std::vector<Property *> &logData = m_inputWS->run().getLogData();
219
220 for (const auto &logEntry : logData) {
221 const std::string &logName = logEntry->name();
222
223 // Check this log value is known to us
224 const auto foundMapping = mantidGenieLogMapping.find(logName);
225 if (foundMapping == mantidGenieLogMapping.cend()) {
226 continue;
227 }
228
229 // Second member of map is the OpenGenie Name / Type as a pair
230 // First member of pair is name, second member of pair is the type
231 const std::string outName = foundMapping->second.first;
232 const std::string outType = foundMapping->second.second;
233 std::string outValue;
234
235 // Calculate dx/dy/dz
236 if (outName == "x_pos" || outName == "y_pos" || outName == "z_pos") {
237 calculateXYZDelta(foundMapping->first, logEntry);
238 } else if (outName == "microamps") {
239 // From reverse engineering the scripts the effective time is
240 // the microamps * 50 - what 50 represents is not documented
241 const std::string effectiveTime = std::to_string(std::stod(logEntry->value()) * 50.);
242 addToOutputBuffer("effective_time", m_floatType, effectiveTime);
243 }
244
245 if (auto *timeSeries = dynamic_cast<ITimeSeriesProperty *>(logEntry)) {
246 outValue = std::to_string(timeSeries->timeAverageValue());
247 } else {
248 outValue = logEntry->value();
249 }
250
251 addToOutputBuffer(outName, outType, outValue);
252 }
253}
254
261 const size_t nSpectra = m_inputWS->getNumberHistograms();
262
263 if (m_inputWS->blocksize() == 0 || nSpectra == 0)
264 throw std::runtime_error("Trying to save an empty workspace");
265 else if (nSpectra > 1) {
266 throw std::runtime_error("Workspace has multiple spectra. This algorithm "
267 "can only save focused workspaces.");
268 } else if (!m_inputWS->isHistogramData()) {
269 throw std::runtime_error("This algorithm cannot save workspaces with event "
270 "data, please convert to histogram data first.");
271 }
272}
273
281void SaveOpenGenieAscii::openFileStream(std::ofstream &stream) {
282 // Retrieve the filename from the properties
283 const std::string filename = getProperty("Filename");
284 // Open file as binary so it doesn't convert CRLF to LF on UNIX
285 stream.open(filename, std::ofstream::binary | std::ofstream::out);
286 if (!stream) {
287 g_log.error("Unable to create file: " + filename);
288 throw Exception::FileError("Unable to create file: ", filename);
289 }
290}
291
298 const std::string floatVal = "999.000";
299 addToOutputBuffer("eurotherm", m_floatType, floatVal);
300 addToOutputBuffer("eurotherm_error", m_floatType, floatVal);
301
302 addToOutputBuffer("load", m_floatType, floatVal);
303 addToOutputBuffer("load_error", m_floatType, floatVal);
304 addToOutputBuffer("macro_strain", m_floatType, floatVal);
305 addToOutputBuffer("macro_strain_error", m_floatType, floatVal);
306 addToOutputBuffer("theta_pos", m_floatType, floatVal);
307 addToOutputBuffer("theta_pos_error", m_floatType, floatVal);
308
309 addToOutputBuffer("pos", m_floatType, floatVal);
310 addToOutputBuffer("pos_error", m_floatType, floatVal);
311 addToOutputBuffer("x_pos_error", m_floatType, floatVal);
312 addToOutputBuffer("y_pos_error", m_floatType, floatVal);
313 addToOutputBuffer("z_pos_error", m_floatType, floatVal);
314}
315
321 // Run Number
322 addToOutputBuffer("run_no", m_stringType, std::to_string(m_inputWS->getRunNumber()));
323 // Workspace title
324 addToOutputBuffer("title", m_stringType, m_inputWS->getTitle());
325 // Instrument name
326 addToOutputBuffer("inst_name", m_stringType, m_inputWS->getInstrument()->getName());
327 // Number of bins
329 // L1 (Source to sample distance)
330 const auto &specInfo = m_inputWS->spectrumInfo();
331 addToOutputBuffer("l1", m_floatType, std::to_string(specInfo.l1()));
332 // L2 (Sample to spectrum distance)
333 addToOutputBuffer("l2", m_floatType, std::to_string(specInfo.l2(0)));
334 // Unsigned scattering angle 2theta
335 const double two_theta = specInfo.twoTheta(0) * 180 / M_PI; // Convert to deg
336 addToOutputBuffer("twotheta", m_floatType, std::to_string(two_theta));
337}
338
345void SaveOpenGenieAscii::writeDataToFile(std::ofstream &outfile) {
346 // Write header
347 if (getProperty("IncludeHeader")) {
348 outfile << "# Open Genie ASCII File #\r\n"
349 << "# label \r\n"
350 << "GXWorkspace\r\n"
351 << m_outputVector.size() << "\r\n";
352 }
353
354 // Sort by parameter name
355 std::sort(m_outputVector.begin(), m_outputVector.end(),
356 [](const OutputBufferEntry &t1, const OutputBufferEntry &t2) { return std::get<0>(t1) < std::get<0>(t2); });
357
358 for (const auto &outTuple : m_outputVector) {
359 // Format is 2 spaces followed by parameter name, newline
360 // 4 spaces then the type name, newline
361 // 4 spaces and value(s), newline
362
363 // The parameter name must be surrounded with quotes
364 // If the type is a string the value must be wrapped in quotes
365
366 // First the parameter name
367 outfile << " " << '"' << std::get<0>(outTuple) << '"' << "\r\n";
368
369 const std::string &outputType = std::get<1>(outTuple);
370 outfile << " " << outputType << "\r\n";
371
372 // Then the data values - the formatting depends on data type
373 outfile << " ";
374 if (outputType == m_stringType) {
375 outfile << '"' << std::get<2>(outTuple) << '"';
376 } else {
377 outfile << std::get<2>(outTuple);
378 }
379 outfile << "\r\n";
380 }
381}
382
383} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
int nSpectra
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
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:77
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
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