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