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
24
25#include <filesystem>
26#include <fstream>
27
28namespace Mantid::DataHandling {
29
30using namespace Mantid::API;
31using namespace Mantid::HistogramData;
32
33// Register the algorithm into the AlgorithmFactory
35
36namespace { // Anonymous namespace
37const std::string RALF("RALF");
38const std::string SLOG("SLOG");
39const std::string FXYE("FXYE");
40const std::string ALT("ALT");
41
43const double m_TOLERANCE = 1.e-10;
44
45void assertNumFilesAndSpectraIsValid(size_t numOutFiles, size_t numOutSpectra) {
46 // If either numOutFiles or numOutSpectra are not 1 we need to check
47 // that we are conforming to the expected storage layout.
48 if ((numOutFiles != 1) || (numOutSpectra != 1)) {
49 assert((numOutFiles > 1) != (numOutSpectra > 1));
50 }
51}
52
53bool doesFileExist(const std::filesystem::path &filePath) { return std::filesystem::exists(filePath); }
54
55double fixErrorValue(const double value) {
56 // Fix error if value is less than zero or infinity
57 // Negative errors cannot be read by GSAS
58 if (value <= 0. || !std::isfinite(value)) {
59 return 0.;
60 } else {
61 return value;
62 }
63}
64
65bool isEqual(const double left, const double right) {
66 return Kernel::withinRelativeDifference(left, right, m_TOLERANCE);
67}
68
69bool isConstantDelta(const HistogramData::BinEdges &xAxis) {
70 const double deltaX = (xAxis[1] - xAxis[0]);
71 for (std::size_t i = 1; i < xAxis.size(); ++i) {
72 if (!isEqual(xAxis[i] - xAxis[i - 1], deltaX)) {
73 return false;
74 }
75 }
76 return true;
77}
78
79std::unique_ptr<std::stringstream> makeStringStream() {
80 // This and all unique_ptrs wrapping streams is a workaround
81 // for GCC 4.x. The standard allowing streams to be moved was
82 // added after the 4 series was released. Allowing this would
83 // break the ABI (hence GCC 5 onwards doesn't have this fault)
84 // so there are lots of restrictions in place with stream.
85 // Instead we can work around this by using pointers to streams.
86 // Tl;dr - This is a workaround for GCC 4.x (RHEL7)
87 return std::make_unique<std::stringstream>();
88}
89
90//----------------------------------------------------------------------------------------------
100void writeBankHeader(std::stringstream &out, const std::string &bintype, const int banknum, const size_t datasize) {
101 std::ios::fmtflags fflags(out.flags());
102 out << "BANK " << std::fixed << std::setprecision(0)
103 << banknum // First bank should be 1 for GSAS; this can be changed
104 << std::fixed << " " << datasize << std::fixed << " " << datasize << std::fixed << " " << bintype;
105 out.flags(fflags);
106}
107} // End of anonymous namespace
108
109//----------------------------------------------------------------------------------------------
110// Initialise the algorithm
112 const std::vector<std::string> exts{".gsa", ".gss", ".gda", ".txt"};
114 std::make_unique<API::WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Kernel::Direction::Input),
115 "The input workspace");
116
117 declareProperty(std::make_unique<API::FileProperty>("Filename", "", API::FileProperty::Save, exts),
118 "The filename to use for the saved data");
119
120 declareProperty("SplitFiles", true,
121 "Whether to save each spectrum into a separate file ('true') "
122 "or not ('false'). Note that this is a string, not a boolean property.");
123
124 declareProperty("Append", true, "If true and Filename already exists, append, else overwrite ");
125
126 declareProperty("Bank", 1,
127 "The bank number to include in the file header for the first spectrum, "
128 "i.e., the starting bank number. "
129 "This will increment for each spectrum or group member.");
130
131 const std::vector<std::string> formats{RALF, SLOG};
132 declareProperty("Format", RALF, std::make_shared<Kernel::StringListValidator>(formats), "GSAS format to save as");
133
134 const std::vector<std::string> ralfDataFormats{FXYE, ALT};
135 declareProperty("DataFormat", FXYE, std::make_shared<Kernel::StringListValidator>(ralfDataFormats),
136 "Saves RALF data as either FXYE or alternative format");
137 setPropertySettings("DataFormat", std::make_unique<Kernel::VisibleWhenProperty>(
139
140 declareProperty("MultiplyByBinWidth", true, "Multiply the intensity (Y) by the bin width; default TRUE.");
141
142 declareProperty("ExtendedHeader", false, "Add information to the header about iparm file and normalization");
143
144 declareProperty("UseSpectrumNumberAsBankID", false,
145 "If true, then each bank's bank ID is equal to the spectrum number; "
146 "otherwise, the continuous bank IDs are applied. ");
147
148 declareProperty(std::make_unique<Kernel::ArrayProperty<std::string>>("UserSpecifiedGSASHeader"),
149 "Each line will be put to the header of the output GSAS file.");
150
151 declareProperty("OverwriteStandardHeader", true,
152 "If true, then the standard header will be replaced "
153 "by the user specified header. Otherwise, the user "
154 "specified header will be "
155 "inserted before the original header");
156
157 declareProperty(std::make_unique<Kernel::ArrayProperty<std::string>>("UserSpecifiedBankHeader"),
158 "Each string will be used to replaced the standard GSAS bank header."
159 "Number of strings in the give array must be same as number of banks."
160 "And the order is preserved.");
161
162 auto must_be_3 = std::make_shared<Kernel::ArrayLengthValidator<int>>(3);
163 auto precision_range = std::make_shared<Kernel::ArrayBoundedValidator<int>>(0, 10);
164
165 auto precision_validator = std::make_shared<Kernel::CompositeValidator>();
166 precision_validator->add(must_be_3);
167 precision_validator->add(precision_range);
168
169 std::vector<int> default_precision(3, 9);
170 declareProperty(std::make_unique<Kernel::ArrayProperty<int>>("SLOGXYEPrecision", std::move(default_precision),
171 std::move(precision_validator)),
172 "Enter 3 integers as the precisions of output X, Y and E for SLOG data "
173 "only."
174 "Default is (9, 9, 9) if it is left empty. Otherwise it is not "
175 "allowed.");
176}
177
178// Execute the algorithm
180 // Retrieve the input workspace and associated properties
181 m_inputWS = getProperty("InputWorkspace");
182 const size_t nHist = m_inputWS->getNumberHistograms();
183
184 // process user special headers
186
187 // Are we writing one file with n spectra or
188 // n files with 1 spectra each
189 const bool split = getProperty("SplitFiles");
190 const size_t numOfOutFiles{split ? nHist : 1};
191 const size_t numOutSpectra{split ? 1 : nHist};
192
193 // Initialise various properties we are going to need within this alg
195 generateOutFileNames(numOfOutFiles);
196 m_outputBuffer.resize(nHist);
197 std::generate(m_outputBuffer.begin(), m_outputBuffer.end(), []() { return makeStringStream(); });
198
199 // Progress is 2 * number of histograms. One for generating data
200 // one for writing out data
201 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, (nHist * 2));
202
203 // Now start executing main part of the code
204 generateGSASBuffer(numOfOutFiles, numOutSpectra);
205 writeBufferToFile(numOfOutFiles, numOutSpectra);
206}
207
208//----------------------------------------------------------------------------------------------
213
214 // user specified GSAS
215 m_user_specified_gsas_header = getProperty("UserSpecifiedGSASHeader");
216
217 if (m_user_specified_gsas_header.empty()) {
219 } else {
220 m_overwrite_std_gsas_header = getProperty("OverwriteStandardHeader");
221 }
222
223 // user specified bank header
224 m_user_specified_bank_headers = getProperty("UserSpecifiedBankHeader");
226
227 return;
228}
229
230//----------------------------------------------------------------------------------------------
238 const auto &spectrumInfo = m_inputWS->spectrumInfo();
239 const size_t numHist = m_inputWS->getNumberHistograms();
240
241 if (!isInstrumentValid()) {
242 g_log.warning("No valid instrument found with this workspace"
243 " Treating as NO-INSTRUMENT CASE");
244 return false;
245 }
246
247 bool allValid = true;
248
249 const auto numHistInt64 = static_cast<int64_t>(numHist);
250
252 for (int64_t histoIndex = 0; histoIndex < numHistInt64; histoIndex++) {
253 if (allValid) {
254 // Check this spectra has detectors
255 if (!spectrumInfo.hasDetectors(histoIndex)) {
256 allValid = false;
257 g_log.warning() << "There is no detector associated with spectrum " << histoIndex
258 << ". Workspace is treated as NO-INSTRUMENT case. \n";
259 }
260 }
261 }
262 return allValid;
263}
264
265//----------------------------------------------------------------------------------------------
277void SaveGSS::generateBankData(std::stringstream &outBuf, size_t specIndex, const std::string &outputFormat,
278 const std::vector<int> &slog_xye_precisions) const {
279 // Determine bank number into GSAS file
280 const bool useSpecAsBank = getProperty("UseSpectrumNumberAsBankID");
281 const bool multiplyByBinWidth = getProperty("MultiplyByBinWidth");
282 const int userStartingBankNumber = getProperty("Bank");
283 const std::string ralfDataFormat = getPropertyValue("DataFormat");
284
285 int bankid;
286 if (useSpecAsBank) {
287 bankid = static_cast<int>(m_inputWS->getSpectrum(specIndex).getSpectrumNo());
288 } else {
289 bankid = userStartingBankNumber + static_cast<int>(specIndex);
290 }
291
292 // Write data
293 const auto &histogram = m_inputWS->histogram(specIndex);
294 if (outputFormat == RALF) {
295 if (ralfDataFormat == FXYE) {
296 writeRALF_XYEdata(bankid, multiplyByBinWidth, outBuf, histogram);
297 } else if (ralfDataFormat == ALT) {
298 writeRALF_ALTdata(outBuf, bankid, histogram);
299 } else {
300 throw std::runtime_error("Unknown RALF data format" + ralfDataFormat);
301 }
302 } else if (outputFormat == SLOG) {
303 writeSLOGdata(specIndex, bankid, multiplyByBinWidth, outBuf, histogram, slog_xye_precisions);
304 } else {
305 throw std::runtime_error("Cannot write to the unknown " + outputFormat + "output format");
306 }
307}
308
317void SaveGSS::generateBankHeader(std::stringstream &out, const API::SpectrumInfo &spectrumInfo,
318 size_t specIndex) const {
319 // If we have all valid detectors get these properties else use 0
321 const auto l1 = spectrumInfo.l1();
322 const auto l2 = spectrumInfo.l2(specIndex);
323 const auto twoTheta = spectrumInfo.twoTheta(specIndex);
324 auto diffConstants = spectrumInfo.diffractometerConstants(specIndex);
325 out << "# Total flight path " << (l1 + l2) << "m, tth " << (twoTheta * 180. / M_PI) << "deg, DIFC "
326 << diffConstants[Kernel::UnitParams::difc] << "\n";
327 }
328 out << "# Data for spectrum :" << specIndex << "\n";
329}
330
338void SaveGSS::generateGSASBuffer(size_t numOutFiles, size_t numOutSpectra) {
339 // Generate the output buffer for each histogram (spectrum)
340 const auto &spectrumInfo = m_inputWS->spectrumInfo();
341 const bool append = getProperty("Append");
342
343 // Check if the caller has reserved space in our output buffer
344 assert(m_outputBuffer.size() > 0);
345
346 // Because of the storage layout we can either handle files > 0
347 // XOR spectra per file > 0. Compensate for the fact that is will be
348 // 1 thing per thing as far as users are concerned.
349 assertNumFilesAndSpectraIsValid(numOutFiles, numOutSpectra);
350
351 // If all detectors are not valid we use the no instrument case and
352 // set l1 to 0
353 const double l1{m_allDetectorsValid ? spectrumInfo.l1() : 0};
354
355 const auto numOutFilesInt64 = static_cast<int64_t>(numOutFiles);
356
357 const std::string outputFormat = getPropertyValue("Format");
358
359 std::vector<int> slog_xye_precisions = getProperty("SLOGXYEPrecision");
360
361 // Create the various output files we will need in a loop
363 for (int64_t fileIndex = 0; fileIndex < numOutFilesInt64; fileIndex++) {
364 // Add header to new files (e.g. doesn't exist or overwriting)
365 if (!doesFileExist(m_outFileNames[fileIndex]) || !append) {
367 }
368
369 // Then add each spectra to buffer
370 for (size_t specIndex = 0; specIndex < numOutSpectra; specIndex++) {
371 // Determine whether to skip the spectrum due to being masked
372 if (m_allDetectorsValid && spectrumInfo.isMasked(specIndex)) {
373 continue;
374 }
375 // Find the index - this is because we are guaranteed at least
376 // one of these is 0 from the assertion above and the fact
377 // split files is a boolean operator on the input side
378 const int64_t index = specIndex + fileIndex;
379 // Add bank header and details to buffer
381 // Add data to buffer
382 generateBankData(*m_outputBuffer[index], index, outputFormat, slog_xye_precisions);
383 m_progress->report();
384 }
385 }
386}
387
395void SaveGSS::generateInstrumentHeader(std::stringstream &out, double l1) const {
396
397 // write user header first
398 if (m_user_specified_gsas_header.size() > 0) {
399 for (const auto &iter : m_user_specified_gsas_header) {
400 out << iter << "\n";
401 }
402 }
403
404 // quit method if user plan to use his own header completely
406 return;
407 }
408
409 // write standard header
410 const Run &runinfo = m_inputWS->run();
411 const std::string format = getPropertyValue("Format");
412
413 // Run number
414 if (format == SLOG) {
415 out << "Sample Run: ";
416 getLogValue(out, runinfo, "run_number");
417 out << " Vanadium Run: ";
418 getLogValue(out, runinfo, "van_number");
419 out << " Wavelength: ";
420 getLogValue(out, runinfo, "LambdaRequest");
421 out << "\n";
422 }
423
424 if (this->getProperty("ExtendedHeader")) {
425 // the instrument parameter file
426 if (runinfo.hasProperty("iparm_file")) {
427 Kernel::Property const *prop = runinfo.getProperty("iparm_file");
428 if (prop != nullptr && (!prop->value().empty())) {
429 out << std::setw(80) << std::left;
430 out << "#Instrument parameter file: " << prop->value() << "\n";
431 }
432 }
433
434 // write out the GSAS monitor counts
435 out << "Monitor: ";
436 if (runinfo.hasProperty("gsas_monitor")) {
437 getLogValue(out, runinfo, "gsas_monitor");
438 } else {
439 getLogValue(out, runinfo, "gd_prtn_chrg", "1");
440 }
441 out << "\n";
442 }
443
444 if (format == SLOG) {
445 out << "# "; // make the next line a comment
446 }
447 out << m_inputWS->getTitle() << "\n";
448 out << "# " << m_inputWS->getNumberHistograms() << " Histograms\n";
449 out << "# File generated by Mantid:\n";
450 out << "# Instrument: " << m_inputWS->getInstrument()->getName() << "\n";
451 out << "# From workspace named : " << m_inputWS->getName() << "\n";
452 if (getProperty("MultiplyByBinWidth"))
453 out << "# with Y multiplied by the bin widths.\n";
454 out << "# Primary flight path " << l1 << "m \n";
455 if (format == SLOG) {
456 out << "# Sample Temperature: ";
457 getLogValue(out, runinfo, "SampleTemp");
458 out << " Freq: ";
459 getLogValue(out, runinfo, "SpeedRequest1");
460 out << " Guide: ";
461 getLogValue(out, runinfo, "guide");
462 out << "\n";
463
464 // print whether it is normalized by monitor or proton charge
465 bool norm_by_current = false;
466 bool norm_by_monitor = false;
467 const Mantid::API::AlgorithmHistories &algohist = m_inputWS->getHistory().getAlgorithmHistories();
468 for (const auto &algo : algohist) {
469 if (algo->name() == "NormaliseByCurrent")
470 norm_by_current = true;
471 if (algo->name() == "NormaliseToMonitor")
472 norm_by_monitor = true;
473 }
474 out << "#";
475 if (norm_by_current)
476 out << " Normalised to pCharge";
477 if (norm_by_monitor)
478 out << " Normalised to monitor";
479 out << "\n";
480 }
481}
482
492void SaveGSS::generateOutFileNames(size_t numberOfOutFiles) {
493 const std::string outputFileName = getProperty("Filename");
494 assert(numberOfOutFiles > 0);
495
496 if (numberOfOutFiles == 1) {
497 // Only add one name and don't generate split filenames
498 // when we are not in split mode
499 m_outFileNames.emplace_back(outputFileName);
500 return;
501 }
502
503 m_outFileNames.resize(numberOfOutFiles);
504
505 const std::filesystem::path filepath(outputFileName);
506 // Filename minus extension
507 const std::filesystem::path saveDir = filepath.parent_path();
508 const std::string basename = filepath.stem().string();
509 const std::string ext = filepath.extension().string().substr(1); // remove the '.'
510
511 // get file name and check with warning
512 const bool append = getProperty("Append");
513 for (size_t i = 0; i < numberOfOutFiles; i++) {
514 // Construct output name of the form 'base name-i.ext'
515 const std::string newFileName = basename + '-' + std::to_string(i) + "." + ext;
516 // Construct new path
517 std::filesystem::path newPath = saveDir / newFileName;
518 std::string filename = newPath.string();
519 m_outFileNames[i].assign(filename);
520 // check and make some warning
521 if (!append && doesFileExist(filename)) {
522 g_log.warning("Target GSAS file " + filename + " exists and will be overwritten.\n");
523 } else if (append && !doesFileExist(filename)) {
524 g_log.warning("Target GSAS file " + filename + " does not exist but algorithm was set to append.\n");
525 }
526 }
527
528 return;
529}
530
543void SaveGSS::getLogValue(std::stringstream &out, const API::Run &runInfo, const std::string &name,
544 const std::string &failsafeValue) const {
545 // Return without property exists
546 if (!runInfo.hasProperty(name)) {
547 out << failsafeValue;
548 return;
549 }
550 // Get handle of property
551 auto *prop = runInfo.getProperty(name);
552
553 // Return without a valid pointer to property
554 if (!prop) {
555 out << failsafeValue;
556 return;
557 }
558
559 // Get value
560 auto const *log = dynamic_cast<Kernel::TimeSeriesProperty<double> *>(prop);
561 if (log) {
562 // Time series to get mean
563 out << log->getStatistics().mean;
564 } else {
565 // No time series - just get the value
566 out << prop->value();
567 }
568
569 // Unit
570 std::string units = prop->units();
571 if (!units.empty()) {
572 out << " " << units;
573 }
574}
575
582 // Instrument related
583 Geometry::Instrument_const_sptr instrument = m_inputWS->getInstrument();
584 if (instrument) {
585 auto source = instrument->getSource();
586 auto sample = instrument->getSample();
587 if (source && sample) {
588 return true;
589 }
590 }
591 return false;
592}
593
605void SaveGSS::openFileStream(const std::string &outFilePath, std::ofstream &outStream) {
606 // We have to take the ofstream as a parameter instead of returning
607 // as GCC 4.X (RHEL7) does not allow a ioStream to be moved or have
608 // NRVO applied
609
610 // Select to append to current stream or override
611 const bool append = getProperty("Append");
612 using std::ios_base;
613 const ios_base::openmode mode = (append ? (ios_base::out | ios_base::app) : (ios_base::out | ios_base::trunc));
614
615 // Have to wrap this in a unique pointer as GCC 4.x (RHEL 7) does
616 // not support the move operator on iostreams
617 outStream.open(outFilePath, mode);
618 if (outStream.fail()) {
619 // Get the error message from library and log before throwing
620 const std::string error = strerror(errno);
621 throw Kernel::Exception::FileError("Failed to open file. Error was: " + error, outFilePath);
622 }
623
624 // Stream is good at this point
625}
626
627//----------------------------------------------------------------------------
636void SaveGSS::setOtherProperties(IAlgorithm *alg, const std::string &propertyName, const std::string &propertyValue,
637 int periodNum) {
638 // We want to append subsequent group members to the first one
639 if (propertyName == "Append") {
640 if (periodNum != 1) {
641 alg->setPropertyValue(propertyName, "1");
642 } else
643 alg->setPropertyValue(propertyName, propertyValue);
644 }
645 // We want the bank number to increment for each member of the group
646 else if (propertyName == "Bank") {
647 alg->setProperty("Bank", std::stoi(propertyValue) + periodNum - 1);
648 } else
649 Algorithm::setOtherProperties(alg, propertyName, propertyValue, periodNum);
650}
651
659std::map<std::string, std::string> SaveGSS::validateInputs() {
660 std::map<std::string, std::string> result;
661
662 API::MatrixWorkspace_const_sptr input_ws = getProperty("InputWorkspace");
663 if (!input_ws) {
664 result["InputWorkspace"] = "The input workspace cannot be a GroupWorkspace.";
665 return result;
666 }
667 // Check the number of histogram/spectra < 99
668 const auto nHist = static_cast<int>(input_ws->getNumberHistograms());
669 const bool split = getProperty("SplitFiles");
670 if (nHist > 99 && !split) {
671 std::string outError = "Number of Spectra(" + std::to_string(nHist) + ") cannot be larger than 99 for GSAS file";
672 result["InputWorkspace"] = outError;
673 result["SplitFiles"] = outError;
674 return result;
675 }
676
677 // Check we have any output filenames
678 std::string output_file_name = getProperty("Filename");
679 if (output_file_name.size() == 0) {
680 result["Filename"] = "Filename cannot be left empty.";
681 return result;
682 }
683
684 // Check about the user specified bank header
685 std::vector<std::string> user_header_vec = getProperty("UserSpecifiedBankHeader");
686 if (user_header_vec.size() > 0 && user_header_vec.size() != input_ws->getNumberHistograms()) {
687 result["UserSpecifiedBankHeader"] = "If user specifies bank header, each bank must "
688 "have a unique user-specified header.";
689 return result;
690 }
691
692 return result;
693}
694
703void SaveGSS::writeBufferToFile(size_t numOutFiles, size_t numSpectra) {
704 // When there are multiple files we can open them all in parallel
705 // Check that either the number of files or spectra is greater than
706 // 1 otherwise our storage method is no longer valid
707 assertNumFilesAndSpectraIsValid(numOutFiles, numSpectra);
708
709 const auto numOutFilesInt64 = static_cast<int64_t>(numOutFiles);
710
712 for (int64_t fileIndex = 0; fileIndex < numOutFilesInt64; fileIndex++) {
714
715 // Open each file when there are multiple
716 std::ofstream fileStream;
717 openFileStream(m_outFileNames[fileIndex], fileStream);
718 for (size_t specIndex = 0; specIndex < numSpectra; specIndex++) {
719 // Write each spectra when there are multiple
720 const size_t index = specIndex + fileIndex;
721 assert(m_outputBuffer[index]->str().size() > 0);
722 fileStream << m_outputBuffer[index]->rdbuf();
723 }
724
725 fileStream.close();
726 if (fileStream.fail()) {
727 const std::string error = strerror(errno);
728 g_log.error("Failed to close file. Error was: " + error);
729 throw std::runtime_error("Failed to close the file at " + m_outFileNames[fileIndex] +
730 " - this file may be empty, corrupted or incorrect.");
731 }
733 }
735}
736
737void SaveGSS::writeRALFHeader(std::stringstream &out, int bank, const HistogramData::Histogram &histo) const {
738 const auto &xVals = histo.binEdges();
739 const size_t datasize = histo.y().size();
740 const double bc1 = xVals[0] * 32;
741 const double bc2 = (xVals[1] - xVals[0]) * 32;
742 // Logarithmic step
743 double bc4 = (xVals[1] - xVals[0]) / xVals[0];
744 if (!std::isfinite(bc4))
745 bc4 = 0; // If X is zero for BANK
746
747 // Write out the data header
748 writeBankHeader(out, "RALF", bank, datasize);
749 const std::string bankDataType = getPropertyValue("DataFormat");
750 out << std::fixed << " " << std::setprecision(0) << std::setw(8) << bc1 << std::fixed << " " << std::setprecision(0)
751 << std::setw(8) << bc2 << std::fixed << " " << std::setprecision(0) << std::setw(8) << bc1 << std::fixed << " "
752 << std::setprecision(5) << std::setw(7) << bc4 << " " << bankDataType << "\n";
753}
754
755void SaveGSS::writeRALF_ALTdata(std::stringstream &out, const int bank, const HistogramData::Histogram &histo) const {
756 const size_t datasize = histo.y().size();
757 const auto &xPointVals = histo.points();
758 const auto &yVals = histo.y();
759 const auto &eVals = histo.e();
760
761 writeRALFHeader(out, bank, histo);
762
763 const size_t dataEntriesPerLine = 4;
764 // This method calculates the minimum number of lines
765 // we need to write to capture all the data for example
766 // if we have 6 data entries it will calculate 2 output lines (4 + 2)
767 const int64_t numberOfOutLines = (datasize + dataEntriesPerLine - 1) / dataEntriesPerLine;
768
769 std::vector<std::unique_ptr<std::stringstream>> outLines;
770 outLines.resize(numberOfOutLines);
771
772 for (int64_t i = 0; i < numberOfOutLines; i++) {
773 outLines[i] = makeStringStream();
774 auto &outLine = *outLines[i];
775
776 size_t dataPosition = i * dataEntriesPerLine;
777 const size_t endPosition = dataPosition + dataEntriesPerLine;
778
779 // 4 Blocks of data (20 chars) per line (80 chars)
780 for (; dataPosition < endPosition; dataPosition++) {
781 if (dataPosition < datasize) {
782 // We have data to append
783
784 const auto epos = static_cast<int>(fixErrorValue(eVals[dataPosition] * 1000));
785
786 outLine << std::fixed << std::setw(8) << static_cast<int>(xPointVals[dataPosition] * 32);
787 outLine << std::fixed << std::setw(7) << static_cast<int>(yVals[dataPosition] * 1000);
788 outLine << std::fixed << std::setw(5) << epos;
789 }
790 }
791 // Append a newline character at the end of each data block
792 outLine << "\n";
793 }
794
795 for (const auto &outLine : outLines) {
796 // Ensure the output order is preserved
797 out << outLine->rdbuf();
798 }
799}
800
801void SaveGSS::writeRALF_XYEdata(const int bank, const bool MultiplyByBinWidth, std::stringstream &out,
802 const HistogramData::Histogram &histo) const {
803 const auto &xVals = histo.binEdges();
804 const auto &xPointVals = histo.points();
805 const auto &yVals = histo.y();
806 const auto &eVals = histo.e();
807 const size_t datasize = yVals.size();
808
809 writeRALFHeader(out, bank, histo);
810
811 std::vector<std::unique_ptr<std::stringstream>> outLines;
812 outLines.resize(datasize);
813
815 for (int64_t i = 0; i < static_cast<int64_t>(datasize); i++) {
816 outLines[i] = makeStringStream();
817 auto &outLine = *outLines[i];
818 const double binWidth = xVals[i + 1] - xVals[i];
819 const double outYVal{MultiplyByBinWidth ? yVals[i] * binWidth : yVals[i]};
820 const double epos = fixErrorValue(MultiplyByBinWidth ? eVals[i] * binWidth : eVals[i]);
821
822 // The center of the X bin.
823 outLine << std::fixed << std::setprecision(5) << std::setw(15) << xPointVals[i];
824 outLine << std::fixed << std::setprecision(8) << std::setw(18) << outYVal;
825 outLine << std::fixed << std::setprecision(8) << std::setw(18) << epos << "\n";
826 }
827
828 for (const auto &outLine : outLines) {
829 // Ensure the output order is preserved
830 out << outLine->rdbuf();
831 }
832}
833
834//----------------------------------------------------------------------------
845void SaveGSS::writeSLOGdata(const size_t ws_index, const int bank, const bool MultiplyByBinWidth,
846 std::stringstream &out, const HistogramData::Histogram &histo,
847 const std::vector<int> &xye_precision) const {
848 // check inputs
849 if (xye_precision.size() != 3)
850 throw std::runtime_error("SLOG XYE precisions are not given in a 3-item vector.");
851
852 const auto &xVals = histo.binEdges();
853 const auto &xPoints = histo.points();
854 const auto &yVals = histo.y();
855 const auto &eVals = histo.e();
856 const size_t datasize = yVals.size();
857
858 const double bc1 = xVals.front(); // minimum TOF in microseconds
859 const double bc2 = *(xPoints.end() - 1); // maximum TOF (in microseconds?)
860 const double bc3 = (*(xVals.begin() + 1) - bc1) / bc1; // deltaT/T
861
862 if (bc1 <= 0.) {
863 throw std::runtime_error("Cannot write out logarithmic data starting at zero or less");
864 }
865 if (isConstantDelta(xVals)) {
866 g_log.error() << "Constant delta - T binning : " << xVals.front() << ", " << *(xVals.begin() + 1) << ", "
867 << *(xVals.begin() + 2) << "... " << std::endl;
868 throw std::runtime_error("While writing SLOG format : Found constant "
869 "delta - T binning for bank " +
870 std::to_string(bank));
871 }
872
873 g_log.debug() << "SaveGSS(): Min TOF = " << bc1 << '\n';
874
875 // Write bank header
877 // write user header only!
878 out << std::fixed << std::setw(80) << m_user_specified_bank_headers[ws_index] << "\n";
879 } else {
880 // write general bank header part
881 writeBankHeader(out, "SLOG", bank, datasize);
882 // write the SLOG specific type
883 out << std::fixed << " " << std::setprecision(0) << std::setw(10) << bc1 << std::fixed << " "
884 << std::setprecision(0) << std::setw(10) << bc2 << std::fixed << " " << std::setprecision(7) << std::setw(10)
885 << bc3 << std::fixed << " 0 FXYE\n";
886 }
887
888 std::vector<std::unique_ptr<std::stringstream>> outLines;
889 outLines.resize(datasize);
890
892 for (int64_t i = 0; i < static_cast<int64_t>(datasize); i++) {
893 outLines[i] = makeStringStream();
894 auto &outLine = *outLines[i];
895 const double binWidth = xVals[i + 1] - xVals[i];
896 const double yValue{MultiplyByBinWidth ? yVals[i] * binWidth : yVals[i]};
897 const double eValue{fixErrorValue(MultiplyByBinWidth ? eVals[i] * binWidth : eVals[i])};
898
899 // FIXME - Next step is to make the precision to be flexible from user
900 // inputs
901 outLine << " " << std::fixed << std::setprecision(xye_precision[0]) << std::setw(20) << xPoints[i] << " "
902 << std::fixed << std::setprecision(xye_precision[1]) << std::setw(20) << yValue << " " << std::fixed
903 << std::setprecision(xye_precision[2]) << std::setw(20) << eValue << std::setw(12) << " "
904 << "\n"; // let it flush its own buffer
905 }
906
907 for (const auto &outLine : outLines) {
908 out << outLine->rdbuf();
909 }
910}
911
912} // namespace Mantid::DataHandling
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double value
The value of the point.
Definition FitMW.cpp:51
double error
std::map< DeltaEMode::Type, std::string > index
double left
double right
#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...
#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.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
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.
@ Save
to specify a file to write to, the file may or may not exist
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.
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
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:111
std::map< std::string, std::string > validateInputs() override
Validates the user input and warns / throws on bad conditions.
Definition SaveGSS.cpp:659
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:845
void processUserSpecifiedHeaders()
Process input user-specified headers.
Definition SaveGSS.cpp:212
void generateOutFileNames(size_t numberOfOutFiles)
Generates the filename(s) and paths to write to and stores in member var.
Definition SaveGSS.cpp:492
void generateInstrumentHeader(std::stringstream &out, double l1) const
Generates the instrument header and returns this as a string stream.
Definition SaveGSS.cpp:395
void writeBufferToFile(size_t numOutFiles, size_t numSpectra)
Writes the current buffer to the user specified file path.
Definition SaveGSS.cpp:703
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:636
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:277
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:543
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:755
void openFileStream(const std::string &outFilePath, std::ofstream &outStream)
Opens a new file stream at the path specified.
Definition SaveGSS.cpp:605
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:801
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:237
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:338
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:317
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:737
bool isInstrumentValid() const
Returns if the input workspace instrument is valid.
Definition SaveGSS.cpp:581
bool m_allDetectorsValid
Indicates whether all spectra have valid detectors.
Definition SaveGSS.h:141
void exec() override
Execution code.
Definition SaveGSS.cpp:179
Support for a property that holds an array of values.
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:145
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
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 Kernel::TimeROI *roi=nullptr) const override
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.
MANTID_KERNEL_DLL bool withinRelativeDifference(T const x, T const y, S const tolerance)
Test whether x, y are within relative tolerance tol.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53