Mantid
Loading...
Searching...
No Matches
SaveGSS.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
23
24#include <Poco/File.h>
25#include <Poco/Path.h>
26
27#include <fstream>
28
29namespace Mantid::DataHandling {
30
31using namespace Mantid::API;
32using namespace Mantid::HistogramData;
33
34// Register the algorithm into the AlgorithmFactory
36
37namespace { // Anonymous namespace
38const std::string RALF("RALF");
39const std::string SLOG("SLOG");
40const std::string FXYE("FXYE");
41const std::string ALT("ALT");
42
44const double m_TOLERANCE = 1.e-10;
45
46void assertNumFilesAndSpectraIsValid(size_t numOutFiles, size_t numOutSpectra) {
47 // If either numOutFiles or numOutSpectra are not 1 we need to check
48 // that we are conforming to the expected storage layout.
49 if ((numOutFiles != 1) || (numOutSpectra != 1)) {
50 assert((numOutFiles > 1) != (numOutSpectra > 1));
51 }
52}
53
54bool doesFileExist(const std::string &filePath) {
55 auto file = Poco::File(filePath);
56 return file.exists();
57}
58
59double fixErrorValue(const double value) {
60 // Fix error if value is less than zero or infinity
61 // Negative errors cannot be read by GSAS
62 if (value <= 0. || !std::isfinite(value)) {
63 return 0.;
64 } else {
65 return value;
66 }
67}
68
69bool isEqual(const double left, const double right) {
70 if (left == right)
71 return true;
72 return (2. * std::fabs(left - right) <= std::fabs(m_TOLERANCE * (right + left)));
73}
74
75bool isConstantDelta(const HistogramData::BinEdges &xAxis) {
76 const double deltaX = (xAxis[1] - xAxis[0]);
77 for (std::size_t i = 1; i < xAxis.size(); ++i) {
78 if (!isEqual(xAxis[i] - xAxis[i - 1], deltaX)) {
79 return false;
80 }
81 }
82 return true;
83}
84
85std::unique_ptr<std::stringstream> makeStringStream() {
86 // This and all unique_ptrs wrapping streams is a workaround
87 // for GCC 4.x. The standard allowing streams to be moved was
88 // added after the 4 series was released. Allowing this would
89 // break the ABI (hence GCC 5 onwards doesn't have this fault)
90 // so there are lots of restrictions in place with stream.
91 // Instead we can work around this by using pointers to streams.
92 // Tl;dr - This is a workaround for GCC 4.x (RHEL7)
93 return std::make_unique<std::stringstream>();
94}
95
96//----------------------------------------------------------------------------------------------
106void writeBankHeader(std::stringstream &out, const std::string &bintype, const int banknum, const size_t datasize) {
107 std::ios::fmtflags fflags(out.flags());
108 out << "BANK " << std::fixed << std::setprecision(0)
109 << banknum // First bank should be 1 for GSAS; this can be changed
110 << std::fixed << " " << datasize << std::fixed << " " << datasize << std::fixed << " " << bintype;
111 out.flags(fflags);
112}
113} // End of anonymous namespace
114
115//----------------------------------------------------------------------------------------------
116// Initialise the algorithm
118 const std::vector<std::string> exts{".gsa", ".gss", ".gda", ".txt"};
120 std::make_unique<API::WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Kernel::Direction::Input),
121 "The input workspace");
122
123 declareProperty(std::make_unique<API::FileProperty>("Filename", "", API::FileProperty::Save, exts),
124 "The filename to use for the saved data");
125
126 declareProperty("SplitFiles", true,
127 "Whether to save each spectrum into a separate file ('true') "
128 "or not ('false'). Note that this is a string, not a boolean property.");
129
130 declareProperty("Append", true, "If true and Filename already exists, append, else overwrite ");
131
132 declareProperty("Bank", 1,
133 "The bank number to include in the file header for the first spectrum, "
134 "i.e., the starting bank number. "
135 "This will increment for each spectrum or group member.");
136
137 const std::vector<std::string> formats{RALF, SLOG};
138 declareProperty("Format", RALF, std::make_shared<Kernel::StringListValidator>(formats), "GSAS format to save as");
139
140 const std::vector<std::string> ralfDataFormats{FXYE, ALT};
141 declareProperty("DataFormat", FXYE, std::make_shared<Kernel::StringListValidator>(ralfDataFormats),
142 "Saves RALF data as either FXYE or alternative format");
143 setPropertySettings("DataFormat", std::make_unique<Kernel::VisibleWhenProperty>(
145
146 declareProperty("MultiplyByBinWidth", true, "Multiply the intensity (Y) by the bin width; default TRUE.");
147
148 declareProperty("ExtendedHeader", false, "Add information to the header about iparm file and normalization");
149
150 declareProperty("UseSpectrumNumberAsBankID", false,
151 "If true, then each bank's bank ID is equal to the spectrum number; "
152 "otherwise, the continuous bank IDs are applied. ");
153
154 declareProperty(std::make_unique<Kernel::ArrayProperty<std::string>>("UserSpecifiedGSASHeader"),
155 "Each line will be put to the header of the output GSAS file.");
156
157 declareProperty("OverwriteStandardHeader", true,
158 "If true, then the standard header will be replaced "
159 "by the user specified header. Otherwise, the user "
160 "specified header will be "
161 "inserted before the original header");
162
163 declareProperty(std::make_unique<Kernel::ArrayProperty<std::string>>("UserSpecifiedBankHeader"),
164 "Each string will be used to replaced the standard GSAS bank header."
165 "Number of strings in the give array must be same as number of banks."
166 "And the order is preserved.");
167
168 auto must_be_3 = std::make_shared<Kernel::ArrayLengthValidator<int>>(3);
169 auto precision_range = std::make_shared<Kernel::ArrayBoundedValidator<int>>(0, 10);
170
171 auto precision_validator = std::make_shared<Kernel::CompositeValidator>();
172 precision_validator->add(must_be_3);
173 precision_validator->add(precision_range);
174
175 std::vector<int> default_precision(3, 9);
176 declareProperty(std::make_unique<Kernel::ArrayProperty<int>>("SLOGXYEPrecision", std::move(default_precision),
177 std::move(precision_validator)),
178 "Enter 3 integers as the precisions of output X, Y and E for SLOG data "
179 "only."
180 "Default is (9, 9, 9) if it is left empty. Otherwise it is not "
181 "allowed.");
182}
183
184// Execute the algorithm
186 // Retrieve the input workspace and associated properties
187 m_inputWS = getProperty("InputWorkspace");
188 const size_t nHist = m_inputWS->getNumberHistograms();
189
190 // process user special headers
192
193 // Are we writing one file with n spectra or
194 // n files with 1 spectra each
195 const bool split = getProperty("SplitFiles");
196 const size_t numOfOutFiles{split ? nHist : 1};
197 const size_t numOutSpectra{split ? 1 : nHist};
198
199 // Initialise various properties we are going to need within this alg
201 generateOutFileNames(numOfOutFiles);
202 m_outputBuffer.resize(nHist);
203 std::generate(m_outputBuffer.begin(), m_outputBuffer.end(), []() { return makeStringStream(); });
204
205 // Progress is 2 * number of histograms. One for generating data
206 // one for writing out data
207 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, (nHist * 2));
208
209 // Now start executing main part of the code
210 generateGSASBuffer(numOfOutFiles, numOutSpectra);
211 writeBufferToFile(numOfOutFiles, numOutSpectra);
212}
213
214//----------------------------------------------------------------------------------------------
219
220 // user specified GSAS
221 m_user_specified_gsas_header = getProperty("UserSpecifiedGSASHeader");
222
223 if (m_user_specified_gsas_header.empty()) {
225 } else {
226 m_overwrite_std_gsas_header = getProperty("OverwriteStandardHeader");
227 }
228
229 // user specified bank header
230 m_user_specified_bank_headers = getProperty("UserSpecifiedBankHeader");
232
233 return;
234}
235
236//----------------------------------------------------------------------------------------------
244 const auto &spectrumInfo = m_inputWS->spectrumInfo();
245 const size_t numHist = m_inputWS->getNumberHistograms();
246
247 if (!isInstrumentValid()) {
248 g_log.warning("No valid instrument found with this workspace"
249 " Treating as NO-INSTRUMENT CASE");
250 return false;
251 }
252
253 bool allValid = true;
254
255 const auto numHistInt64 = static_cast<int64_t>(numHist);
256
258 for (int64_t histoIndex = 0; histoIndex < numHistInt64; histoIndex++) {
259 if (allValid) {
260 // Check this spectra has detectors
261 if (!spectrumInfo.hasDetectors(histoIndex)) {
262 allValid = false;
263 g_log.warning() << "There is no detector associated with spectrum " << histoIndex
264 << ". Workspace is treated as NO-INSTRUMENT case. \n";
265 }
266 }
267 }
268 return allValid;
269}
270
271//----------------------------------------------------------------------------------------------
283void SaveGSS::generateBankData(std::stringstream &outBuf, size_t specIndex, const std::string &outputFormat,
284 const std::vector<int> &slog_xye_precisions) const {
285 // Determine bank number into GSAS file
286 const bool useSpecAsBank = getProperty("UseSpectrumNumberAsBankID");
287 const bool multiplyByBinWidth = getProperty("MultiplyByBinWidth");
288 const int userStartingBankNumber = getProperty("Bank");
289 const std::string ralfDataFormat = getPropertyValue("DataFormat");
290
291 int bankid;
292 if (useSpecAsBank) {
293 bankid = static_cast<int>(m_inputWS->getSpectrum(specIndex).getSpectrumNo());
294 } else {
295 bankid = userStartingBankNumber + static_cast<int>(specIndex);
296 }
297
298 // Write data
299 const auto &histogram = m_inputWS->histogram(specIndex);
300 if (outputFormat == RALF) {
301 if (ralfDataFormat == FXYE) {
302 writeRALF_XYEdata(bankid, multiplyByBinWidth, outBuf, histogram);
303 } else if (ralfDataFormat == ALT) {
304 writeRALF_ALTdata(outBuf, bankid, histogram);
305 } else {
306 throw std::runtime_error("Unknown RALF data format" + ralfDataFormat);
307 }
308 } else if (outputFormat == SLOG) {
309 writeSLOGdata(specIndex, bankid, multiplyByBinWidth, outBuf, histogram, slog_xye_precisions);
310 } else {
311 throw std::runtime_error("Cannot write to the unknown " + outputFormat + "output format");
312 }
313}
314
323void SaveGSS::generateBankHeader(std::stringstream &out, const API::SpectrumInfo &spectrumInfo,
324 size_t specIndex) const {
325 // If we have all valid detectors get these properties else use 0
327 const auto l1 = spectrumInfo.l1();
328 const auto l2 = spectrumInfo.l2(specIndex);
329 const auto twoTheta = spectrumInfo.twoTheta(specIndex);
330 auto diffConstants = spectrumInfo.diffractometerConstants(specIndex);
331 out << "# Total flight path " << (l1 + l2) << "m, tth " << (twoTheta * 180. / M_PI) << "deg, DIFC "
332 << diffConstants[Kernel::UnitParams::difc] << "\n";
333 }
334 out << "# Data for spectrum :" << specIndex << "\n";
335}
336
344void SaveGSS::generateGSASBuffer(size_t numOutFiles, size_t numOutSpectra) {
345 // Generate the output buffer for each histogram (spectrum)
346 const auto &spectrumInfo = m_inputWS->spectrumInfo();
347 const bool append = getProperty("Append");
348
349 // Check if the caller has reserved space in our output buffer
350 assert(m_outputBuffer.size() > 0);
351
352 // Because of the storage layout we can either handle files > 0
353 // XOR spectra per file > 0. Compensate for the fact that is will be
354 // 1 thing per thing as far as users are concerned.
355 assertNumFilesAndSpectraIsValid(numOutFiles, numOutSpectra);
356
357 // If all detectors are not valid we use the no instrument case and
358 // set l1 to 0
359 const double l1{m_allDetectorsValid ? spectrumInfo.l1() : 0};
360
361 const auto numOutFilesInt64 = static_cast<int64_t>(numOutFiles);
362
363 const std::string outputFormat = getPropertyValue("Format");
364
365 std::vector<int> slog_xye_precisions = getProperty("SLOGXYEPrecision");
366
367 // Create the various output files we will need in a loop
369 for (int64_t fileIndex = 0; fileIndex < numOutFilesInt64; fileIndex++) {
370 // Add header to new files (e.g. doesn't exist or overwriting)
371 if (!doesFileExist(m_outFileNames[fileIndex]) || !append) {
373 }
374
375 // Then add each spectra to buffer
376 for (size_t specIndex = 0; specIndex < numOutSpectra; specIndex++) {
377 // Determine whether to skip the spectrum due to being masked
378 if (m_allDetectorsValid && spectrumInfo.isMasked(specIndex)) {
379 continue;
380 }
381 // Find the index - this is because we are guaranteed at least
382 // one of these is 0 from the assertion above and the fact
383 // split files is a boolean operator on the input side
384 const int64_t index = specIndex + fileIndex;
385 // Add bank header and details to buffer
387 // Add data to buffer
388 generateBankData(*m_outputBuffer[index], index, outputFormat, slog_xye_precisions);
389 m_progress->report();
390 }
391 }
392}
393
403void SaveGSS::generateInstrumentHeader(std::stringstream &out, double l1) const {
404
405 // write user header first
406 if (m_user_specified_gsas_header.size() > 0) {
407 for (const auto &iter : m_user_specified_gsas_header) {
408 out << iter << "\n";
409 }
410 }
411
412 // quit method if user plan to use his own header completely
414 return;
415 }
416
417 // write standard header
418 const Run &runinfo = m_inputWS->run();
419 const std::string format = getPropertyValue("Format");
420
421 // Run number
422 if (format == SLOG) {
423 out << "Sample Run: ";
424 getLogValue(out, runinfo, "run_number");
425 out << " Vanadium Run: ";
426 getLogValue(out, runinfo, "van_number");
427 out << " Wavelength: ";
428 getLogValue(out, runinfo, "LambdaRequest");
429 out << "\n";
430 }
431
432 if (this->getProperty("ExtendedHeader")) {
433 // the instrument parameter file
434 if (runinfo.hasProperty("iparm_file")) {
435 Kernel::Property *prop = runinfo.getProperty("iparm_file");
436 if (prop != nullptr && (!prop->value().empty())) {
437 out << std::setw(80) << std::left;
438 out << "#Instrument parameter file: " << prop->value() << "\n";
439 }
440 }
441
442 // write out the GSAS monitor counts
443 out << "Monitor: ";
444 if (runinfo.hasProperty("gsas_monitor")) {
445 getLogValue(out, runinfo, "gsas_monitor");
446 } else {
447 getLogValue(out, runinfo, "gd_prtn_chrg", "1");
448 }
449 out << "\n";
450 }
451
452 if (format == SLOG) {
453 out << "# "; // make the next line a comment
454 }
455 out << m_inputWS->getTitle() << "\n";
456 out << "# " << m_inputWS->getNumberHistograms() << " Histograms\n";
457 out << "# File generated by Mantid:\n";
458 out << "# Instrument: " << m_inputWS->getInstrument()->getName() << "\n";
459 out << "# From workspace named : " << m_inputWS->getName() << "\n";
460 if (getProperty("MultiplyByBinWidth"))
461 out << "# with Y multiplied by the bin widths.\n";
462 out << "# Primary flight path " << l1 << "m \n";
463 if (format == SLOG) {
464 out << "# Sample Temperature: ";
465 getLogValue(out, runinfo, "SampleTemp");
466 out << " Freq: ";
467 getLogValue(out, runinfo, "SpeedRequest1");
468 out << " Guide: ";
469 getLogValue(out, runinfo, "guide");
470 out << "\n";
471
472 // print whether it is normalized by monitor or proton charge
473 bool norm_by_current = false;
474 bool norm_by_monitor = false;
475 const Mantid::API::AlgorithmHistories &algohist = m_inputWS->getHistory().getAlgorithmHistories();
476 for (const auto &algo : algohist) {
477 if (algo->name() == "NormaliseByCurrent")
478 norm_by_current = true;
479 if (algo->name() == "NormaliseToMonitor")
480 norm_by_monitor = true;
481 }
482 out << "#";
483 if (norm_by_current)
484 out << " Normalised to pCharge";
485 if (norm_by_monitor)
486 out << " Normalised to monitor";
487 out << "\n";
488 }
489}
490
500void SaveGSS::generateOutFileNames(size_t numberOfOutFiles) {
501 const std::string outputFileName = getProperty("Filename");
502 assert(numberOfOutFiles > 0);
503
504 if (numberOfOutFiles == 1) {
505 // Only add one name and don't generate split filenames
506 // when we are not in split mode
507 m_outFileNames.emplace_back(outputFileName);
508 return;
509 }
510
511 m_outFileNames.resize(numberOfOutFiles);
512
513 Poco::Path path(outputFileName);
514 // Filename minus extension
515 const std::string basename = path.getBaseName();
516 const std::string ext = path.getExtension();
517
518 // get file name and check with warning
519 const bool append = getProperty("Append");
520 for (size_t i = 0; i < numberOfOutFiles; i++) {
521 // Construct output name of the form 'base name-i.ext'
522 std::string newFileName = basename;
523 ((newFileName += '-') += std::to_string(i) += ".") += ext;
524 // Remove filename from path
525 path.makeParent();
526 path.append(newFileName);
527 std::string filename = path.toString();
528 m_outFileNames[i].assign(filename);
529 // check and make some warning
530 if (!append && doesFileExist(filename)) {
531 g_log.warning("Target GSAS file " + filename + " exists and will be overwritten.\n");
532 } else if (append && !doesFileExist(filename)) {
533 g_log.warning("Target GSAS file " + filename + " does not exist but algorithm was set to append.\n");
534 }
535 }
536
537 return;
538}
539
552void SaveGSS::getLogValue(std::stringstream &out, const API::Run &runInfo, const std::string &name,
553 const std::string &failsafeValue) const {
554 // Return without property exists
555 if (!runInfo.hasProperty(name)) {
556 out << failsafeValue;
557 return;
558 }
559 // Get handle of property
560 auto *prop = runInfo.getProperty(name);
561
562 // Return without a valid pointer to property
563 if (!prop) {
564 out << failsafeValue;
565 return;
566 }
567
568 // Get value
569 auto *log = dynamic_cast<Kernel::TimeSeriesProperty<double> *>(prop);
570 if (log) {
571 // Time series to get mean
572 out << log->getStatistics().mean;
573 } else {
574 // No time series - just get the value
575 out << prop->value();
576 }
577
578 // Unit
579 std::string units = prop->units();
580 if (!units.empty()) {
581 out << " " << units;
582 }
583}
584
591 // Instrument related
592 Geometry::Instrument_const_sptr instrument = m_inputWS->getInstrument();
593 if (instrument) {
594 auto source = instrument->getSource();
595 auto sample = instrument->getSample();
596 if (source && sample) {
597 return true;
598 }
599 }
600 return false;
601}
602
614void SaveGSS::openFileStream(const std::string &outFilePath, std::ofstream &outStream) {
615 // We have to take the ofstream as a parameter instead of returning
616 // as GCC 4.X (RHEL7) does not allow a ioStream to be moved or have
617 // NRVO applied
618
619 // Select to append to current stream or override
620 const bool append = getProperty("Append");
621 using std::ios_base;
622 const ios_base::openmode mode = (append ? (ios_base::out | ios_base::app) : (ios_base::out | ios_base::trunc));
623
624 // Have to wrap this in a unique pointer as GCC 4.x (RHEL 7) does
625 // not support the move operator on iostreams
626 outStream.open(outFilePath, mode);
627 if (outStream.fail()) {
628 // Get the error message from library and log before throwing
629 const std::string error = strerror(errno);
630 throw Kernel::Exception::FileError("Failed to open file. Error was: " + error, outFilePath);
631 }
632
633 // Stream is good at this point
634}
635
636//----------------------------------------------------------------------------
645void SaveGSS::setOtherProperties(IAlgorithm *alg, const std::string &propertyName, const std::string &propertyValue,
646 int periodNum) {
647 // We want to append subsequent group members to the first one
648 if (propertyName == "Append") {
649 if (periodNum != 1) {
650 alg->setPropertyValue(propertyName, "1");
651 } else
652 alg->setPropertyValue(propertyName, propertyValue);
653 }
654 // We want the bank number to increment for each member of the group
655 else if (propertyName == "Bank") {
656 alg->setProperty("Bank", std::stoi(propertyValue) + periodNum - 1);
657 } else
658 Algorithm::setOtherProperties(alg, propertyName, propertyValue, periodNum);
659}
660
668std::map<std::string, std::string> SaveGSS::validateInputs() {
669 std::map<std::string, std::string> result;
670
671 API::MatrixWorkspace_const_sptr input_ws = getProperty("InputWorkspace");
672 if (!input_ws) {
673 result["InputWorkspace"] = "The input workspace cannot be a GroupWorkspace.";
674 return result;
675 }
676 // Check the number of histogram/spectra < 99
677 const auto nHist = static_cast<int>(input_ws->getNumberHistograms());
678 const bool split = getProperty("SplitFiles");
679 if (nHist > 99 && !split) {
680 std::string outError = "Number of Spectra(" + std::to_string(nHist) + ") cannot be larger than 99 for GSAS file";
681 result["InputWorkspace"] = outError;
682 result["SplitFiles"] = outError;
683 return result;
684 }
685
686 // Check we have any output filenames
687 std::string output_file_name = getProperty("Filename");
688 if (output_file_name.size() == 0) {
689 result["Filename"] = "Filename cannot be left empty.";
690 return result;
691 }
692
693 // Check about the user specified bank header
694 std::vector<std::string> user_header_vec = getProperty("UserSpecifiedBankHeader");
695 if (user_header_vec.size() > 0 && user_header_vec.size() != input_ws->getNumberHistograms()) {
696 result["UserSpecifiedBankHeader"] = "If user specifies bank header, each bank must "
697 "have a unique user-specified header.";
698 return result;
699 }
700
701 return result;
702}
703
712void SaveGSS::writeBufferToFile(size_t numOutFiles, size_t numSpectra) {
713 // When there are multiple files we can open them all in parallel
714 // Check that either the number of files or spectra is greater than
715 // 1 otherwise our storage method is no longer valid
716 assertNumFilesAndSpectraIsValid(numOutFiles, numSpectra);
717
718 const auto numOutFilesInt64 = static_cast<int64_t>(numOutFiles);
719
721 for (int64_t fileIndex = 0; fileIndex < numOutFilesInt64; fileIndex++) {
723
724 // Open each file when there are multiple
725 std::ofstream fileStream;
726 openFileStream(m_outFileNames[fileIndex], fileStream);
727 for (size_t specIndex = 0; specIndex < numSpectra; specIndex++) {
728 // Write each spectra when there are multiple
729 const size_t index = specIndex + fileIndex;
730 assert(m_outputBuffer[index]->str().size() > 0);
731 fileStream << m_outputBuffer[index]->rdbuf();
732 }
733
734 fileStream.close();
735 if (fileStream.fail()) {
736 const std::string error = strerror(errno);
737 g_log.error("Failed to close file. Error was: " + error);
738 throw std::runtime_error("Failed to close the file at " + m_outFileNames[fileIndex] +
739 " - this file may be empty, corrupted or incorrect.");
740 }
742 }
744}
745
746void SaveGSS::writeRALFHeader(std::stringstream &out, int bank, const HistogramData::Histogram &histo) const {
747 const auto &xVals = histo.binEdges();
748 const size_t datasize = histo.y().size();
749 const double bc1 = xVals[0] * 32;
750 const double bc2 = (xVals[1] - xVals[0]) * 32;
751 // Logarithmic step
752 double bc4 = (xVals[1] - xVals[0]) / xVals[0];
753 if (!std::isfinite(bc4))
754 bc4 = 0; // If X is zero for BANK
755
756 // Write out the data header
757 writeBankHeader(out, "RALF", bank, datasize);
758 const std::string bankDataType = getPropertyValue("DataFormat");
759 out << std::fixed << " " << std::setprecision(0) << std::setw(8) << bc1 << std::fixed << " " << std::setprecision(0)
760 << std::setw(8) << bc2 << std::fixed << " " << std::setprecision(0) << std::setw(8) << bc1 << std::fixed << " "
761 << std::setprecision(5) << std::setw(7) << bc4 << " " << bankDataType << "\n";
762}
763
764void SaveGSS::writeRALF_ALTdata(std::stringstream &out, const int bank, const HistogramData::Histogram &histo) const {
765 const size_t datasize = histo.y().size();
766 const auto &xPointVals = histo.points();
767 const auto &yVals = histo.y();
768 const auto &eVals = histo.e();
769
770 writeRALFHeader(out, bank, histo);
771
772 const size_t dataEntriesPerLine = 4;
773 // This method calculates the minimum number of lines
774 // we need to write to capture all the data for example
775 // if we have 6 data entries it will calculate 2 output lines (4 + 2)
776 const int64_t numberOfOutLines = (datasize + dataEntriesPerLine - 1) / dataEntriesPerLine;
777
778 std::vector<std::unique_ptr<std::stringstream>> outLines;
779 outLines.resize(numberOfOutLines);
780
781 for (int64_t i = 0; i < numberOfOutLines; i++) {
782 outLines[i] = makeStringStream();
783 auto &outLine = *outLines[i];
784
785 size_t dataPosition = i * dataEntriesPerLine;
786 const size_t endPosition = dataPosition + dataEntriesPerLine;
787
788 // 4 Blocks of data (20 chars) per line (80 chars)
789 for (; dataPosition < endPosition; dataPosition++) {
790 if (dataPosition < datasize) {
791 // We have data to append
792
793 const auto epos = static_cast<int>(fixErrorValue(eVals[dataPosition] * 1000));
794
795 outLine << std::fixed << std::setw(8) << static_cast<int>(xPointVals[dataPosition] * 32);
796 outLine << std::fixed << std::setw(7) << static_cast<int>(yVals[dataPosition] * 1000);
797 outLine << std::fixed << std::setw(5) << epos;
798 }
799 }
800 // Append a newline character at the end of each data block
801 outLine << "\n";
802 }
803
804 for (const auto &outLine : outLines) {
805 // Ensure the output order is preserved
806 out << outLine->rdbuf();
807 }
808}
809
810void SaveGSS::writeRALF_XYEdata(const int bank, const bool MultiplyByBinWidth, std::stringstream &out,
811 const HistogramData::Histogram &histo) const {
812 const auto &xVals = histo.binEdges();
813 const auto &xPointVals = histo.points();
814 const auto &yVals = histo.y();
815 const auto &eVals = histo.e();
816 const size_t datasize = yVals.size();
817
818 writeRALFHeader(out, bank, histo);
819
820 std::vector<std::unique_ptr<std::stringstream>> outLines;
821 outLines.resize(datasize);
822
824 for (int64_t i = 0; i < static_cast<int64_t>(datasize); i++) {
825 outLines[i] = makeStringStream();
826 auto &outLine = *outLines[i];
827 const double binWidth = xVals[i + 1] - xVals[i];
828 const double outYVal{MultiplyByBinWidth ? yVals[i] * binWidth : yVals[i]};
829 const double epos = fixErrorValue(MultiplyByBinWidth ? eVals[i] * binWidth : eVals[i]);
830
831 // The center of the X bin.
832 outLine << std::fixed << std::setprecision(5) << std::setw(15) << xPointVals[i];
833 outLine << std::fixed << std::setprecision(8) << std::setw(18) << outYVal;
834 outLine << std::fixed << std::setprecision(8) << std::setw(18) << epos << "\n";
835 }
836
837 for (const auto &outLine : outLines) {
838 // Ensure the output order is preserved
839 out << outLine->rdbuf();
840 }
841}
842
843//----------------------------------------------------------------------------
854void SaveGSS::writeSLOGdata(const size_t ws_index, const int bank, const bool MultiplyByBinWidth,
855 std::stringstream &out, const HistogramData::Histogram &histo,
856 const std::vector<int> &xye_precision) const {
857 // check inputs
858 if (xye_precision.size() != 3)
859 throw std::runtime_error("SLOG XYE precisions are not given in a 3-item vector.");
860
861 const auto &xVals = histo.binEdges();
862 const auto &xPoints = histo.points();
863 const auto &yVals = histo.y();
864 const auto &eVals = histo.e();
865 const size_t datasize = yVals.size();
866
867 const double bc1 = xVals.front(); // minimum TOF in microseconds
868 const double bc2 = *(xPoints.end() - 1); // maximum TOF (in microseconds?)
869 const double bc3 = (*(xVals.begin() + 1) - bc1) / bc1; // deltaT/T
870
871 if (bc1 <= 0.) {
872 throw std::runtime_error("Cannot write out logarithmic data starting at zero or less");
873 }
874 if (isConstantDelta(xVals)) {
875 g_log.error() << "Constant delta - T binning : " << xVals.front() << ", " << *(xVals.begin() + 1) << ", "
876 << *(xVals.begin() + 2) << "... " << std::endl;
877 throw std::runtime_error("While writing SLOG format : Found constant "
878 "delta - T binning for bank " +
879 std::to_string(bank));
880 }
881
882 g_log.debug() << "SaveGSS(): Min TOF = " << bc1 << '\n';
883
884 // Write bank header
886 // write user header only!
887 out << std::fixed << std::setw(80) << m_user_specified_bank_headers[ws_index] << "\n";
888 } else {
889 // write general bank header part
890 writeBankHeader(out, "SLOG", bank, datasize);
891 // write the SLOG specific type
892 out << std::fixed << " " << std::setprecision(0) << std::setw(10) << bc1 << std::fixed << " "
893 << std::setprecision(0) << std::setw(10) << bc2 << std::fixed << " " << std::setprecision(7) << std::setw(10)
894 << bc3 << std::fixed << " 0 FXYE\n";
895 }
896
897 std::vector<std::unique_ptr<std::stringstream>> outLines;
898 outLines.resize(datasize);
899
901 for (int64_t i = 0; i < static_cast<int64_t>(datasize); i++) {
902 outLines[i] = makeStringStream();
903 auto &outLine = *outLines[i];
904 const double binWidth = xVals[i + 1] - xVals[i];
905 const double yValue{MultiplyByBinWidth ? yVals[i] * binWidth : yVals[i]};
906 const double eValue{fixErrorValue(MultiplyByBinWidth ? eVals[i] * binWidth : eVals[i])};
907
908 // FIXME - Next step is to make the precision to be flexible from user
909 // inputs
910 outLine << " " << std::fixed << std::setprecision(xye_precision[0]) << std::setw(20) << xPoints[i] << " "
911 << std::fixed << std::setprecision(xye_precision[1]) << std::setw(20) << yValue << " " << std::fixed
912 << std::setprecision(xye_precision[2]) << std::setw(20) << eValue << std::setw(12) << " "
913 << "\n"; // let it flush its own buffer
914 }
915
916 for (const auto &outLine : outLines) {
917 out << outLine->rdbuf();
918 }
919}
920
921} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double value
The value of the point.
Definition: FitMW.cpp:51
double error
Definition: IndexPeaks.cpp:133
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
double left
Definition: LineProfile.cpp:80
double right
Definition: LineProfile.cpp:81
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
Definition: MultiThreaded.h:94
#define PARALLEL_FOR_NO_WSP_CHECK()
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
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
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
virtual void setOtherProperties(IAlgorithm *alg, const std::string &propertyName, const std::string &propertyValue, int periodNum)
Virtual method to set the non workspace properties for this algorithm.
Definition: Algorithm.cpp:1547
@ Save
to specify a file to write to, the file may or may not exist
Definition: FileProperty.h:49
IAlgorithm is the interface implemented by the Algorithm base class.
Definition: IAlgorithm.h:45
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Definition: LogManager.cpp:265
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
Definition: LogManager.cpp:404
This class stores information regarding an experimental run as a series of log entries.
Definition: Run.h:38
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
Definition: SpectrumInfo.h:53
Kernel::UnitParametersMap diffractometerConstants(const size_t index, std::vector< detid_t > &uncalibratedDets) const
Calculate average diffractometer constants (DIFA, DIFC, TZERO) of detectors associated with this spec...
double twoTheta(const size_t index) const
Returns the scattering angle 2 theta in radians (angle w.r.t.
double l2(const size_t index) const
Returns L2 (distance from sample to spectrum).
double l1() const
Returns L1 (distance from source to sample).
A property class for workspaces.
Saves a focused data set into a three column GSAS format containing X_i, Y_i*step,...
Definition: SaveGSS.h:56
bool m_overwrite_std_gsas_header
flag to overwrite standard GSAS header
Definition: SaveGSS.h:147
void init() override
Initialisation code.
Definition: SaveGSS.cpp:117
std::map< std::string, std::string > validateInputs() override
Validates the user input and warns / throws on bad conditions.
Definition: SaveGSS.cpp:668
void writeSLOGdata(const size_t ws_index, const int bank, const bool MultiplyByBinWidth, std::stringstream &out, const HistogramData::Histogram &histo, const std::vector< int > &xye_precision) const
Write out the data in SLOG format.
Definition: SaveGSS.cpp:854
void processUserSpecifiedHeaders()
Process input user-specified headers.
Definition: SaveGSS.cpp:218
void generateOutFileNames(size_t numberOfOutFiles)
Generates the filename(s) and paths to write to and stores in member var.
Definition: SaveGSS.cpp:500
void generateInstrumentHeader(std::stringstream &out, double l1) const
Generates the instrument header and returns this as a string stream.
Definition: SaveGSS.cpp:403
void writeBufferToFile(size_t numOutFiles, size_t numSpectra)
Writes the current buffer to the user specified file path.
Definition: SaveGSS.cpp:712
void setOtherProperties(IAlgorithm *alg, const std::string &propertyName, const std::string &propertyValue, int periodNum) override
sets non workspace properties for the algorithm
Definition: SaveGSS.cpp:645
const std::string name() const override
Algorithm's name.
Definition: SaveGSS.h:59
void generateBankData(std::stringstream &outBuf, size_t specIndex, const std::string &outputFormat, const std::vector< int > &slog_xye_precisions) const
Turns the data associated with this spectra into a string stream.
Definition: SaveGSS.cpp:283
std::vector< std::string > m_user_specified_gsas_header
User specified header string.
Definition: SaveGSS.h:145
API::MatrixWorkspace_const_sptr m_inputWS
Workspace.
Definition: SaveGSS.h:134
void getLogValue(std::stringstream &out, const API::Run &runInfo, const std::string &name, const std::string &failsafeValue="UNKNOWN") const
Returns the log value in a GSAS format as a string stream.
Definition: SaveGSS.cpp:552
void writeRALF_ALTdata(std::stringstream &out, const int bank, const HistogramData::Histogram &histo) const
Write out the data in RALF - ALT format.
Definition: SaveGSS.cpp:764
void openFileStream(const std::string &outFilePath, std::ofstream &outStream)
Opens a new file stream at the path specified.
Definition: SaveGSS.cpp:614
std::vector< std::unique_ptr< std::stringstream > > m_outputBuffer
The output buffer.
Definition: SaveGSS.h:137
std::unique_ptr< API::Progress > m_progress
Holds pointer to progress bar.
Definition: SaveGSS.h:143
void writeRALF_XYEdata(const int bank, const bool MultiplyByBinWidth, std::stringstream &out, const HistogramData::Histogram &histo) const
Write out the data in RALF - FXYE format.
Definition: SaveGSS.cpp:810
std::vector< std::string > m_outFileNames
The output filename(s)
Definition: SaveGSS.h:139
bool areAllDetectorsValid() const
Determines if all spectra have detectors.
Definition: SaveGSS.cpp:243
std::vector< std::string > m_user_specified_bank_headers
User specified bank header.
Definition: SaveGSS.h:149
void generateGSASBuffer(size_t numOutFiles, size_t numOutSpectra)
Generates the output which will be written to the GSAS file.
Definition: SaveGSS.cpp:344
void generateBankHeader(std::stringstream &out, const API::SpectrumInfo &spectrumInfo, size_t specIndex) const
Generates the bank header and returns this as a string stream.
Definition: SaveGSS.cpp:323
bool m_overwrite_std_bank_header
flag to overwrite standard GSAS bank header
Definition: SaveGSS.h:151
void writeRALFHeader(std::stringstream &out, int bank, const HistogramData::Histogram &histo) const
Definition: SaveGSS.cpp:746
bool isInstrumentValid() const
Returns if the input workspace instrument is valid.
Definition: SaveGSS.cpp:590
bool m_allDetectorsValid
Indicates whether all spectra have valid detectors.
Definition: SaveGSS.h:141
void exec() override
Execution code.
Definition: SaveGSS.cpp:185
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
Records the filename and the description of failure.
Definition: Exception.h:98
virtual void setPropertyValue(const std::string &name, const std::string &value)=0
Sets property value from a string.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
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
Base class for properties.
Definition: Property.h:94
virtual std::string value() const =0
Returns the value of the property as a string.
A specialised Property class for holding a series of time-value pairs.
TimeSeriesPropertyStatistics getStatistics() const
Return a TimeSeriesPropertyStatistics object.
std::vector< AlgorithmHistory_sptr > AlgorithmHistories
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
void split(const int A, int &S, int &V)
Split a number into the sign and positive value.
Definition: Acomp.cpp:42
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition: Property.h:53