Mantid
Loading...
Searching...
No Matches
LoadNXSPE.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 +
13#include "MantidAPI/Run.h"
19
21// clang-format off
22#include <nexus/NeXusFile.hpp>
23#include <nexus/NeXusException.hpp>
24// clang-format on
25
30
31#include <boost/regex.hpp>
32
33#include <Poco/File.h>
34
35#include <map>
36#include <sstream>
37#include <string>
38#include <vector>
39
40namespace Mantid::DataHandling {
41
43
44using namespace Mantid::Kernel;
45using namespace Mantid::API;
46using Mantid::HistogramData::BinEdges;
47
54int LoadNXSPE::identiferConfidence(const std::string &value) {
55 int confidence = 0;
56 if (value == "NXSPE") {
57 confidence = 99;
58 } else {
59 boost::regex re("^NXSP", boost::regex::icase);
60 if (boost::regex_match(value, re)) {
61 confidence = 95;
62 }
63 }
64 return confidence;
65}
66
74 int confidence(0);
75 using string_map_t = std::map<std::string, std::string>;
76 try {
77 ::NeXus::File file = ::NeXus::File(descriptor.filename());
78 string_map_t entries = file.getEntries();
79 for (string_map_t::const_iterator it = entries.begin(); it != entries.end(); ++it) {
80 if (it->second == "NXentry") {
81 file.openGroup(it->first, it->second);
82 file.openData("definition");
83 const std::string value = file.getStrData();
85 }
86 }
87 } catch (::NeXus::Exception &) {
88 }
89 return confidence;
90}
91
95 const std::vector<std::string> exts{".nxspe", ""};
96 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, exts), "An NXSPE file");
97 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
98 "The name of the workspace that will be created.");
99}
100
104 std::string filename = getProperty("Filename");
105 // quicly check if it's really nxspe
106 try {
107 ::NeXus::File file(filename);
108 std::string mainEntry = (*(file.getEntries().begin())).first;
109 file.openGroup(mainEntry, "NXentry");
110 file.openData("definition");
111 if (identiferConfidence(file.getStrData()) < 1) {
112 throw std::invalid_argument("Not NXSPE");
113 }
114 file.close();
115 } catch (...) {
116 throw std::invalid_argument("Not NeXus or not NXSPE");
117 }
118
119 // Load the data
120 ::NeXus::File file(filename);
121
122 std::string mainEntry = (*(file.getEntries().begin())).first;
123 file.openGroup(mainEntry, "NXentry");
124
125 file.openGroup("NXSPE_info", "NXcollection");
126 std::map<std::string, std::string> entries = file.getEntries();
127 std::vector<double> temporary;
128 double fixed_energy, psi = 0.;
129
130 if (!entries.count("fixed_energy")) {
131 throw std::invalid_argument("fixed_energy field was not found");
132 }
133 file.openData("fixed_energy");
134 file.getData(temporary);
135 fixed_energy = temporary.at(0);
136 file.closeData();
137
138 if (entries.count("psi")) {
139 file.openData("psi");
140 file.getData(temporary);
141 psi = temporary.at(0);
142 if (std::isnan(psi)) {
143 psi = 0.;
144 g_log.warning("Entry for PSI is empty, will use default of 0.0 instead.");
145 }
146 file.closeData();
147 }
148
149 int kikfscaling = 0;
150 if (entries.count("ki_over_kf_scaling")) {
151 file.openData("ki_over_kf_scaling");
152 std::vector<int> temporaryint;
153 file.getData(temporaryint);
154 kikfscaling = temporaryint.at(0);
155 file.closeData();
156 }
157
158 file.closeGroup(); // NXSPE_Info
159
160 file.openGroup("data", "NXdata");
161 entries = file.getEntries();
162
163 if (!entries.count("data")) {
164 throw std::invalid_argument("data field was not found");
165 }
166 file.openData("data");
167 ::NeXus::Info info = file.getInfo();
168 auto numSpectra = static_cast<std::size_t>(info.dims.at(0));
169 auto numBins = static_cast<std::size_t>(info.dims.at(1));
170 std::vector<double> data;
171 file.getData(data);
172 file.closeData();
173
174 if (!entries.count("error")) {
175 throw std::invalid_argument("error field was not found");
176 }
177 file.openData("error");
178 std::vector<double> error;
179 file.getData(error);
180 file.closeData();
181
182 if (!entries.count("energy")) {
183 throw std::invalid_argument("energy field was not found");
184 }
185 file.openData("energy");
186 std::vector<double> energies;
187 file.getData(energies);
188 file.closeData();
189
190 if (!entries.count("azimuthal")) {
191 throw std::invalid_argument("azimuthal field was not found");
192 }
193 file.openData("azimuthal");
194 std::vector<double> azimuthal;
195 file.getData(azimuthal);
196 file.closeData();
197
198 if (!entries.count("azimuthal_width")) {
199 throw std::invalid_argument("azimuthal_width field was not found");
200 }
201 file.openData("azimuthal_width");
202 std::vector<double> azimuthal_width;
203 file.getData(azimuthal_width);
204 file.closeData();
205
206 if (!entries.count("polar")) {
207 throw std::invalid_argument("polar field was not found");
208 }
209 file.openData("polar");
210 std::vector<double> polar;
211 file.getData(polar);
212 file.closeData();
213
214 if (!entries.count("polar_width")) {
215 throw std::invalid_argument("polar_width field was not found");
216 }
217 file.openData("polar_width");
218 std::vector<double> polar_width;
219 file.getData(polar_width);
220 file.closeData();
221
222 // distance might not have been saved in all NXSPE files
223 std::vector<double> distance;
224 if (entries.count("distance")) {
225 file.openData("distance");
226 file.getData(distance);
227 file.closeData();
228 }
229
230 file.closeGroup(); // data group
231
232 file.openGroup("instrument", "NXinstrument");
233 entries = file.getEntries();
234 std::string instrument_name;
235 if (entries.count("name")) {
236 file.openData("name");
237 instrument_name = file.getStrData();
238 file.closeData();
239 }
240 file.closeGroup(); // instrument group
241
242 file.closeGroup(); // Main entry
243 file.close();
244
245 // check if dimensions of the vectors are correct
246 if ((error.size() != data.size()) || (azimuthal.size() != numSpectra) || (azimuthal_width.size() != numSpectra) ||
247 (polar.size() != numSpectra) || (polar_width.size() != numSpectra) ||
248 ((energies.size() != numBins) && (energies.size() != numBins + 1))) {
249 throw std::invalid_argument("incompatible sizes of fields in the NXSPE file");
250 }
251
252 MatrixWorkspace_sptr outputWS = std::dynamic_pointer_cast<MatrixWorkspace>(
253 WorkspaceFactory::Instance().create("Workspace2D", numSpectra, energies.size(), numBins));
254 // Need to get hold of the parameter map
255 outputWS->getAxis(0)->unit() = UnitFactory::Instance().create("DeltaE");
256 outputWS->setYUnit("SpectraNumber");
257
258 // add logs
259 outputWS->mutableRun().addLogData(new PropertyWithValue<double>("Ei", fixed_energy));
260 outputWS->mutableRun().addLogData(new PropertyWithValue<double>("psi", psi));
261 outputWS->mutableRun().addLogData(
262 new PropertyWithValue<std::string>("ki_over_kf_scaling", kikfscaling == 1 ? "true" : "false"));
263
264 // Set Goniometer
266 gm.pushAxis("psi", 0, 1, 0, psi);
267 outputWS->mutableRun().setGoniometer(gm, true);
268
269 // generate instrument
270 Geometry::Instrument_sptr instrument(new Geometry::Instrument(instrument_name.empty() ? "NXSPE" : instrument_name));
271
272 Geometry::ObjComponent *source = new Geometry::ObjComponent("source");
273 source->setPos(0.0, 0.0, -10.);
274 instrument->add(source);
275 instrument->markAsSource(source);
276 Geometry::Component *sample = new Geometry::Component("sample");
277 instrument->add(sample);
278 instrument->markAsSamplePos(sample);
279
280 constexpr double deg2rad = M_PI / 180.0;
281
282 for (std::size_t i = 0; i < numSpectra; ++i) {
283 double r = 1.0;
284 if (!distance.empty()) {
285 r = distance.at(i);
286 }
287
288 Kernel::V3D pos;
289 pos.spherical(r, polar.at(i), azimuthal.at(i));
290 // Define the size of the detector using the minimum of the polar or azimuthal width
291 double rr = r * sin(std::min(std::abs(polar_width.at(i)), std::abs(azimuthal_width.at(i))) * deg2rad / 2);
292 const auto shape = Geometry::ShapeFactory().createSphere(V3D(0, 0, 0), std::max(rr, 0.01));
293
294 Geometry::Detector *det = new Geometry::Detector("pixel", static_cast<int>(i + 1), sample);
295 det->setPos(pos);
296 det->setShape(shape);
297 instrument->add(det);
298 instrument->markAsDetector(det);
299 }
300 outputWS->setInstrument(instrument);
301
302 std::vector<double>::iterator itdata = data.begin(), iterror = error.begin(), itdataend, iterrorend;
303 auto &spectrumInfo = outputWS->mutableSpectrumInfo();
304 API::Progress prog = API::Progress(this, 0.0, 0.9, numSpectra);
305 BinEdges edges(std::move(energies));
306 for (std::size_t i = 0; i < numSpectra; ++i) {
307 itdataend = itdata + numBins;
308 iterrorend = iterror + numBins;
309 outputWS->getSpectrum(i).setDetectorID(static_cast<detid_t>(i + 1));
310 outputWS->setBinEdges(i, edges);
311 if ((!std::isfinite(*itdata)) || (*itdata <= -1e10)) // masked bin
312 {
313 spectrumInfo.setMasked(i, true);
314 } else {
315 outputWS->mutableY(i).assign(itdata, itdataend);
316 outputWS->mutableE(i).assign(iterror, iterrorend);
317 }
318 itdata = (itdataend);
319 iterror = (iterrorend);
320 prog.report();
321 }
322
323 // If an instrument name is defined, load instrument parameter file for Emode
324 // NB. LoadParameterFile must be used on a workspace with an instrument
325 if (!instrument_name.empty() && instrument_name != "NXSPE") {
326 std::string IDF_filename = InstrumentFileFinder::getInstrumentFilename(instrument_name);
327 std::string instrument_parfile = IDF_filename.substr(0, IDF_filename.find("_Definition")) + "_Parameters.xml";
328 if (Poco::File(instrument_parfile).exists()) {
329 try {
330 auto loadParamAlg = createChildAlgorithm("LoadParameterFile");
331 loadParamAlg->setProperty("Filename", instrument_parfile);
332 loadParamAlg->setProperty("Workspace", outputWS);
333 loadParamAlg->execute();
334 } catch (...) {
335 g_log.information("Cannot load the instrument parameter file.");
336 }
337 }
338 }
339 // For NXSPE files generated by Mantid data is actually a distribution.
340 outputWS->setDistribution(true);
341 setProperty("OutputWorkspace", outputWS);
342}
343
344} // namespace Mantid::DataHandling
double value
The value of the point.
Definition: FitMW.cpp:51
double error
Definition: IndexPeaks.cpp:133
#define DECLARE_NEXUS_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro wh...
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
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
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
static std::string getInstrumentFilename(const std::string &instrumentName, const std::string &date="")
Get the IDF using the instrument name and date.
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
A property class for workspaces.
void init() override
Initialise the properties.
Definition: LoadNXSPE.cpp:94
static int identiferConfidence(const std::string &value)
Confidence in identifier.
Definition: LoadNXSPE.cpp:54
int confidence(Kernel::NexusDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
Definition: LoadNXSPE.cpp:73
void exec() override
Run the algorithm.
Definition: LoadNXSPE.cpp:103
Component is a wrapper for a Component which can modify some of its parameters, e....
Definition: Component.h:41
void setPos(double, double, double) override
Set the IComponent position, x, y, z respective to parent (if present)
Definition: Component.cpp:204
This class represents a detector - i.e.
Definition: Detector.h:30
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition: Goniometer.h:55
void pushAxis(const std::string &name, double axisx, double axisy, double axisz, double angle=0., int sense=CCW, int angUnit=angDegrees)
Add an additional axis to the goniometer, closer to the sample.
Definition: Goniometer.cpp:133
Base Instrument Class.
Definition: Instrument.h:47
Object Component class, this class brings together the physical attributes of the component to the po...
Definition: ObjComponent.h:33
void setShape(std::shared_ptr< const IObject > newShape)
Set a new shape on the component void setShape(std::shared_ptr<const IObject> newShape);.
Class originally intended to be used with the DataHandling 'LoadInstrument' algorithm.
Definition: ShapeFactory.h:89
static std::shared_ptr< CSGObject > createSphere(const Kernel::V3D &centre, double radius)
Create a Sphere.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
Defines a wrapper around a file whose internal structure can be accessed using the NeXus API.
const std::string & filename() const
Access the filename.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
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...
Class for 3D vectors.
Definition: V3D.h:34
void spherical(const double R, const double theta, const double phi) noexcept
Sets the vector position based on spherical coordinates.
Definition: V3D.cpp:57
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
bool exists(::NeXus::File &file, const std::string &name)
Based on the current group in the file, does the named sub-entry exist?
std::unique_ptr< T > create(const P &parent, const IndexArg &indexArg, const HistArg &histArg)
This is the create() method that all the other create() methods call.
constexpr double deg2rad
Defines units/enum for Crystal work.
Definition: AngleUnits.h:20
std::shared_ptr< Instrument > Instrument_sptr
Shared pointer to an instrument object.
int32_t detid_t
Typedef for a detector ID.
Definition: SpectrumInfo.h:21
@ Output
An output workspace.
Definition: Property.h:54