Mantid
Loading...
Searching...
No Matches
LoadCanSAS1D.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8#include "MantidAPI/Axis.h"
11#include "MantidAPI/Run.h"
12#include "MantidAPI/Sample.h"
19
20#include <Poco/AutoPtr.h>
21#include <Poco/DOM/DOMParser.h>
22#include <Poco/DOM/Document.h>
23#include <Poco/DOM/NodeList.h>
24#include <Poco/SAX/InputSource.h>
25
26using Poco::XML::Document;
27using Poco::XML::DOMParser;
28using Poco::XML::Element;
29using Poco::XML::Node;
30using Poco::XML::NodeList;
31
32using namespace Mantid::Kernel;
33using namespace Mantid::API;
34using namespace Mantid::DataObjects;
35
36namespace {
37int getGeometryID(const std::string &selection) {
38 int geometryID = 0;
39 if (selection == "Cylinder") {
40 geometryID = 1;
41 } else if (selection == "Flat plate") {
42 geometryID = 2;
43 } else if (selection == "Disc") {
44 geometryID = 3;
45 } else {
46 geometryID = 0;
47 }
48 return geometryID;
49}
50
59bool setLogFromElementIfNameIs(std::string const &searchName, Element *elem, Run &run, std::string const &logName) {
60 if (!elem)
61 return false;
62
63 const std::string termName = elem->getAttribute("name");
64 if (termName == searchName) {
65 std::string file = elem->innerText();
66 run.addLogData(new PropertyWithValue<std::string>(logName, file));
67 return true;
68 }
69
70 return false;
71}
72} // namespace
73
74namespace Mantid::DataHandling {
75
77
78
84int LoadCanSAS1D::confidence(Kernel::FileDescriptor &descriptor) const {
85 const std::string &extn = descriptor.extension();
86 if (extn != ".xml")
87 return 0;
88
89 std::istream &is = descriptor.data();
90 int confidence(0);
91
92 { // start of inner scope
93 Poco::XML::InputSource src(is);
94 // Set up the DOM parser and parse xml file
95 DOMParser pParser;
96 Poco::AutoPtr<Document> pDoc;
97 try {
98 pDoc = pParser.parse(&src);
99 } catch (...) {
100 throw Kernel::Exception::FileError("Unable to parse File:", descriptor.filename());
101 }
102 // Get pointer to root element
103 Element *pRootElem = pDoc->documentElement();
104 if (pRootElem) {
105 if (pRootElem->tagName() == "SASroot") {
106 confidence = 80;
107 }
108 }
109 } // end of inner scope
110
111 return confidence;
112}
113
116 declareProperty(std::make_unique<API::FileProperty>("Filename", "", API::FileProperty::Load, ".xml"),
117 "The name of the CanSAS1D file to load");
118 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("OutputWorkspace", "", Kernel::Direction::Output),
119 "The name to use for the output workspace");
120}
121
128 const std::string fileName = getPropertyValue("Filename");
129 // Set up the DOM parser and parse xml file
130 DOMParser pParser;
131 Poco::AutoPtr<Document> pDoc;
132 try {
133 pDoc = pParser.parse(fileName);
134 } catch (...) {
135 throw Exception::FileError("Unable to parse File:", fileName);
136 }
137 // Get pointer to root element
138 Element *pRootElem = pDoc->documentElement();
139 if (!pRootElem->hasChildNodes()) {
140 throw Kernel::Exception::NotFoundError("No root element in CanSAS1D XML file", fileName);
141 }
142 // there can be multiple <SASentry> elements, each one contains a period which
143 // will go into a workspace group if there are more than one of them
144 Poco::AutoPtr<NodeList> entryList = pRootElem->getElementsByTagName("SASentry");
145 size_t numEntries = entryList->length();
146 Workspace_sptr outputWork;
148 std::string runName;
149 switch (numEntries) {
150 case 0:
151 throw Exception::NotFoundError("No <SASentry>s were found in the file", fileName);
152 break;
153 case 1:
154 // the value of the string runName is unused in this case
155 WS = loadEntry(entryList->item(0), runName);
156 WS->mutableRun().addProperty("Filename", fileName);
157 outputWork = WS;
158 break;
159 default:
160 auto group = std::make_shared<WorkspaceGroup>();
161 for (unsigned int i = 0; i < numEntries; ++i) {
162 std::string run;
163 MatrixWorkspace_sptr newWork = loadEntry(entryList->item(i), run);
164 newWork->mutableRun().addProperty("Filename", fileName);
165 appendDataToOutput(newWork, run, group);
166 }
167 outputWork = group;
168 }
169 setProperty("OutputWorkspace", outputWork);
170}
178MatrixWorkspace_sptr LoadCanSAS1D::loadEntry(Poco::XML::Node *const workspaceData, std::string &runName) {
179 auto *workspaceElem = dynamic_cast<Element *>(workspaceData);
180 check(workspaceElem, "<SASentry>");
181 runName = workspaceElem->getAttribute("name");
182
183 Poco::AutoPtr<NodeList> runs = workspaceElem->getElementsByTagName("Run");
184 if (runs->length() != 1) {
185 throw Exception::NotImplementedError("<SASentry>s containing multiple "
186 "runs, or no runs, are not currently "
187 "supported");
188 }
189
190 Element *sasDataElem = workspaceElem->getChildElement("SASdata");
191 check(sasDataElem, "<SASdata>");
192 // getting number of Idata elements in the xml file
193 Poco::AutoPtr<NodeList> idataElemList = sasDataElem->getElementsByTagName("Idata");
194 size_t nBins = idataElemList->length();
195
196 MatrixWorkspace_sptr dataWS = WorkspaceFactory::Instance().create("Workspace2D", 1, nBins, nBins);
197
198 createLogs(workspaceElem, dataWS);
199
200 Element *titleElem = workspaceElem->getChildElement("Title");
201 check(titleElem, "<Title>");
202 dataWS->setTitle(titleElem->innerText());
203 dataWS->setDistribution(true);
204 dataWS->setYUnit("");
205
206 // load workspace data
207 auto &X = dataWS->mutableX(0);
208 auto &Y = dataWS->mutableY(0);
209 auto &E = dataWS->mutableE(0);
210
211 dataWS->setPointStandardDeviations(0, nBins);
212 auto &Dx = dataWS->mutableDx(0);
213 int vecindex = 0;
214 std::string yUnit;
215 bool isCommon = true;
216 // iterate through each Idata element and get the values of "Q",
217 //"I" and "Idev" text nodes and fill X,Y,E vectors
218 for (unsigned long index = 0; index < nBins; ++index) {
219 Node *idataElem = idataElemList->item(index);
220 auto *elem = dynamic_cast<Element *>(idataElem);
221 if (elem) {
222 // setting X vector
223 std::string nodeVal;
224 Element *qElem = elem->getChildElement("Q");
225 check(qElem, "Q");
226 nodeVal = qElem->innerText();
227 std::stringstream x(nodeVal);
228 double d;
229 x >> d;
230 X[vecindex] = d;
231
232 // setting dX vector [optional]
233 Element *dqElem = elem->getChildElement("Qdev");
234 if (dqElem) {
235 nodeVal = dqElem->innerText();
236 std::stringstream dx(nodeVal);
237 dx >> d;
238 Dx[vecindex] = d;
239 }
240
241 // setting Y vector
242 Element *iElem = elem->getChildElement("I");
243 check(qElem, "I");
244 const std::string unit = iElem->getAttribute("unit");
245 if (index == 0)
246 yUnit = unit;
247 else if (unit != yUnit)
248 isCommon = false;
249 nodeVal = iElem->innerText();
250 std::stringstream y(nodeVal);
251 y >> d;
252 Y[vecindex] = d;
253
254 // setting the error vector
255 // If there is no error of the intensity recorded, then
256 // it is assumed to be the sqare root of the intensity
257 Element *idevElem = elem->getChildElement("Idev");
258 if (idevElem) {
259 check(qElem, "Idev");
260 nodeVal = idevElem->innerText();
261 std::stringstream e(nodeVal);
262 e >> d;
263 E[vecindex] = d;
264 } else {
265 E[vecindex] = std::sqrt(d);
266 }
267
268 ++vecindex;
269 }
270 }
271
272 Element *instrElem = workspaceElem->getChildElement("SASinstrument");
273 check(instrElem, "SASinstrument");
274 std::string instname;
275 Element *nameElem = instrElem->getChildElement("name");
276 check(nameElem, "name");
277 instname = nameElem->innerText();
278 // run load instrument
279 runLoadInstrument(instname, dataWS);
280
281 // Load the sample information
282 createSampleInformation(workspaceElem, dataWS);
283
284 dataWS->getAxis(0)->setUnit("MomentumTransfer");
285 if (isCommon)
286 dataWS->setYUnitLabel(yUnit);
287 return dataWS;
288}
289/* This method throws not found error if a element is not found in the xml file
290 * @param[in] toCheck pointer to element
291 * @param[in] name element name
292 * @throw NotFoundError if the pointer is NULL
293 */
294void LoadCanSAS1D::check(const Poco::XML::Element *const toCheck, const std::string &name) const {
295 if (!toCheck) {
296 std::string fileName = getPropertyValue("Filename");
297 throw Kernel::Exception::NotFoundError("<" + name + "> element not found in CanSAS1D XML file", fileName);
298 }
299}
309void LoadCanSAS1D::appendDataToOutput(const API::MatrixWorkspace_sptr &newWork, const std::string &newWorkName,
310 const API::WorkspaceGroup_sptr &container) {
311 // the name of the property, like the workspace name must be different for
312 // each workspace. Add "_run" at the end to stop problems with names like
313 // "outputworkspace"
314 std::string propName = newWorkName + "_run";
315
316 // the following code registers the workspace with the AnalysisDataService and
317 // with the workspace group, I'm taking this oone trust I don't know why it's
318 // done this way sorry, Steve
319 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>(propName, newWorkName, Direction::Output));
320 container->addWorkspace(newWork);
321 setProperty(propName, newWork);
322}
327void LoadCanSAS1D::runLoadInstrument(const std::string &inst_name, const API::MatrixWorkspace_sptr &localWorkspace) {
328
329 auto loadInst = createChildAlgorithm("LoadInstrument");
330
331 // Now execute the Child Algorithm. Catch and log any error, but don't stop.
332 try {
333 loadInst->setPropertyValue("InstrumentName", inst_name);
334 loadInst->setProperty<API::MatrixWorkspace_sptr>("Workspace", localWorkspace);
335 loadInst->setProperty("RewriteSpectraMap", Mantid::Kernel::OptionalBool(true));
336 loadInst->execute();
337 } catch (std::invalid_argument &) {
338 g_log.information("Invalid argument to LoadInstrument Child Algorithm");
339 } catch (std::runtime_error &) {
340 g_log.information("Unable to successfully run LoadInstrument Child Algorithm");
341 }
342}
343
348void LoadCanSAS1D::createLogs(const Poco::XML::Element *const sasEntry, const API::MatrixWorkspace_sptr &wSpace) const {
349 API::Run &run = wSpace->mutableRun();
350 Element *runText = sasEntry->getChildElement("Run");
351 check(runText, "Run");
352 run.addLogData(new PropertyWithValue<std::string>("run_number", runText->innerText()));
353
354 Element *process = sasEntry->getChildElement("SASprocess");
355 if (process) {
356 Poco::AutoPtr<NodeList> terms = process->getElementsByTagName("term");
357 auto setUserFile = false;
358 auto setBatchFile = false;
359 for (unsigned int i = 0; i < terms->length() && (!setUserFile || !setBatchFile); ++i) {
360 Node *term = terms->item(i);
361 auto *elem = dynamic_cast<Element *>(term);
362 if (!setUserFile && setLogFromElementIfNameIs("user_file", elem, run, "UserFile"))
363 setUserFile = true;
364 else if (!setBatchFile && setLogFromElementIfNameIs("batch_file", elem, run, "BatchFile"))
365 setBatchFile = true;
366 }
367 }
368}
369
370void LoadCanSAS1D::createSampleInformation(const Poco::XML::Element *const sasEntry,
371 const Mantid::API::MatrixWorkspace_sptr &wSpace) const {
372 auto &sample = wSpace->mutableSample();
373
374 // Get the thickness information
375 auto sasSampleElement = sasEntry->getChildElement("SASsample");
376 check(sasSampleElement, "<SASsample>");
377 auto thicknessElement = sasSampleElement->getChildElement("thickness");
378 if (thicknessElement) {
379 double thickness = std::stod(thicknessElement->innerText());
380 sample.setThickness(thickness);
381 }
382
383 auto sasInstrumentElement = sasEntry->getChildElement("SASinstrument");
384 check(sasInstrumentElement, "<SASinstrument>");
385 auto sasCollimationElement = sasInstrumentElement->getChildElement("SAScollimation");
386 check(sasCollimationElement, "<SAScollimation>");
387
388 // Since we have shipped a sligthly invalid CanSAS1D format we need to
389 // make sure that we can read those files back in again
390 bool isInValidOldFormat = true;
391 try {
392 auto name = sasCollimationElement->getChildElement("name");
393 check(name, "name");
395 isInValidOldFormat = false;
396 }
397
398 if (isInValidOldFormat) {
399 // Get the geometry information
400 auto geometryElement = sasCollimationElement->getChildElement("name");
401 if (geometryElement) {
402 auto geometry = geometryElement->innerText();
403 auto geometryID = getGeometryID(geometry);
404 sample.setGeometryFlag(geometryID);
405 }
406
407 // Get the width information
408 auto widthElement = sasCollimationElement->getChildElement("X");
409 if (widthElement) {
410 double width = std::stod(widthElement->innerText());
411 sample.setWidth(width);
412 }
413
414 // Get the height information
415 auto heightElement = sasCollimationElement->getChildElement("Y");
416 if (heightElement) {
417 double height = std::stod(heightElement->innerText());
418 sample.setHeight(height);
419 }
420
421 } else {
422 // Get aperture
423 auto aperture = sasCollimationElement->getChildElement("aperture");
424 if (aperture) {
425 // Get geometry element
426 auto geometry = aperture->getAttribute("name");
427 if (!geometry.empty()) {
428 auto geometryID = getGeometryID(Poco::XML::fromXMLString(geometry));
429 sample.setGeometryFlag(geometryID);
430 }
431
432 // Get size
433 auto size = aperture->getChildElement("size");
434
435 // Get the width information
436 auto widthElement = size->getChildElement("x");
437 if (widthElement) {
438 double width = std::stod(widthElement->innerText());
439 sample.setWidth(width);
440 }
441
442 // Get the height information
443 auto heightElement = size->getChildElement("y");
444 if (heightElement) {
445 double height = std::stod(heightElement->innerText());
446 sample.setHeight(height);
447 }
448 }
449 }
450}
451} // namespace Mantid::DataHandling
double height
Definition: GetAllEi.cpp:155
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
#define DECLARE_FILELOADER_ALGORITHM(classname)
DECLARE_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro when wri...
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
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Definition: Algorithm.cpp:842
@ Load
allowed here which will be passed to the algorithm
Definition: FileProperty.h:52
void addLogData(Kernel::Property *p)
Add a log entry.
Definition: LogManager.h:115
This class stores information regarding an experimental run as a series of log entries.
Definition: Run.h:38
A property class for workspaces.
This algorithm loads 1 CanSAS1d xml file into a workspace.
Definition: LoadCanSAS1D.h:39
void exec() override
Overwrites Algorithm method.
const std::string name() const override
Algorithm's name for identification overriding a virtual method.
Definition: LoadCanSAS1D.h:42
void runLoadInstrument(const std::string &inst_name, const API::MatrixWorkspace_sptr &localWorkspace)
Run LoadInstrument Child Algorithm.
void init() override
Overwrites Algorithm method.
void check(const Poco::XML::Element *const toCheck, const std::string &name) const
Checks if the pointer to the loaded data is not null or throws if it is.
void appendDataToOutput(const API::MatrixWorkspace_sptr &newWork, const std::string &newWorkName, const API::WorkspaceGroup_sptr &container)
Appends the new data workspace creating a workspace group if there was existing data.
void createLogs(const Poco::XML::Element *const sasEntry, const API::MatrixWorkspace_sptr &wSpace) const
Loads data into the run log.
virtual API::MatrixWorkspace_sptr loadEntry(Poco::XML::Node *const workspaceData, std::string &runName)
Loads an individual SASentry element into a new workspace.
void createSampleInformation(const Poco::XML::Element *const sasEntry, const Mantid::API::MatrixWorkspace_sptr &wSpace) const
Loads the information about hhe sample.
Records the filename and the description of failure.
Definition: Exception.h:98
Exception for when an item is not found in a collection.
Definition: Exception.h:145
Marks code as not implemented yet.
Definition: Exception.h:138
Defines a wrapper around an open file.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
OptionalBool : Tri-state bool.
Definition: OptionalBool.h:25
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Definition: Workspace_fwd.h:20
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
@ Output
An output workspace.
Definition: Property.h:54