21#include <boost/algorithm/string/split.hpp>
22#include <boost/algorithm/string/trim.hpp>
28void encode(std::string &data) {
30 buffer.reserve(data.size());
32 for (
auto &element : data) {
35 buffer.append(
"&");
38 buffer.append(
""");
41 buffer.append(
"'");
44 buffer.append(
"<");
47 buffer.append(
">");
50 buffer.push_back(element);
71 std::make_shared<API::WorkspaceUnitValidator>(
"MomentumTransfer")),
72 "The input workspace, which must be in units of Q");
74 "The name of the xml file to save");
76 std::vector<std::string> radiation_source{
"Spallation Neutron Source",
77 "Pulsed Reactor Neutron Source",
78 "Reactor Neutron Source",
79 "Synchrotron X-ray Source",
81 "Rotating Anode X-ray",
87 declareProperty(
"RadiationSource",
"Spallation Neutron Source",
88 std::make_shared<Kernel::StringListValidator>(radiation_source),
"The type of radiation used.");
89 declareProperty(
"Append",
false,
90 "Selecting append allows the workspace to "
91 "be added to an existing canSAS 1-D file as "
93 declareProperty(
"Process",
"",
"Text to append to Process section");
94 declareProperty(
"DetectorNames",
"",
95 "Specify in a comma separated list, which detectors to store "
96 "information about; \nwhere each name must match a name "
97 "given for a detector in the [[IDF|instrument definition "
98 "file (IDF)]]. \nIDFs are located in the instrument "
99 "sub-directory of the MantidPlot install directory.");
102 std::vector<std::string> collimationGeometry{
107 declareProperty(
"Geometry",
"Disc", std::make_shared<Kernel::StringListValidator>(collimationGeometry),
108 "The geometry type of the collimation.");
109 auto mustBePositiveOrZero = std::make_shared<Kernel::BoundedValidator<double>>();
110 mustBePositiveOrZero->setLower(0);
111 declareProperty(
"SampleHeight", 0.0, mustBePositiveOrZero,
112 "The height of the collimation element in mm. If specified "
113 "as 0 it will not be recorded.");
114 declareProperty(
"SampleWidth", 0.0, mustBePositiveOrZero,
115 "The width of the collimation element in mm. If specified as "
116 "0 it will not be recorded.");
119 declareProperty(
"SampleThickness", 0.0, mustBePositiveOrZero,
120 "The thickness of the sample in mm. If specified as 0 it "
121 "will not be recorded.");
131 const std::string &propertyValue,
int perioidNum) {
135 if ((propertyName ==
"Append") && (perioidNum > 1)) {
143 throw std::invalid_argument(
"Invalid inputworkspace ,Error in SaveCanSAS1D");
147 throw std::invalid_argument(
"Error in SaveCanSAS1D - more than one histogram.");
157 std::string sasTitle;
173 std::string sasSample;
179 std::string sasInstrument;
184 }
catch (std::runtime_error &) {
189 std::string sasProcess;
193 std::string sasNote =
"\n\t\t<SASnote>";
194 sasNote +=
"\n\t\t</SASnote>";
210 m_outFile.exceptions(std::ios::eofbit | std::ios::failbit | std::ios::badbit);
233 m_outFile.open(filename.c_str(), std::ios::out | std::ios::in);
241 }
catch (std::fstream::failure &) {
242 g_log.
information() <<
"File " << filename <<
" couldn't be opened for a appending, will try to create the file\n";
256 const int rootTagLen =
static_cast<int>(std::string(
"</SASroot>").length());
259 static const int LAST_TAG_LEN = 11;
263 m_outFile.seekg(-LAST_TAG_LEN - rootTagLen, std::ios::end);
264 char test_tag[LAST_TAG_LEN + 1];
267 static const char LAST_TAG[LAST_TAG_LEN + 1] =
"</SASentry>";
268 if (std::string(test_tag, LAST_TAG_LEN) != std::string(LAST_TAG, LAST_TAG_LEN)) {
271 bool tagFound(
false);
273 static const int UNCERT = 20;
274 for (
int i = 1; i < UNCERT; ++i) {
277 m_outFile.seekg(-i - LAST_TAG_LEN - rootTagLen, std::ios::end);
279 std::string read = std::string(test_tag, LAST_TAG_LEN);
280 if (read == std::string(LAST_TAG, LAST_TAG_LEN)) {
286 throw std::logic_error(
"Couldn't find the end of the existing data, "
287 "missing </SASentry> tag");
292 }
catch (std::fstream::failure &) {
294 throw std::logic_error(
"Trouble reading existing data in the output file, "
295 "are you appending to an invalid CanSAS1D file?");
305 m_outFile.open(fileName.c_str(), std::ios::out | std::ios::trunc);
308 <<
"<?xml-stylesheet type=\"text/xsl\" "
309 "href=\"cansasxml-html.xsl\" ?>\n";
313 }
catch (std::fstream::failure &) {
322 std::string specialchars =
"&<>'\"";
323 std::string::size_type searchIndex = 0;
324 std::string::size_type findIndex;
325 for (
char specialchar : specialchars) {
326 while (searchIndex < input.length()) {
327 findIndex = input.find(specialchar, searchIndex);
328 if (findIndex != std::string::npos) {
329 searchIndex = findIndex + 1;
347 std::basic_string<char>::reference str = input.at(
index);
350 input.replace(
index, 1,
"&");
353 input.replace(
index, 1,
"<");
356 input.replace(
index, 1,
">");
359 input.replace(
index, 1,
"'");
362 input.replace(
index, 1,
""");
371 rootElem =
"<SASroot version=\"1.0\"";
372 rootElem +=
"\n\t\t xmlns=\"cansas1d/1.0\"";
373 rootElem +=
"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
374 rootElem +=
"\n\t\t xsi:schemaLocation=\"cansas1d/1.0 "
375 "http://svn.smallangles.net/svn/canSAS/1dwg/trunk/"
386 sasTitle =
"\n\t\t<Title>";
388 sasTitle +=
"</Title>";
398 if (
m_workspace->run().hasProperty(
"run_number")) {
402 g_log.
debug() <<
"Didn't find RunNumber log in workspace. Writing "
403 "<Run></Run> to the CANSAS file\n";
408 sasRun =
"\n\t\t<Run>";
429 if (dataUnit ==
"I(q) (cm-1)")
432 const auto intensities =
m_workspace->points(workspaceIndex);
433 auto intensityDeltas =
m_workspace->pointStandardDeviations(workspaceIndex);
434 if (!intensityDeltas)
435 intensityDeltas = HistogramData::PointStandardDeviations(intensities.size(), 0.0);
436 const auto &ydata =
m_workspace->y(workspaceIndex);
437 const auto &edata =
m_workspace->e(workspaceIndex);
438 sasData +=
"\n\t\t<SASdata>";
439 for (
size_t j = 0; j < ydata.size(); ++j) {
443 std::stringstream dx_str;
444 dx_str << intensityDeltas[j];
445 sasData +=
"\n\t\t\t<Idata><Q unit=\"1/A\">";
448 sasData +=
"<I unit=";
462 sasData +=
"<Idev unit=";
468 sasData +=
"</Idev>";
470 sasData +=
"<Qdev unit=\"1/A\">";
471 sasData += dx_str.str();
472 sasData +=
"</Qdev>";
474 sasData +=
"</Idata>";
476 sasData +=
"\n\t\t</SASdata>";
483 sasSample =
"\n\t\t<SASsample>";
485 std::string sasSampleId =
"\n\t\t\t<ID>";
489 sasSampleId += sampleid;
490 sasSampleId +=
"</ID>";
491 sasSample += sasSampleId;
496 std::string thicknessTag =
"\n\t\t\t<thickness unit=\"mm\">";
498 thicknessTag +=
"</thickness>";
499 sasSample += thicknessTag;
501 sasSample +=
"\n\t\t</SASsample>";
508 sasSource =
"\n\t\t\t<SASsource>";
512 std::string sasrad =
"\n\t\t\t\t<radiation>";
513 sasrad += radiation_source;
514 sasrad +=
"</radiation>";
517 sasSource +=
"\n\t\t\t</SASsource>";
524 const std::string detectorNames =
getProperty(
"DetectorNames");
526 if (detectorNames.empty()) {
527 sasDet +=
"\n\t\t\t<SASdetector>";
528 std::string sasDetname =
"\n\t\t\t\t<name/>";
529 sasDet += sasDetname;
530 sasDet +=
"\n\t\t\t</SASdetector>";
534 std::list<std::string> detList;
535 using std::placeholders::_1;
536 boost::algorithm::split(detList, detectorNames, std::bind(std::equal_to<char>(), _1,
','));
537 for (
auto detectorName : detList) {
538 boost::algorithm::trim(detectorName);
541 std::shared_ptr<const IComponent> comp =
m_workspace->getInstrument()->getComponentByName(detectorName);
542 if (comp != std::shared_ptr<const IComponent>()) {
543 sasDet +=
"\n\t\t\t<SASdetector>";
545 std::string sasDetname =
"\n\t\t\t\t<name>";
546 sasDetname += detectorName;
547 sasDetname +=
"</name>";
548 sasDet += sasDetname;
550 std::string sasDetUnit =
"\n\t\t\t\t<SDD unit=\"m\">";
552 std::stringstream sdd;
553 double distance = comp->getDistance(*
m_workspace->getInstrument()->getSample());
556 sasDetUnit += sdd.str();
557 sasDetUnit +=
"</SDD>";
559 sasDet += sasDetUnit;
560 sasDet +=
"\n\t\t\t</SASdetector>";
562 g_log.
notice() <<
"Detector with name " << detectorName
563 <<
" does not exist in the instrument of the workspace: " <<
m_workspace->getName() <<
'\n';
572 sasProcess =
"\n\t\t<SASprocess>";
575 std::string sasProcname =
"\n\t\t\t<name>";
576 sasProcname +=
"Mantid generated CanSAS1D XML";
577 sasProcname +=
"</name>";
578 sasProcess += sasProcname;
584 strftime(temp, 25,
"%d-%b-%Y %H:%M:%S", localtime(&rawtime));
585 std::string sasDate(temp);
587 std::string sasProcdate =
"\n\t\t\t<date>";
588 sasProcdate += sasDate;
589 sasProcdate +=
"</date>";
590 sasProcess += sasProcdate;
592 std::string sasProcsvn =
"\n\t\t\t<term name=\"svn\">";
594 sasProcsvn +=
"</term>";
595 sasProcess += sasProcsvn;
598 std::string user_file;
603 std::string sasProcuserfile =
"\n\t\t\t<term name=\"user_file\">";
604 sasProcuserfile += user_file;
605 sasProcuserfile +=
"</term>";
607 sasProcess += sasProcuserfile;
611 if (!process_xml.empty()) {
612 std::string processNote =
"\n\t\t\t<SASprocessnote>";
614 processNote += process_xml;
615 processNote +=
"</SASprocessnote>";
616 sasProcess += processNote;
618 sasProcess +=
"\n\t\t\t<SASprocessnote/>";
621 sasProcess +=
"\n\t\t</SASprocess>";
644 sasInstrument =
"\n\t\t<SASinstrument>";
647 std::string sasInstrName =
"\n\t\t\t<name>";
648 std::string instrname =
m_workspace->getInstrument()->getName();
651 sasInstrName += instrname;
652 sasInstrName +=
"</name>";
653 sasInstrument += sasInstrName;
656 std::string sasSource;
658 sasInstrument += sasSource;
663 double collimationHeight =
getProperty(
"SampleHeight");
664 double collimationWidth =
getProperty(
"SampleWidth");
665 std::string sasCollimation =
"\n\t\t\t<SAScollimation/>";
666 if (collimationHeight > 0 || collimationWidth > 0) {
667 sasCollimation =
"\n\t\t\t<SAScollimation>";
670 std::string collimationGeometry =
getProperty(
"Geometry");
671 sasCollimation +=
"\n\t\t\t\t<aperture name=\"" + collimationGeometry +
"\">";
674 sasCollimation +=
"\n\t\t\t\t\t<size>";
677 sasCollimation +=
"\n\t\t\t\t\t\t<x unit=\"mm\">" +
std::to_string(collimationWidth) +
"</x>";
679 sasCollimation +=
"\n\t\t\t\t\t\t<y unit=\"mm\">" +
std::to_string(collimationHeight) +
"</y>";
681 sasCollimation +=
"\n\t\t\t\t\t</size>";
682 sasCollimation +=
"\n\t\t\t\t</aperture>";
683 sasCollimation +=
"\n\t\t\t</SAScollimation>";
685 sasInstrument += sasCollimation;
690 sasInstrument += sasDet;
691 sasInstrument +=
"\n\t\t</SASinstrument>";
#define DECLARE_ALGORITHM(classname)
std::map< DeltaEMode::Type, std::string > index
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 * getLogData(const std::string &name) const
Access a single log entry.
This class stores information regarding an experimental run as a series of log entries.
A property class for workspaces.
bool openForAppending(const std::string &filename)
opens the named file if possible or returns false
void createSASSourceElement(std::string &sasSource)
this method creates a sasSource element
std::fstream m_outFile
an fstream object is used to write the xml manually as the user requires a specific format with new l...
void findEndofLastEntry()
Moves to the end of the last entry in the file.
void createSASTitleElement(std::string &sasTitle)
this method creates a sasTitle element
void createSASInstrument(std::string &sasInstrument)
this method creates a sasInstrument element
virtual void createSASRootElement(std::string &rootElem)
sasroot element
void setOtherProperties(API::IAlgorithm *alg, const std::string &propertyName, const std::string &propertyValue, int perioidNum) override
overriden method sets appending for workspace groups
void searchandreplaceSpecialChars(std::string &input)
this method searches for xml special characters and replace with entity references
void createSASProcessElement(std::string &sasProcess)
this method creates a sasProcess element
void createSASDataElement(std::string &sasData, size_t workspaceIndex)
this method creates a sasData element
void createSASDetectorElement(std::string &sasDet)
this method creates a sasDetector element
void createSASSampleElement(std::string &sasSample)
this method creates a sasSample element
virtual void writeHeader(const std::string &fileName)
Write xml header tags.
void prepareFileToWriteEntry(const std::string &fileName)
Opens the output file and, as necessary blanks it, writes the file header and moves the file pointer.
void createSASRunElement(std::string &sasRun)
this method creates a sasRun Element
API::MatrixWorkspace_const_sptr m_workspace
points to the workspace that will be written to file
void replacewithEntityReference(std::string &input, const std::string::size_type &index)
replaces the charcter at index in the input string with xml entity reference(eg.replace '&' with "&")
void exec() override
Overwrites Algorithm method.
Records the filename and the description of failure.
Exception for when an item is not found in a collection.
virtual void setPropertyValue(const std::string &name, const std::string &value)=0
Sets property value from a string.
void debug(const std::string &msg)
Logs at debug level.
void notice(const std::string &msg)
Logs at notice level.
void information(const std::string &msg)
Logs at information level.
static const char * version()
The full version number.
Base class for properties.
virtual std::string value() const =0
Returns the value of the property as a string.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.