Mantid
Loading...
Searching...
No Matches
SaveSPE.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"
15#include "MantidHistogramData/HistogramE.h"
16#include "MantidHistogramData/HistogramY.h"
18#include "MantidKernel/Unit.h"
19
20#include "Poco/File.h"
21#include <cmath>
22
23#include <cmath>
24#include <cstdio>
25#include <stdexcept>
26
27namespace Mantid::DataHandling {
28
29// Register the algorithm into the AlgorithmFactory
30DECLARE_ALGORITHM(SaveSPE)
31
32
42// use macro on platforms without variadic templates.
43#if defined(__INTEL_COMPILER) || (defined(_MSC_VER) && _MSC_VER < 1800)
44
45#define FPRINTF_WITH_EXCEPTION(stream, format, ...) \
46 if (fprintf(stream, format, ##__VA_ARGS__) <= 0) { \
47 throw std::runtime_error("Error writing to file. Check folder permissions and disk space."); \
48 }
49
50#else
51namespace {
52
53template <typename... vargs> void FPRINTF_WITH_EXCEPTION(FILE *stream, const char *format, vargs... args) {
54 if (fprintf(stream, format, args...) <= 0) {
55 throw std::runtime_error("Error writing to file. Check folder permissions and disk space.");
56 }
57}
58
59// special case needed for case with only two arguments.
60void FPRINTF_WITH_EXCEPTION(FILE *stream, const char *format) { FPRINTF_WITH_EXCEPTION(stream, format, ""); }
61} // namespace
62#endif
63
64using namespace Kernel;
65using namespace API;
66
68const char NUM_FORM[] = "%-10.4G";
69const char NUMS_FORM[] = "%-10.4G%-10.4G%-10.4G%-10.4G%-10.4G%-10.4G%-10.4G%-10.4G\n";
70static const char Y_HEADER[] = "### S(Phi,w)\n";
71static const char E_HEADER[] = "### Errors\n";
73
76static const unsigned int NUM_PER_LINE = 8;
77
78const double SaveSPE::MASK_FLAG = -1e30;
79const double SaveSPE::MASK_ERROR = 0.0;
80
81SaveSPE::SaveSPE() : API::Algorithm(), m_remainder(-1), m_nBins(0) {}
82
83//---------------------------------------------------
84// Private member functions
85//---------------------------------------------------
90 // Data must be in Energy Transfer and common bins
91 auto wsValidator = std::make_shared<Kernel::CompositeValidator>();
92 wsValidator->add<API::CommonBinsValidator>();
93 wsValidator->add<API::HistogramValidator>();
94 declareProperty(std::make_unique<API::WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, wsValidator),
95 "The input workspace, which must be in Energy Transfer");
96 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Save, ".spe"),
97 "The filename to use for the saved data");
98}
99
104 using namespace Mantid::API;
105 // Retrieve the input workspace
106 const MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
107
108 // Retrieve the filename from the properties
109 const std::string filename = getProperty("Filename");
110
111 FILE *outSPEFile = fopen(filename.c_str(), "w");
112 if (!outSPEFile) {
113 throw Kernel::Exception::FileError("Failed to open file:", filename);
114 }
115 try {
116 // write to the file being ready to catch it if something happens during
117 // writing
118 writeSPEFile(outSPEFile, inputWS);
119 fclose(outSPEFile);
120 } catch (std::exception &) {
121 fclose(outSPEFile);
122 Poco::File(filename).remove();
123 // throw the exception again so the base class can deal with it too in it's
124 // own way
125 throw;
126 }
127}
128
133void SaveSPE::writeSPEFile(FILE *outSPEFile, const API::MatrixWorkspace_const_sptr &inputWS) {
134 const size_t nHist = inputWS->getNumberHistograms();
135 m_nBins = inputWS->blocksize();
136 // Number of Workspaces and Number of Energy Bins
137 FPRINTF_WITH_EXCEPTION(outSPEFile, "%8u%8u\n", static_cast<unsigned int>(nHist), static_cast<unsigned int>(m_nBins));
138 // Write the angle grid (dummy if no 'vertical' axis)
139 size_t phiPoints(0);
140 if (inputWS->axes() > 1 && inputWS->getAxis(1)->isNumeric()) {
141 const Axis &axis = *inputWS->getAxis(1);
142 const std::string commentLine = "### " + axis.unit()->caption() + " Grid\n";
143 FPRINTF_WITH_EXCEPTION(outSPEFile, "%s", commentLine.c_str());
144 const size_t axisLength = axis.length();
145 phiPoints = (axisLength == nHist) ? axisLength + 1 : axisLength;
146 for (size_t i = 0; i < phiPoints; i++) {
147 const double value = (i < axisLength) ? axis(i) : axis(axisLength - 1) + 1;
148 FPRINTF_WITH_EXCEPTION(outSPEFile, NUM_FORM, value);
149 if ((i + 1) % 8 == 0) {
150 FPRINTF_WITH_EXCEPTION(outSPEFile, "\n");
151 }
152 }
153 } else {
154 FPRINTF_WITH_EXCEPTION(outSPEFile, "### Phi Grid\n");
155 phiPoints = nHist + 1; // Pretend this is binned
156 for (size_t i = 0; i < phiPoints; i++) {
157 const double value = static_cast<int>(i) + 0.5;
158 FPRINTF_WITH_EXCEPTION(outSPEFile, NUM_FORM, value);
159 if ((i + 1) % 8 == 0) {
160 FPRINTF_WITH_EXCEPTION(outSPEFile, "\n");
161 }
162 }
163 }
164
165 // If the number of points written isn't a factor of 8 then we need to add an
166 // extra newline
167 if (phiPoints % 8 != 0) {
168 FPRINTF_WITH_EXCEPTION(outSPEFile, "\n");
169 }
170
171 // Get the Energy Axis (X) of the first spectra (they are all the same -
172 // checked above)
173 const auto &X = inputWS->x(0);
174 // Write the energy grid
175 FPRINTF_WITH_EXCEPTION(outSPEFile, "### Energy Grid\n");
176 const size_t energyPoints = m_nBins + 1; // Validator enforces binned data
177 size_t i = NUM_PER_LINE - 1;
178 for (; i < energyPoints; i += NUM_PER_LINE) { // output a whole line of numbers at once
179 FPRINTF_WITH_EXCEPTION(outSPEFile, NUMS_FORM, X[i - 7], X[i - 6], X[i - 5], X[i - 4], X[i - 3], X[i - 2], X[i - 1],
180 X[i]);
181 }
182 // if the last line is not a full line enter them individually
183 if (energyPoints % NUM_PER_LINE != 0) { // the condition above means that the
184 // last line has less than the maximum
185 // number of digits
186 for (i -= 7; i < energyPoints; ++i) {
187 FPRINTF_WITH_EXCEPTION(outSPEFile, NUM_FORM, X[i]);
188 }
189 FPRINTF_WITH_EXCEPTION(outSPEFile, "\n");
190 }
191
192 writeHists(inputWS, outSPEFile);
193}
194
199void SaveSPE::writeHists(const API::MatrixWorkspace_const_sptr &WS, FILE *const outFile) {
200 // We write out values NUM_PER_LINE at a time, so will need to do extra work
201 // if nBins isn't a factor of NUM_PER_LINE
203 bool isNumericAxis = WS->getAxis(1)->isNumeric();
204 const size_t nHist = WS->getNumberHistograms();
205 // Create a progress reporting object
206 Progress progress(this, 0.0, 1.0, 100);
207 const auto progStep = static_cast<int>(ceil(static_cast<int>(nHist) / 100.0));
208
209 // there are very often spectra that are missing detectors, as this can be a
210 // lot of detectors log it once at the end
211 std::vector<int> spuriousSpectra;
212 // used only for debugging
213 int nMasked = 0;
214 const auto &spectrumInfo = WS->spectrumInfo();
215 // Loop over the spectra, writing out Y and then E values for each
216 for (int i = 0; i < static_cast<int>(nHist); i++) {
217 if (spectrumInfo.hasDetectors(i)) {
218 if (isNumericAxis || !spectrumInfo.isMasked(i)) {
219 // there's no masking, write the data
220 writeHist(WS, outFile, i);
221 } else { // all the detectors are masked, write the masking value from the
222 // SPE spec
223 // http://www.mantidproject.org/images/3/3d/Spe_file_format.pdf
224 writeMaskFlags(outFile);
225 nMasked++;
226 }
227 } else { // if the detector isn't in the instrument definition file, write
228 // mask values and prepare to log what happened
229 spuriousSpectra.emplace_back(i);
230 writeMaskFlags(outFile);
231 }
232 // make regular progress reports and check for canceling the algorithm
233 if (i % progStep == 0) {
234 progress.report();
235 }
236 }
237 logMissingMasked(spuriousSpectra, nHist - nMasked, nMasked);
238}
250void SaveSPE::check_and_copy_spectra(const HistogramData::HistogramY &inSignal, const HistogramData::HistogramE &inErr,
251 std::vector<double> &Signal, std::vector<double> &Error) const {
252 if (Signal.size() != inSignal.size()) {
253 Signal.resize(inSignal.size());
254 Error.resize(inSignal.size());
255 }
256 for (size_t i = 0; i < inSignal.size(); i++) {
257 if (!std::isfinite(inSignal[i])) {
258 Signal[i] = SaveSPE::MASK_FLAG;
259 Error[i] = SaveSPE::MASK_ERROR;
260 } else {
261 Signal[i] = inSignal[i];
262 Error[i] = inErr[i];
263 }
264 }
265}
266
272void SaveSPE::writeHist(const API::MatrixWorkspace_const_sptr &WS, FILE *const outFile, const int wsIn) const {
273 check_and_copy_spectra(WS->y(wsIn), WS->e(wsIn), m_tSignal, m_tError);
274 FPRINTF_WITH_EXCEPTION(outFile, "%s", Y_HEADER);
275 writeBins(m_tSignal, outFile);
276
277 FPRINTF_WITH_EXCEPTION(outFile, "%s", E_HEADER);
278 writeBins(m_tError, outFile);
279}
283void SaveSPE::writeMaskFlags(FILE *const outFile) const {
284 FPRINTF_WITH_EXCEPTION(outFile, "%s", Y_HEADER);
285 writeValue(MASK_FLAG, outFile);
286
287 FPRINTF_WITH_EXCEPTION(outFile, "%s", E_HEADER);
288 writeValue(MASK_ERROR, outFile);
289}
295void SaveSPE::writeBins(const std::vector<double> &Vs, FILE *const outFile) const {
296
297 for (size_t j = NUM_PER_LINE - 1; j < m_nBins; j += NUM_PER_LINE) { // output a whole line of numbers at once
298 FPRINTF_WITH_EXCEPTION(outFile, NUMS_FORM, Vs[j - 7], Vs[j - 6], Vs[j - 5], Vs[j - 4], Vs[j - 3], Vs[j - 2],
299 Vs[j - 1], Vs[j]);
300 }
301 if (m_remainder) {
302 for (size_t l = m_nBins - m_remainder; l < m_nBins; ++l) {
303 FPRINTF_WITH_EXCEPTION(outFile, NUM_FORM, Vs[l]);
304 }
305 FPRINTF_WITH_EXCEPTION(outFile, "\n");
306 }
307}
312void SaveSPE::writeValue(const double value, FILE *const outFile) const {
313 for (size_t j = NUM_PER_LINE - 1; j < m_nBins; j += NUM_PER_LINE) { // output a whole line of numbers at once
314 FPRINTF_WITH_EXCEPTION(outFile, NUMS_FORM, value, value, value, value, value, value, value, value);
315 }
316 if (m_remainder) {
317 for (size_t l = m_nBins - m_remainder; l < m_nBins; ++l) {
318 FPRINTF_WITH_EXCEPTION(outFile, NUM_FORM, value);
319 }
320 FPRINTF_WITH_EXCEPTION(outFile, "\n");
321 }
322}
330void SaveSPE::logMissingMasked(const std::vector<int> &inds, const size_t nonMasked, const int masked) const {
331 if (inds.cbegin() != inds.cend()) {
332 g_log.information() << "Found " << inds.size()
333 << " spectra without associated detectors, probably "
334 "the detectors are not present in the instrument "
335 "definition, this is not unusual. The Y values for "
336 "those spectra have been set to zero.\n";
337 }
338 g_log.debug() << "Wrote " << nonMasked << " histograms and " << masked
339 << " masked histograms to the output SPE file\n";
340}
341} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double value
The value of the point.
Definition: FitMW.cpp:51
Base class from which all concrete algorithm classes should be derived.
Definition: Algorithm.h:85
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
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
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
Class to represent the axis of a workspace.
Definition: Axis.h:30
virtual std::size_t length() const =0
Get the length of the axis.
const std::shared_ptr< Kernel::Unit > & unit() const
The unit for this axis.
Definition: Axis.cpp:28
A validator which provides a TENTATIVE check that a workspace contains common bins in each spectrum.
@ Save
to specify a file to write to, the file may or may not exist
Definition: FileProperty.h:49
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
A property class for workspaces.
void writeSPEFile(FILE *outSPEFile, const API::MatrixWorkspace_const_sptr &inputWS)
Write the data to the SPE file.
Definition: SaveSPE.cpp:133
void init() override
Initialization code.
Definition: SaveSPE.cpp:89
std::vector< double > m_tSignal
Definition: SaveSPE.h:100
void writeBins(const std::vector< double > &Vs, FILE *const outFile) const
Write the values in the array to the file in the correct format.
Definition: SaveSPE.cpp:295
static const double MASK_ERROR
the error value (=0.0) for spectra whose detectors are all masked, from the SPE specification http://...
Definition: SaveSPE.h:97
void writeMaskFlags(FILE *const outFile) const
Write the mask flags for in a histogram entry.
Definition: SaveSPE.cpp:283
void logMissingMasked(const std::vector< int > &inds, const size_t nonMasked, const int masked) const
Write a summary information about what the algorithm had managed to save to the file.
Definition: SaveSPE.cpp:330
int m_remainder
the SPE files have a constant number of numbers written on each line, but depending on the number of ...
Definition: SaveSPE.h:89
void writeValue(const double value, FILE *const outFile) const
Write the value the file a number of times given by m_nbins.
Definition: SaveSPE.cpp:312
static const double MASK_FLAG
the mask flag (=-1e30) from the SPE specification http://www.mantidproject.org/images/3/3d/Spe_file_f...
Definition: SaveSPE.h:70
void check_and_copy_spectra(const HistogramData::HistogramY &inSignal, const HistogramData::HistogramE &inErr, std::vector< double > &Signal, std::vector< double > &Error) const
method verifies if a spectra contains any NaN or Inf values and replaces these values with SPE-specif...
Definition: SaveSPE.cpp:250
void writeHist(const API::MatrixWorkspace_const_sptr &WS, FILE *const outFile, const int wsIn) const
Write the bin values and errors in a single histogram spectra to the file.
Definition: SaveSPE.cpp:272
void exec() override
Execution code.
Definition: SaveSPE.cpp:103
size_t m_nBins
the number of bins in each histogram, as the histogram must have common bins this shouldn't change
Definition: SaveSPE.h:92
void writeHists(const API::MatrixWorkspace_const_sptr &WS, FILE *const outFile)
Write the bin values and errors for all histograms to the file.
Definition: SaveSPE.cpp:199
std::vector< double > m_tError
Definition: SaveSPE.h:102
Records the filename and the description of failure.
Definition: Exception.h:98
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
static const unsigned int NUM_PER_LINE
set to the number of numbers on each line (the length of lines is hard-coded in other parts of the co...
Definition: SaveSPE.cpp:76
@ Input
An input workspace.
Definition: Property.h:53