37const std::string RALF(
"RALF");
38const std::string SLOG(
"SLOG");
39const std::string FXYE(
"FXYE");
40const std::string ALT(
"ALT");
43const double m_TOLERANCE = 1.e-10;
45void assertNumFilesAndSpectraIsValid(
size_t numOutFiles,
size_t numOutSpectra) {
48 if ((numOutFiles != 1) || (numOutSpectra != 1)) {
49 assert((numOutFiles > 1) != (numOutSpectra > 1));
53bool doesFileExist(
const std::filesystem::path &filePath) {
return std::filesystem::exists(filePath); }
55double fixErrorValue(
const double value) {
65bool isEqual(
const double left,
const double right) {
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)) {
79std::unique_ptr<std::stringstream> makeStringStream() {
87 return std::make_unique<std::stringstream>();
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)
104 << std::fixed <<
" " << datasize << std::fixed <<
" " << datasize << std::fixed <<
" " << bintype;
112 const std::vector<std::string> exts{
".gsa",
".gss",
".gda",
".txt"};
115 "The input workspace");
118 "The filename to use for the saved data");
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.");
124 declareProperty(
"Append",
true,
"If true and Filename already exists, append, else overwrite ");
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.");
131 const std::vector<std::string> formats{RALF, SLOG};
132 declareProperty(
"Format", RALF, std::make_shared<Kernel::StringListValidator>(formats),
"GSAS format to save as");
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");
140 declareProperty(
"MultiplyByBinWidth",
true,
"Multiply the intensity (Y) by the bin width; default TRUE.");
142 declareProperty(
"ExtendedHeader",
false,
"Add information to the header about iparm file and normalization");
145 "If true, then each bank's bank ID is equal to the spectrum number; "
146 "otherwise, the continuous bank IDs are applied. ");
149 "Each line will be put to the header of the output GSAS file.");
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");
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.");
162 auto must_be_3 = std::make_shared<Kernel::ArrayLengthValidator<int>>(3);
163 auto precision_range = std::make_shared<Kernel::ArrayBoundedValidator<int>>(0, 10);
165 auto precision_validator = std::make_shared<Kernel::CompositeValidator>();
166 precision_validator->add(must_be_3);
167 precision_validator->add(precision_range);
169 std::vector<int> default_precision(3, 9);
171 std::move(precision_validator)),
172 "Enter 3 integers as the precisions of output X, Y and E for SLOG data "
174 "Default is (9, 9, 9) if it is left empty. Otherwise it is not "
182 const size_t nHist =
m_inputWS->getNumberHistograms();
190 const size_t numOfOutFiles{
split ? nHist : 1};
191 const size_t numOutSpectra{
split ? 1 : nHist};
201 m_progress = std::make_unique<Progress>(
this, 0.0, 1.0, (nHist * 2));
238 const auto &spectrumInfo =
m_inputWS->spectrumInfo();
239 const size_t numHist =
m_inputWS->getNumberHistograms();
242 g_log.
warning(
"No valid instrument found with this workspace"
243 " Treating as NO-INSTRUMENT CASE");
247 bool allValid =
true;
249 const auto numHistInt64 =
static_cast<int64_t
>(numHist);
252 for (int64_t histoIndex = 0; histoIndex < numHistInt64; histoIndex++) {
255 if (!spectrumInfo.hasDetectors(histoIndex)) {
257 g_log.
warning() <<
"There is no detector associated with spectrum " << histoIndex
258 <<
". Workspace is treated as NO-INSTRUMENT case. \n";
278 const std::vector<int> &slog_xye_precisions)
const {
280 const bool useSpecAsBank =
getProperty(
"UseSpectrumNumberAsBankID");
281 const bool multiplyByBinWidth =
getProperty(
"MultiplyByBinWidth");
282 const int userStartingBankNumber =
getProperty(
"Bank");
287 bankid =
static_cast<int>(
m_inputWS->getSpectrum(specIndex).getSpectrumNo());
289 bankid = userStartingBankNumber +
static_cast<int>(specIndex);
293 const auto &histogram =
m_inputWS->histogram(specIndex);
294 if (outputFormat == RALF) {
295 if (ralfDataFormat == FXYE) {
297 }
else if (ralfDataFormat == ALT) {
300 throw std::runtime_error(
"Unknown RALF data format" + ralfDataFormat);
302 }
else if (outputFormat == SLOG) {
303 writeSLOGdata(specIndex, bankid, multiplyByBinWidth, outBuf, histogram, slog_xye_precisions);
305 throw std::runtime_error(
"Cannot write to the unknown " + outputFormat +
"output format");
318 size_t specIndex)
const {
321 const auto l1 = spectrumInfo.
l1();
322 const auto l2 = spectrumInfo.
l2(specIndex);
325 out <<
"# Total flight path " << (l1 +
l2) <<
"m, tth " << (
twoTheta * 180. / M_PI) <<
"deg, DIFC "
328 out <<
"# Data for spectrum :" << specIndex <<
"\n";
340 const auto &spectrumInfo =
m_inputWS->spectrumInfo();
349 assertNumFilesAndSpectraIsValid(numOutFiles, numOutSpectra);
355 const auto numOutFilesInt64 =
static_cast<int64_t
>(numOutFiles);
359 std::vector<int> slog_xye_precisions =
getProperty(
"SLOGXYEPrecision");
363 for (int64_t fileIndex = 0; fileIndex < numOutFilesInt64; fileIndex++) {
370 for (
size_t specIndex = 0; specIndex < numOutSpectra; specIndex++) {
378 const int64_t
index = specIndex + fileIndex;
414 if (format == SLOG) {
415 out <<
"Sample Run: ";
417 out <<
" Vanadium Run: ";
419 out <<
" Wavelength: ";
428 if (prop !=
nullptr && (!prop->
value().empty())) {
429 out << std::setw(80) << std::left;
430 out <<
"#Instrument parameter file: " << prop->
value() <<
"\n";
444 if (format == SLOG) {
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";
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: ";
465 bool norm_by_current =
false;
466 bool norm_by_monitor =
false;
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;
476 out <<
" Normalised to pCharge";
478 out <<
" Normalised to monitor";
493 const std::string outputFileName =
getProperty(
"Filename");
494 assert(numberOfOutFiles > 0);
496 if (numberOfOutFiles == 1) {
505 const std::filesystem::path filepath(outputFileName);
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);
513 for (
size_t i = 0; i < numberOfOutFiles; i++) {
515 const std::string newFileName = basename +
'-' +
std::to_string(i) +
"." + ext;
517 std::filesystem::path newPath = saveDir / newFileName;
518 std::string filename = newPath.string();
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");
544 const std::string &failsafeValue)
const {
547 out << failsafeValue;
555 out << failsafeValue;
566 out << prop->value();
570 std::string units = prop->units();
571 if (!units.empty()) {
585 auto source = instrument->getSource();
586 auto sample = instrument->getSample();
587 if (source && sample) {
613 const ios_base::openmode mode = (append ? (ios_base::out | ios_base::app) : (ios_base::out | ios_base::trunc));
617 outStream.open(outFilePath, mode);
618 if (outStream.fail()) {
620 const std::string
error = strerror(errno);
639 if (propertyName ==
"Append") {
640 if (periodNum != 1) {
646 else if (propertyName ==
"Bank") {
647 alg->
setProperty(
"Bank", std::stoi(propertyValue) + periodNum - 1);
660 std::map<std::string, std::string> result;
664 result[
"InputWorkspace"] =
"The input workspace cannot be a GroupWorkspace.";
668 const auto nHist =
static_cast<int>(input_ws->getNumberHistograms());
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;
678 std::string output_file_name =
getProperty(
"Filename");
679 if (output_file_name.size() == 0) {
680 result[
"Filename"] =
"Filename cannot be left empty.";
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.";
707 assertNumFilesAndSpectraIsValid(numOutFiles, numSpectra);
709 const auto numOutFilesInt64 =
static_cast<int64_t
>(numOutFiles);
712 for (int64_t fileIndex = 0; fileIndex < numOutFilesInt64; fileIndex++) {
716 std::ofstream fileStream;
718 for (
size_t specIndex = 0; specIndex < numSpectra; specIndex++) {
720 const size_t index = specIndex + fileIndex;
726 if (fileStream.fail()) {
727 const std::string
error = strerror(errno);
729 throw std::runtime_error(
"Failed to close the file at " +
m_outFileNames[fileIndex] +
730 " - this file may be empty, corrupted or incorrect.");
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;
743 double bc4 = (xVals[1] - xVals[0]) / xVals[0];
744 if (!std::isfinite(bc4))
748 writeBankHeader(out,
"RALF", bank, datasize);
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";
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();
763 const size_t dataEntriesPerLine = 4;
767 const int64_t numberOfOutLines = (datasize + dataEntriesPerLine - 1) / dataEntriesPerLine;
769 std::vector<std::unique_ptr<std::stringstream>> outLines;
770 outLines.resize(numberOfOutLines);
772 for (int64_t i = 0; i < numberOfOutLines; i++) {
773 outLines[i] = makeStringStream();
774 auto &outLine = *outLines[i];
776 size_t dataPosition = i * dataEntriesPerLine;
777 const size_t endPosition = dataPosition + dataEntriesPerLine;
780 for (; dataPosition < endPosition; dataPosition++) {
781 if (dataPosition < datasize) {
784 const auto epos =
static_cast<int>(fixErrorValue(eVals[dataPosition] * 1000));
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;
795 for (
const auto &outLine : outLines) {
797 out << outLine->rdbuf();
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();
811 std::vector<std::unique_ptr<std::stringstream>> outLines;
812 outLines.resize(datasize);
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]);
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";
828 for (
const auto &outLine : outLines) {
830 out << outLine->rdbuf();
846 std::stringstream &out,
const HistogramData::Histogram &histo,
847 const std::vector<int> &xye_precision)
const {
849 if (xye_precision.size() != 3)
850 throw std::runtime_error(
"SLOG XYE precisions are not given in a 3-item vector.");
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();
858 const double bc1 = xVals.front();
859 const double bc2 = *(xPoints.end() - 1);
860 const double bc3 = (*(xVals.begin() + 1) - bc1) / bc1;
863 throw std::runtime_error(
"Cannot write out logarithmic data starting at zero or less");
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 " +
873 g_log.
debug() <<
"SaveGSS(): Min TOF = " << bc1 <<
'\n';
881 writeBankHeader(out,
"SLOG", bank, datasize);
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";
888 std::vector<std::unique_ptr<std::stringstream>> outLines;
889 outLines.resize(datasize);
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])};
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) <<
" "
907 for (
const auto &outLine : outLines) {
908 out << outLine->rdbuf();
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
std::map< DeltaEMode::Type, std::string > index
#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.
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.
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.
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,...
bool m_overwrite_std_gsas_header
flag to overwrite standard GSAS header
void init() override
Initialisation code.
std::map< std::string, std::string > validateInputs() override
Validates the user input and warns / throws on bad conditions.
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.
void processUserSpecifiedHeaders()
Process input user-specified headers.
void generateOutFileNames(size_t numberOfOutFiles)
Generates the filename(s) and paths to write to and stores in member var.
void generateInstrumentHeader(std::stringstream &out, double l1) const
Generates the instrument header and returns this as a string stream.
void writeBufferToFile(size_t numOutFiles, size_t numSpectra)
Writes the current buffer to the user specified file path.
void setOtherProperties(IAlgorithm *alg, const std::string &propertyName, const std::string &propertyValue, int periodNum) override
sets non workspace properties for the algorithm
const std::string name() const override
Algorithm's name.
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.
std::vector< std::string > m_user_specified_gsas_header
User specified header string.
API::MatrixWorkspace_const_sptr m_inputWS
Workspace.
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.
void writeRALF_ALTdata(std::stringstream &out, const int bank, const HistogramData::Histogram &histo) const
Write out the data in RALF - ALT format.
void openFileStream(const std::string &outFilePath, std::ofstream &outStream)
Opens a new file stream at the path specified.
std::vector< std::unique_ptr< std::stringstream > > m_outputBuffer
The output buffer.
std::unique_ptr< API::Progress > m_progress
Holds pointer to progress bar.
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.
std::vector< std::string > m_outFileNames
The output filename(s)
bool areAllDetectorsValid() const
Determines if all spectra have detectors.
std::vector< std::string > m_user_specified_bank_headers
User specified bank header.
void generateGSASBuffer(size_t numOutFiles, size_t numOutSpectra)
Generates the output which will be written to the GSAS file.
void generateBankHeader(std::stringstream &out, const API::SpectrumInfo &spectrumInfo, size_t specIndex) const
Generates the bank header and returns this as a string stream.
bool m_overwrite_std_bank_header
flag to overwrite standard GSAS bank header
void writeRALFHeader(std::stringstream &out, int bank, const HistogramData::Histogram &histo) const
bool isInstrumentValid() const
Returns if the input workspace instrument is valid.
bool m_allDetectorsValid
Indicates whether all spectra have valid detectors.
void exec() override
Execution code.
Support for a property that holds an array of values.
Records the filename and the description of failure.
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.
void error(const std::string &msg)
Logs at error level.
void warning(const std::string &msg)
Logs at warning level.
Base class for properties.
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.
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.