Mantid
Loading...
Searching...
No Matches
LoadHelper.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 +
7/*
8 * Helper file to gather common routines to the Loaders
9 * */
10
20#include "MantidNexus/NexusFile.h"
21#include <strstream>
22
23namespace Mantid {
24
25namespace {
27Kernel::Logger g_log("LoadHelper");
28} // namespace
29
30namespace DataHandling {
31using namespace Kernel;
32using namespace API;
33
39 std::string result("");
40 std::vector<Mantid::Nexus::NXClassInfo> v = firstEntry.groups();
41 const auto it = std::find_if(v.cbegin(), v.cend(), [](const auto &group) { return group.nxclass == "NXinstrument"; });
42 if (it != v.cend())
43 result = it->nxname;
44
45 return result;
46}
47
49 const std::string &nexusAddress) {
50 return firstEntry.getString(nexusAddress);
51}
52
54 const std::string &nexusAddress) {
55 return firstEntry.getFloat(nexusAddress);
56}
57
63 const std::string &nexusAddress) {
64
65 Mantid::Nexus::NXFloat timeBinningNexus = firstEntry.openNXFloat(nexusAddress);
66 timeBinningNexus.load();
67
68 size_t numberOfBins = static_cast<size_t>(timeBinningNexus.dim0()) + 1; // boundaries
69
70 float *timeBinning_p = &timeBinningNexus[0];
71 std::vector<double> timeBinning(numberOfBins);
72 std::copy(timeBinning_p, timeBinning_p + timeBinningNexus.dim0(), std::begin(timeBinning));
73 // calculate the extra bin at the end
74 timeBinning[numberOfBins - 1] = timeBinning[numberOfBins - 2] + timeBinning[1] - timeBinning[0];
75
76 return timeBinning;
77}
83double LoadHelper::calculateEnergy(double wavelength) {
85 (2 * PhysicalConstants::NeutronMass * wavelength * wavelength * 1e-20) / PhysicalConstants::meV;
86 return e;
87}
88
95double LoadHelper::calculateTOF(double distance, double wavelength) {
96 if (wavelength <= 0) {
97 throw std::runtime_error("Wavelenght is <= 0");
98 }
99
100 double velocity = PhysicalConstants::h / (PhysicalConstants::NeutronMass * wavelength * 1e-10); // m/s
101
102 return distance / velocity;
103}
104
105/*
106 * Get instrument property as double
107 * @s - input property name
108 *
109 */
111 std::vector<std::string> prop = workspace->getInstrument()->getStringParameter(s);
112 if (prop.empty()) {
113 return EMPTY_DBL();
114 } else {
115 return boost::lexical_cast<double>(prop[0]);
116 }
117}
118
128void LoadHelper::addNexusFieldsToWsRun(Nexus::File &filehandle, API::Run &runDetails, const std::string &entryName,
129 bool useFullAddress) {
130 // As a workaround against some "not so good" old ILL nexus files (ILLIN5_Vana_095893.nxs for example) by default we
131 // begin the parse on the first entry (entry0), or from a chosen entryName. This allow to avoid the bogus entries that
132 // follows
133 std::string entryNameActual(entryName);
134 if (entryName.empty()) {
135 entryNameActual = filehandle.getTopLevelEntryName();
136 }
137
138 // open the group and parse down
139 if (!entryNameActual.empty()) {
140 constexpr int LEVEL_RECURSE{1};
141 filehandle.openGroup(entryNameActual, "NXentry");
142 const std::string EMPTY_STR;
143 recurseAndAddNexusFieldsToWsRun(filehandle, runDetails, EMPTY_STR, EMPTY_STR, LEVEL_RECURSE, useFullAddress);
144 filehandle.closeGroup();
145 }
146}
147
148namespace {
149template <typename NumericType>
150void addNumericProperty(Nexus::File &filehandle, const Nexus::Info &nxinfo, const std::string &property_name,
151 API::Run &runDetails) {
152 if (!runDetails.hasProperty(property_name)) {
153 g_log.warning() << "Property " << property_name << " was set twice. Please check the Nexus file and your inputs.";
154 }
155 // this assumes all values are rank==1
156
157 // Look for "units"
158 std::string units; // empty string
159 if (filehandle.hasAttr("units"))
160 filehandle.getAttr("units", units);
161
162 // read the field
163 std::vector<NumericType> data_vec;
164 data_vec.reserve(nxinfo.dims.front());
165 filehandle.getDataCoerce(data_vec);
166
167 // create the property on the run objects
168 const auto dim1size = data_vec.size();
169 if (dim1size == 1) {
170 if (units.empty())
171 runDetails.addProperty(property_name, data_vec.front());
172 else
173 runDetails.addProperty(property_name, data_vec.front(), units);
174 } else {
175 // create a bunch of little properties
176 for (size_t index = 0; index < dim1size; ++index) {
177 const auto property_name_indexed = property_name + "_" + std::to_string(index);
178 if (units.empty())
179 runDetails.addProperty(property_name_indexed, data_vec[index]);
180 else
181 runDetails.addProperty(property_name_indexed, data_vec[index], units);
182 }
183 }
184}
185} // namespace
186
199void LoadHelper::recurseAndAddNexusFieldsToWsRun(Nexus::File &filehandle, API::Run &runDetails,
200 const std::string &parent_name, const std::string &parent_class,
201 int level, bool useFullAddress) {
202 const std::string SDS("SDS"); // denotes data field
203
204 for (const auto &entry : filehandle.getEntries()) {
205 const auto nxname = entry.first;
206 const auto nxclass = entry.second;
207 if (nxclass == SDS) {
208 if ((parent_class != "NXData") && parent_class != "NXMonitor" && (nxname != "data")) { // create a property
209 filehandle.openData(nxname);
210 const auto nxinfo = filehandle.getInfo(); // get information about the open data
211 const auto rank = nxinfo.dims.size();
212
213 // Note, we choose to only build properties on small float arrays filter logic is below
214 bool read_property = (rank == 1);
215 switch (nxinfo.type) {
216 case (NXnumtype::CHAR):
217 break; // everything is fine
218 case (NXnumtype::FLOAT32):
219 case (NXnumtype::FLOAT64):
220 // some 1d float arrays may be loaded
221 read_property = (nxinfo.dims[0] <= 9);
222 break;
223 case (NXnumtype::INT16):
224 case (NXnumtype::INT32):
225 case (NXnumtype::UINT16):
226 // only read scalar values for everything else - e.g. integers
227 read_property = (nxinfo.dims.front() == 1);
228 break;
229 default:
230 read_property = false;
231 }
232
233 if (read_property) {
234 // generate a name for the property
235 const std::string property_name = (parent_name.empty() ? nxname : parent_name + "." + nxname);
236
237 switch (nxinfo.type) {
238 case (NXnumtype::CHAR): {
239 std::string property_value = filehandle.getStrData();
240 if (property_name.ends_with("_time")) {
241 // That's a time value! Convert to Mantid standard
242 property_value = dateTimeInIsoFormat(property_value);
243 if (runDetails.hasProperty(property_name))
244 runDetails.getProperty(property_name)->setValue(property_value);
245 else
246 runDetails.addProperty(property_name, property_value);
247 } else {
248 if (!runDetails.hasProperty(property_name)) {
249 runDetails.addProperty(property_name, property_value);
250 } else {
251 g_log.warning() << "Property " << property_name
252 << " was set twice. Please check the Nexus file and your inputs.\n";
253 }
254 }
255 break;
256 }
257 case (NXnumtype::FLOAT32):
258 case (NXnumtype::FLOAT64):
259 addNumericProperty<double>(filehandle, nxinfo, property_name, runDetails);
260 break;
261 case (NXnumtype::INT16):
262 case (NXnumtype::INT32):
263 case (NXnumtype::UINT16):
264 addNumericProperty<int>(filehandle, nxinfo, property_name, runDetails);
265 break;
266 default:
267 std::stringstream msg;
268 msg << "Encountered unknown type: " << nxinfo.type;
269 throw std::runtime_error(msg.str());
270 break;
271 }
272 }
273 filehandle.closeData();
274 }
275 } else {
276 if ((nxclass != "ILL_data_scan_vars") && (nxclass != "NXill_data_scan_vars")) {
277 // open the group and recurse down, if the group is known to nexus
278 filehandle.openGroup(nxname, nxclass);
279
280 const std::string property_name = (parent_name.empty() ? nxname : parent_name + "." + nxname);
281 const std::string p_nxname =
282 useFullAddress ? property_name : nxname; // current names can be useful for next level
283 const std::string p_nxclass(nxclass);
284
285 recurseAndAddNexusFieldsToWsRun(filehandle, runDetails, p_nxname, p_nxclass, level + 1, useFullAddress);
286
287 filehandle.closeGroup();
288 }
289 }
290 }
291}
292
302std::string LoadHelper::dateTimeInIsoFormat(const std::string &dateToParse) {
303 namespace bt = boost::posix_time;
304 try {
305 // Try to convert to a boost date as an iso string. If it works, we are already in ISO and no conversion is
306 // requiered.
307 bt::from_iso_extended_string(dateToParse);
308 return dateToParse;
309
310 } catch (...) {
311 }
312 // parsing format
313 const std::locale format = std::locale(std::locale::classic(), new bt::time_input_facet("%d-%b-%y %H:%M:%S"));
314
315 bt::ptime pt;
316 std::istringstream is(dateToParse);
317 is.imbue(format);
318 is >> pt;
319
320 if (pt != bt::ptime()) {
321 // Converts to ISO
322 std::string s = bt::to_iso_extended_string(pt);
323 return s;
324 } else {
325 return "";
326 }
327}
328
335void LoadHelper::moveComponent(const API::MatrixWorkspace_sptr &ws, const std::string &componentName,
336 const V3D &newPos) {
337 Geometry::IComponent_const_sptr component = ws->getInstrument()->getComponentByName(componentName);
338 if (!component) {
339 throw std::invalid_argument("Instrument component " + componentName + " not found");
340 }
341 auto &componentInfo = ws->mutableComponentInfo();
342 const auto componentIndex = componentInfo.indexOf(component->getComponentID());
343 componentInfo.setPosition(componentIndex, newPos);
344}
345
353void LoadHelper::rotateComponent(const API::MatrixWorkspace_sptr &ws, const std::string &componentName,
354 const Kernel::Quat &rot) {
355 Geometry::IComponent_const_sptr component = ws->getInstrument()->getComponentByName(componentName);
356 if (!component) {
357 throw std::invalid_argument("Instrument component " + componentName + " not found");
358 }
359 auto &componentInfo = ws->mutableComponentInfo();
360 const auto componentIndex = componentInfo.indexOf(component->getComponentID());
361 componentInfo.setRotation(componentIndex, rot);
362}
363
370V3D LoadHelper::getComponentPosition(const API::MatrixWorkspace_sptr &ws, const std::string &componentName) {
371 Geometry::IComponent_const_sptr component = ws->getInstrument()->getComponentByName(componentName);
372 if (!component) {
373 throw std::invalid_argument("Instrument component " + componentName + " not found");
374 }
375 V3D pos = component->getPos();
376 return pos;
377}
378
385void LoadHelper::loadEmptyInstrument(const API::MatrixWorkspace_sptr &ws, const std::string &instrumentName,
386 const std::string &instrumentPath) {
387 auto loadInst = AlgorithmManager::Instance().create("LoadInstrument");
388 loadInst->initialize();
389 loadInst->setChild(true);
390 loadInst->setPropertyValue("InstrumentName", instrumentName);
391 if (!instrumentPath.empty())
392 loadInst->setPropertyValue("Filename", instrumentPath);
393 loadInst->setProperty<MatrixWorkspace_sptr>("Workspace", ws);
394 loadInst->setProperty("RewriteSpectraMap", OptionalBool(true));
395 loadInst->execute();
396}
397
413 const std::vector<double> &xAxis, int64_t initialSpectrum, bool pointData,
414 const std::vector<detid_t> &detectorIDs,
415 const std::set<detid_t> &acceptedDetectorIDs,
416 const std::tuple<short, short, short> &axisOrder) {
417
418 const bool customDetectorIDs = detectorIDs.size() != 0;
419 const bool excludeDetectorIDs = acceptedDetectorIDs.size() != 0;
420
421 std::array dims = {data.dim0(), data.dim1(), data.dim2()};
422 const auto nTubes = dims[std::get<0>(axisOrder)];
423 const auto nPixels = dims[std::get<1>(axisOrder)];
424 const auto nChannels = dims[std::get<2>(axisOrder)];
425
426 int loadOrder[3] = {0, 1, 2};
427 loadingOrder(axisOrder, loadOrder);
428
429 HistogramData::Points histoPoints;
430 HistogramData::BinEdges binEdges;
431 if (pointData)
432 histoPoints = HistogramData::Points(xAxis);
433 else
434 binEdges = HistogramData::BinEdges(xAxis);
435 int nSkipped = 0;
436
437#pragma omp parallel for if (!excludeDetectorIDs && Kernel::threadSafe(*ws))
438 for (specnum_t tube_no = 0; tube_no < static_cast<specnum_t>(nTubes); ++tube_no) {
439 for (specnum_t pixel_no = 0; pixel_no < static_cast<specnum_t>(nPixels); ++pixel_no) {
440 specnum_t currentSpectrum =
441 static_cast<specnum_t>(initialSpectrum) + tube_no * static_cast<specnum_t>(nPixels) + pixel_no;
442 if (excludeDetectorIDs != 0 && std::find(acceptedDetectorIDs.cbegin(), acceptedDetectorIDs.cend(),
443 currentSpectrum) == acceptedDetectorIDs.end()) {
444 nSkipped++;
445 continue;
446 }
447 currentSpectrum -= nSkipped;
448
449 std::vector<int> spectrum(nChannels);
450 for (specnum_t channel_no = 0; channel_no < static_cast<specnum_t>(nChannels); ++channel_no) {
451 const int dataIndices[3] = {tube_no, pixel_no, channel_no};
452 spectrum[channel_no] = data(dataIndices[loadOrder[0]], dataIndices[loadOrder[1]], dataIndices[loadOrder[2]]);
453 }
454 const HistogramData::Counts counts(spectrum.begin(), spectrum.end());
455 const HistogramData::CountVariances countVariances(spectrum.begin(), spectrum.end());
456 if (pointData) {
457 ws->setCounts(currentSpectrum, counts);
458 ws->setCountVariances(currentSpectrum, countVariances);
459 ws->setPoints(currentSpectrum, histoPoints);
460 } else {
461 ws->setHistogram(currentSpectrum, binEdges, counts);
462 }
463 const specnum_t detectorID = customDetectorIDs ? detectorIDs[currentSpectrum] : currentSpectrum;
464 ws->getSpectrum(currentSpectrum).setSpectrumNo(detectorID);
465 }
466 }
467}
468
474void LoadHelper::loadingOrder(const std::tuple<short, short, short> &dataOrder, int *dataIndices) {
475 if (std::get<0>(dataOrder) != 0)
476 dataIndices[0] = std::get<1>(dataOrder) == 0 ? 1 : 2;
477 if (std::get<1>(dataOrder) != 1)
478 dataIndices[1] = std::get<0>(dataOrder) == 1 ? 0 : 2;
479 if (std::get<2>(dataOrder) != 2)
480 dataIndices[2] = std::get<1>(dataOrder) == 2 ? 1 : 0;
481}
482
496 const std::vector<double> &xAxis, int64_t initialSpectrum,
497 const std::set<detid_t> &acceptedDetectorIDs,
498 const std::vector<detid_t> &customDetectorIDs,
499 const std::tuple<short, short, short> &axisOrder) {
500
501 const auto useCustomSpectraMap = customDetectorIDs.size() != 0;
502 const auto useAcceptedDetectorIDs = acceptedDetectorIDs.size() != 0;
503
504 std::array<Nexus::dimsize_t, 3U> dims = {data.dim0(), data.dim1(), data.dim2()};
505 const auto nTubes = dims[std::get<0>(axisOrder)];
506 const auto nPixels = dims[std::get<1>(axisOrder)];
507 const auto nScans = dims[std::get<2>(axisOrder)];
508
509 int nSkipped = 0;
510#pragma omp parallel for if (Kernel::threadSafe(*ws))
511 for (specnum_t tube_no = 0; tube_no < static_cast<specnum_t>(nTubes); ++tube_no) {
512 for (specnum_t pixel_no = 0; pixel_no < static_cast<specnum_t>(nPixels); ++pixel_no) {
513 specnum_t currentDetector =
514 static_cast<specnum_t>(initialSpectrum) + tube_no * static_cast<specnum_t>(nPixels) + pixel_no;
515 if (useAcceptedDetectorIDs && std::find(acceptedDetectorIDs.cbegin(), acceptedDetectorIDs.cend(),
516 currentDetector) == acceptedDetectorIDs.end()) {
517 nSkipped++;
518 continue;
519 }
520 currentDetector -= nSkipped;
521 int64_t currentSpectrum;
522 if (useCustomSpectraMap)
523 currentSpectrum = customDetectorIDs[currentDetector - initialSpectrum];
524 else
525 currentSpectrum = currentDetector;
526 currentSpectrum *= nScans;
527 for (specnum_t channel_no = 0; channel_no < static_cast<specnum_t>(nScans); ++channel_no) {
528 auto spectrumValue = data(channel_no, tube_no, pixel_no);
529 ws->mutableY(currentSpectrum) = spectrumValue;
530 ws->mutableE(currentSpectrum) = sqrt(spectrumValue);
531 ws->mutableX(currentSpectrum) = xAxis;
532 currentSpectrum++;
533 }
534 }
535 }
536}
537
543void LoadHelper::replaceZeroErrors(const API::MatrixWorkspace_sptr &ws, double zeroCountsError) {
544 for (size_t spectrum_no = 0; spectrum_no < ws->getNumberHistograms(); ++spectrum_no) {
545 auto &errorAxis = ws->mutableE(spectrum_no);
546 std::transform(errorAxis.begin(), errorAxis.end(), errorAxis.begin(),
547 [zeroCountsError](const auto &error) { return error == 0 ? zeroCountsError : error; });
548 }
549}
550
557Nexus::NXInt LoadHelper::getIntDataset(const Nexus::NXEntry &entry, const std::string &groupName) {
558 auto dataGroup = entry.openNXData(groupName);
559 return dataGroup.openIntData();
560}
561
568Nexus::NXDouble LoadHelper::getDoubleDataset(const Nexus::NXEntry &entry, const std::string &groupName) {
569 auto dataGroup = entry.openNXData(groupName);
570 return dataGroup.openDoubleData();
571}
572
573} // namespace DataHandling
574} // namespace Mantid
double error
IPeaksWorkspace_sptr workspace
std::map< DeltaEMode::Type, std::string > index
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
void addProperty(Kernel::Property *prop, bool overwrite=false)
Add data to the object in the form of a property.
Definition LogManager.h:91
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
OptionalBool : Tri-state bool.
virtual std::string setValue(const std::string &)=0
Set the value of the property via a string.
Class for quaternions.
Definition Quat.h:39
Class for 3D vectors.
Definition V3D.h:34
std::size_t size() const noexcept
Number of components in V3D.
Definition V3D.h:49
float getFloat(const std::string &name) const
Returns a float.
std::string getString(const std::string &name) const
Returns a string.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
std::vector< NXClassInfo > & groups() const
Returns a list of all classes (or groups) in this NXClass.
Templated class implementation of NXDataSet.
void load()
Read all of the datablock in.
dimsize_t dim0() const
Returns the number of elements along the first dimension.
dimsize_t dim2() const
Returns the number of elements along the third dimension.
dimsize_t dim1() const
Returns the number of elements along the second dimension.
NXDouble openDoubleData()
Opens data of double type.
NXInt openIntData()
Opens data of int type.
Implements NXentry Nexus class.
NXData openNXData(const std::string &name) const
Opens a NXData.
static unsigned short constexpr UINT16
static unsigned short constexpr INT16
static unsigned short constexpr CHAR
static unsigned short constexpr INT32
static unsigned short constexpr FLOAT32
static unsigned short constexpr FLOAT64
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::string getStringFromNexusAddress(const Mantid::Nexus::NXEntry &, const std::string &)
void recurseAndAddNexusFieldsToWsRun(Nexus::File &filehandle, API::Run &runDetails, const std::string &parent_name, const std::string &parent_class, int level, bool useFullAddress)
Recursively add properties from a nexus file to the workspace run.
void fillMovingWorkspace(const API::MatrixWorkspace_sptr &, const Mantid::Nexus::NXInt &, const std::vector< double > &xAxis, int64_t initialSpectrum=0, const std::set< detid_t > &acceptedID=std::set< int >(), const std::vector< detid_t > &customID=std::vector< int >(), const std::tuple< short, short, short > &axisOrder=std::tuple< short, short, short >(0, 1, 2))
Fills workspace with histogram data from provided data structure.
double getInstrumentProperty(const API::MatrixWorkspace_sptr &, const std::string &)
void addNexusFieldsToWsRun(Nexus::File &filehandle, API::Run &runDetails, const std::string &entryName="", bool useFullAddress=false)
Add properties from a nexus file to the workspace run.
double calculateEnergy(double)
Calculate Neutron Energy from wavelength: .
void loadingOrder(const std::tuple< short, short, short > &axisOrder, int *dataIndices)
Handles non-standard loading order of the provided data, based on the provided data dimension order.
void rotateComponent(const API::MatrixWorkspace_sptr &ws, const std::string &componentName, const Kernel::Quat &rot)
LoadHelper::rotateComponent.
Nexus::NXInt getIntDataset(const Nexus::NXEntry &, const std::string &)
Fetches NXInt data from the requested group name in the entry provided.
Nexus::NXDouble getDoubleDataset(const Nexus::NXEntry &, const std::string &)
Fetches NXDouble data from the requested group name in the entry provided.
void moveComponent(const API::MatrixWorkspace_sptr &ws, const std::string &componentName, const Kernel::V3D &newPos)
LoadHelper::moveComponent.
double calculateTOF(double, double)
Calculate TOF from distance.
std::vector< double > getTimeBinningFromNexusAddress(const Mantid::Nexus::NXEntry &, const std::string &)
Gets the time binning from a Nexus float array Adds an extra bin at the end.
void fillStaticWorkspace(const API::MatrixWorkspace_sptr &, const Mantid::Nexus::NXInt &, const std::vector< double > &xAxis, int64_t initialSpectrum=0, bool pointData=false, const std::vector< detid_t > &detectorIDs=std::vector< int >(), const std::set< detid_t > &acceptedID=std::set< int >(), const std::tuple< short, short, short > &axisOrder=std::tuple< short, short, short >(0, 1, 2))
Fills workspace with histogram data from provided data structure.
std::string dateTimeInIsoFormat(const std::string &)
Parses the date as formatted at the ILL: 29-Jun-12 11:27:26 and converts it to the ISO format used in...
Kernel::V3D getComponentPosition(const API::MatrixWorkspace_sptr &ws, const std::string &componentName)
LoadHelper::getComponentPosition.
void loadEmptyInstrument(const API::MatrixWorkspace_sptr &ws, const std::string &instrumentName, const std::string &instrumentAddress="")
Loads empty instrument of chosen name into a provided workspace.
double getDoubleFromNexusAddress(const Mantid::Nexus::NXEntry &, const std::string &)
std::string findInstrumentNexusAddress(const Mantid::Nexus::NXEntry &)
Finds the address for the instrument name in the nexus file Usually of the form: entry0/<NXinstrument...
void replaceZeroErrors(const API::MatrixWorkspace_sptr &, double)
Replaces errors of bins with zero counts with provided value.
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:167
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double h
Planck constant in J*s.
static constexpr double meV
1 meV in Joules.
Helper class which provides the Collimation Length for SANS instruments.
int32_t specnum_t
Typedef for a spectrum Number.
Definition IDTypes.h:14
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
std::string to_string(const wide_integer< Bits, Signed > &n)
This structure holds the type and dimensions of a primative field/array.
DimVector dims
The dimensions of the file.
NXnumtype type
The primative type for the field.