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"
25#include "MantidNexus/NexusFile.h"
26
27#include <boost/regex.hpp>
28
29#include <filesystem>
30#include <map>
31#include <sstream>
32#include <string>
33#include <vector>
34
35namespace Mantid::DataHandling {
36
38
39using namespace Mantid::Kernel;
40using namespace Mantid::API;
41using Mantid::HistogramData::BinEdges;
42
49int LoadNXSPE::identiferConfidence(const std::string &value) {
50 int identifierConfidence = 0;
51 if (value == "NXSPE") {
52 identifierConfidence = 99;
53 } else {
54 boost::regex re("^NXSP", boost::regex::icase);
55 if (boost::regex_match(value, re)) {
56 identifierConfidence = 95;
57 }
58 }
59 return identifierConfidence;
60}
61
69 int confidence(0);
70 using string_map_t = std::map<std::string, std::string>;
71 try {
72 Nexus::File file = Nexus::File(descriptor.filename());
73 string_map_t entries = file.getEntries();
74 for (string_map_t::const_iterator it = entries.begin(); it != entries.end(); ++it) {
75 if (it->second == "NXentry") {
76 file.openGroup(it->first, it->second);
77 file.openData("definition");
78 const std::string value = file.getStrData();
80 }
81 }
82 } catch (Nexus::Exception const &) {
83 }
84 return confidence;
85}
86
90 const std::vector<std::string> exts{".nxspe", ""};
91 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, exts), "An NXSPE file");
92 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
93 "The name of the workspace that will be created.");
94}
95
99 std::string filename = getProperty("Filename");
100 // quicly check if it's really nxspe
101 try {
102 Nexus::File file(filename);
103 std::string mainEntry = (*(file.getEntries().begin())).first;
104 file.openGroup(mainEntry, "NXentry");
105 file.openData("definition");
106 if (identiferConfidence(file.getStrData()) < 1) {
107 throw std::invalid_argument("Not NXSPE");
108 }
109 file.close();
110 } catch (...) {
111 throw std::invalid_argument("Not NeXus or not NXSPE");
112 }
113
114 // Load the data
115 Nexus::File file(filename);
116
117 std::string mainEntry = (*(file.getEntries().begin())).first;
118 file.openGroup(mainEntry, "NXentry");
119
120 file.openGroup("NXSPE_info", "NXcollection");
121 std::map<std::string, std::string> entries = file.getEntries();
122 std::vector<double> temporary;
123 double fixed_energy, psi = 0.;
124
125 if (!entries.count("fixed_energy")) {
126 throw std::invalid_argument("fixed_energy field was not found");
127 }
128 file.openData("fixed_energy");
129 file.getData(temporary);
130 fixed_energy = temporary.at(0);
131 file.closeData();
132
133 if (entries.count("psi")) {
134 file.openData("psi");
135 file.getData(temporary);
136 psi = temporary.at(0);
137 if (std::isnan(psi)) {
138 psi = 0.;
139 g_log.warning("Entry for PSI is empty, will use default of 0.0 instead.");
140 }
141 file.closeData();
142 }
143
144 int kikfscaling = 0;
145 if (entries.count("ki_over_kf_scaling")) {
146 file.openData("ki_over_kf_scaling");
147 std::vector<int> temporaryint;
148 file.getData(temporaryint);
149 kikfscaling = temporaryint.at(0);
150 file.closeData();
151 }
152
153 file.closeGroup(); // NXSPE_Info
154
155 file.openGroup("data", "NXdata");
156 entries = file.getEntries();
157
158 if (!entries.count("data")) {
159 throw std::invalid_argument("data field was not found");
160 }
161 file.openData("data");
162 Nexus::Info info = file.getInfo();
163 auto numSpectra = static_cast<std::size_t>(info.dims.at(0));
164 auto numBins = static_cast<std::size_t>(info.dims.at(1));
165 std::vector<double> data;
166 file.getData(data);
167 file.closeData();
168
169 if (!entries.count("error")) {
170 throw std::invalid_argument("error field was not found");
171 }
172 file.openData("error");
173 std::vector<double> error;
174 file.getData(error);
175 file.closeData();
176
177 if (!entries.count("energy")) {
178 throw std::invalid_argument("energy field was not found");
179 }
180 file.openData("energy");
181 std::vector<double> energies;
182 file.getData(energies);
183 file.closeData();
184
185 if (!entries.count("azimuthal")) {
186 throw std::invalid_argument("azimuthal field was not found");
187 }
188 file.openData("azimuthal");
189 std::vector<double> azimuthal;
190 file.getData(azimuthal);
191 file.closeData();
192
193 if (!entries.count("azimuthal_width")) {
194 throw std::invalid_argument("azimuthal_width field was not found");
195 }
196 file.openData("azimuthal_width");
197 std::vector<double> azimuthal_width;
198 file.getData(azimuthal_width);
199 file.closeData();
200
201 if (!entries.count("polar")) {
202 throw std::invalid_argument("polar field was not found");
203 }
204 file.openData("polar");
205 std::vector<double> polar;
206 file.getData(polar);
207 file.closeData();
208
209 if (!entries.count("polar_width")) {
210 throw std::invalid_argument("polar_width field was not found");
211 }
212 file.openData("polar_width");
213 std::vector<double> polar_width;
214 file.getData(polar_width);
215 file.closeData();
216
217 // distance might not have been saved in all NXSPE files
218 std::vector<double> distance;
219 if (entries.count("distance")) {
220 file.openData("distance");
221 file.getData(distance);
222 file.closeData();
223 }
224
225 file.closeGroup(); // data group
226
227 file.openGroup("instrument", "NXinstrument");
228 entries = file.getEntries();
229 std::string instrument_name;
230 if (entries.count("name")) {
231 file.openData("name");
232 instrument_name = file.getStrData();
233 file.closeData();
234 }
235 file.closeGroup(); // instrument group
236
237 file.closeGroup(); // Main entry
238 file.close();
239
240 // check if dimensions of the vectors are correct
241 if ((error.size() != data.size()) || (azimuthal.size() != numSpectra) || (azimuthal_width.size() != numSpectra) ||
242 (polar.size() != numSpectra) || (polar_width.size() != numSpectra) ||
243 ((energies.size() != numBins) && (energies.size() != numBins + 1))) {
244 throw std::invalid_argument("incompatible sizes of fields in the NXSPE file");
245 }
246
247 MatrixWorkspace_sptr outputWS = std::dynamic_pointer_cast<MatrixWorkspace>(
248 WorkspaceFactory::Instance().create("Workspace2D", numSpectra, energies.size(), numBins));
249 // Need to get hold of the parameter map
250 outputWS->getAxis(0)->unit() = UnitFactory::Instance().create("DeltaE");
251 outputWS->setYUnit("SpectraNumber");
252
253 // add logs
254 outputWS->mutableRun().addLogData(new PropertyWithValue<double>("Ei", fixed_energy));
255 outputWS->mutableRun().addLogData(new PropertyWithValue<double>("psi", psi));
256 outputWS->mutableRun().addLogData(
257 new PropertyWithValue<std::string>("ki_over_kf_scaling", kikfscaling == 1 ? "true" : "false"));
258
259 // Set Goniometer
261 gm.pushAxis("psi", 0, 1, 0, psi);
262 outputWS->mutableRun().setGoniometer(gm, true);
263
264 // generate instrument
265 Geometry::Instrument_sptr instrument(new Geometry::Instrument(instrument_name.empty() ? "NXSPE" : instrument_name));
266
267 Geometry::ObjComponent *source = new Geometry::ObjComponent("source");
268 source->setPos(0.0, 0.0, -10.);
269 instrument->add(source);
270 instrument->markAsSource(source);
271 Geometry::Component *sample = new Geometry::Component("sample");
272 instrument->add(sample);
273 instrument->markAsSamplePos(sample);
274
275 constexpr double deg2rad = M_PI / 180.0;
276
277 for (std::size_t i = 0; i < numSpectra; ++i) {
278 double r = 1.0;
279 if (!distance.empty()) {
280 r = distance.at(i);
281 }
282
283 Kernel::V3D pos;
284 pos.spherical(r, polar.at(i), azimuthal.at(i));
285 // Define the size of the detector using the minimum of the polar or azimuthal width (with maximum of 2 deg)
286 double rr =
287 r * sin(std::min(std::min(std::abs(polar_width.at(i)), std::abs(azimuthal_width.at(i))), 2.0) * deg2rad / 2);
288
289 const auto shape = Geometry::ShapeFactory().createSphere(V3D(0, 0, 0), std::max(rr, 0.01));
290
291 Geometry::Detector *det = new Geometry::Detector("pixel", static_cast<int>(i + 1), sample);
292 det->setPos(pos);
293 det->setShape(shape);
294 instrument->add(det);
295 instrument->markAsDetector(det);
296 }
297 outputWS->setInstrument(instrument);
298
299 std::vector<double>::iterator itdata = data.begin(), iterror = error.begin(), itdataend, iterrorend;
300 auto &spectrumInfo = outputWS->mutableSpectrumInfo();
301 API::Progress prog = API::Progress(this, 0.0, 0.9, numSpectra);
302 BinEdges edges(std::move(energies));
303 for (std::size_t i = 0; i < numSpectra; ++i) {
304 itdataend = itdata + numBins;
305 iterrorend = iterror + numBins;
306 outputWS->getSpectrum(i).setDetectorID(static_cast<detid_t>(i + 1));
307 outputWS->setBinEdges(i, edges);
308 if ((!std::isfinite(*itdata)) || (*itdata <= -1e10)) // masked bin
309 {
310 spectrumInfo.setMasked(i, true);
311 } else {
312 outputWS->mutableY(i).assign(itdata, itdataend);
313 outputWS->mutableE(i).assign(iterror, iterrorend);
314 }
315 itdata = (itdataend);
316 iterror = (iterrorend);
317 prog.report();
318 }
319
320 // If an instrument name is defined, load instrument parameter file for Emode
321 // NB. LoadParameterFile must be used on a workspace with an instrument
322 if (!instrument_name.empty() && instrument_name != "NXSPE") {
323 std::string IDF_filename = InstrumentFileFinder::getInstrumentFilename(instrument_name);
324 std::string instrument_parfile = IDF_filename.substr(0, IDF_filename.find("_Definition")) + "_Parameters.xml";
325 if (std::filesystem::exists(instrument_parfile)) {
326 try {
327 auto loadParamAlg = createChildAlgorithm("LoadParameterFile");
328 loadParamAlg->setProperty("Filename", instrument_parfile);
329 loadParamAlg->setProperty("Workspace", outputWS);
330 loadParamAlg->execute();
331 } catch (...) {
332 g_log.information("Cannot load the instrument parameter file.");
333 }
334 }
335 }
336 // For NXSPE files generated by Mantid data is actually a distribution.
337 outputWS->setDistribution(true);
338 setProperty("OutputWorkspace", outputWS);
339}
340
341} // namespace Mantid::DataHandling
double value
The value of the point.
Definition FitMW.cpp:51
double error
#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.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
@ Load
allowed here which will be passed to the algorithm
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.
int confidence(Nexus::NexusDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
Definition LoadNXSPE.cpp:68
void init() override
Initialise the properties.
Definition LoadNXSPE.cpp:89
static int identiferConfidence(const std::string &value)
Confidence in identifier.
Definition LoadNXSPE.cpp:49
void exec() override
Run the algorithm.
Definition LoadNXSPE.cpp:98
Component is a wrapper for a Component which can modify some of its parameters, e....
Definition Component.h:42
void setPos(double, double, double) override
Set the IComponent position, x, y, z respective to parent (if present)
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.
Base Instrument Class.
Definition Instrument.h:47
Object Component class, this class brings together the physical attributes of the component to the po...
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.
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:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
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:56
Class that provides for a standard Nexus exception.
const std::string & filename() const noexcept
Returns a copy of the current file name.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
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.
@ Output
An output workspace.
Definition Property.h:54
This structure holds the type and dimensions of a primative field/array.
DimVector dims
The dimensions of the file.