Newer
Older
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidNexus/NexusClasses.h"
namespace Mantid {
namespace NeXus {
std::vector<std::string> NXAttributes::names() const {
std::vector<std::string> out;
std::map<std::string, std::string>::const_iterator it = m_values.begin();
for (; it != m_values.end(); ++it)
out.push_back(it->first);
return out;
std::vector<std::string> NXAttributes::values() const {
std::vector<std::string> out;
std::map<std::string, std::string>::const_iterator it = m_values.begin();
for (; it != m_values.end(); ++it)
out.push_back(it->second);
return out;
}
/** Returns the value of an attribute
Janik Zikovsky
committed
* @param name :: The name of the attribute
* @return The value of the attribute if it exists or an empty string
* otherwise
std::string NXAttributes::operator()(const std::string &name) const {
std::map<std::string, std::string>::const_iterator it = m_values.find(name);
if (it == m_values.end())
return "";
return it->second;
}
/** Sets the value of the attribute.
Janik Zikovsky
committed
* @param name :: The name of the attribute
* @param value :: The new value of the attribute
void NXAttributes::set(const std::string &name, const std::string &value) {
m_values[name] = value;
}
/** Sets the value of the attribute as a double.
Janik Zikovsky
committed
* @param name :: The name of the attribute
* @param value :: The new value of the attribute
void NXAttributes::set(const std::string &name, double value) {
std::ostringstream ostr;
ostr << value;
m_values[name] = ostr.str();
}
//---------------------------------------------------------
// NXObject methods
//---------------------------------------------------------
/** NXObject constructor.
Janik Zikovsky
committed
* @param fileID :: The Nexus file id
* @param parent :: The parent Nexus class. In terms of HDF it is the group
* containing the object.
Janik Zikovsky
committed
* @param name :: The name of the object relative to its parent
NXObject::NXObject(const NXhandle fileID, const NXClass *parent,
const std::string &name)
: m_fileID(fileID), m_open(false) {
if (parent && !name.empty()) {
m_path = parent->path() + "/" + name;
}
std::string NXObject::name() const {
size_t i = m_path.find_last_of('/');
if (i == std::string::npos)
return m_path;
else
return m_path.substr(i + 1, m_path.size() - i - 1);
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
void NXObject::getAttributes() {
NXname pName;
int iLength, iType;
int nbuff = 127;
boost::shared_array<char> buff(new char[nbuff + 1]);
while (NXgetnextattr(m_fileID, pName, &iLength, &iType) != NX_EOD) {
// std::cerr<<"--------------------------\n";
// std::cerr<<"name="<<path()<<'\n';
// std::cerr<<pName<<' ' <<iLength<<' '<<iType<<'\n';
switch (iType) {
case NX_CHAR: {
if (iLength > nbuff + 1) {
nbuff = iLength;
buff.reset(new char[nbuff + 1]);
}
int nz = iLength + 1;
NXgetattr(m_fileID, pName, buff.get(), &nz, &iType);
attributes.set(pName, buff.get());
// std::cerr<<"value="<<buff.get()<<'\n';
break;
}
case NX_INT16: {
short int value;
NXgetattr(m_fileID, pName, &value, &iLength, &iType);
sprintf(buff.get(), "%i", value);
attributes.set(pName, buff.get());
break;
}
case NX_INT32: {
int value;
NXgetattr(m_fileID, pName, &value, &iLength, &iType);
sprintf(buff.get(), "%i", value);
attributes.set(pName, buff.get());
break;
}
case NX_UINT16: {
short unsigned int value;
NXgetattr(m_fileID, pName, &value, &iLength, &iType);
sprintf(buff.get(), "%u", value);
attributes.set(pName, buff.get());
break;
}
}
};
}
//---------------------------------------------------------
// NXClass methods
//---------------------------------------------------------
NXClass::NXClass(const NXClass &parent, const std::string &name)
: NXObject(parent.m_fileID, &parent, name) {
clear();
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
NXClassInfo NXClass::getNextEntry() {
NXClassInfo res;
char nxname[NX_MAXNAMELEN], nxclass[NX_MAXNAMELEN];
res.stat = NXgetnextentry(m_fileID, nxname, nxclass, &res.datatype);
if (res) // Check if previous call was successful
{
res.nxname = nxname;
res.nxclass = nxclass;
}
return res;
}
void NXClass::readAllInfo() {
clear();
NXClassInfo info;
while ((info = getNextEntry())) {
if (info.nxclass == "SDS") {
NXInfo data_info;
NXopendata(m_fileID, info.nxname.c_str());
data_info.stat =
NXgetinfo(m_fileID, &data_info.rank, data_info.dims, &data_info.type);
NXclosedata(m_fileID);
data_info.nxname = info.nxname;
m_datasets->push_back(data_info);
} else if (info.nxclass.substr(0, 2) == "NX" ||
info.nxclass.substr(0, 2) == "IX") {
m_groups->push_back(info);
// std::cerr<<'!'<<info.nxname<<'\n';
}
reset();
bool NXClass::isValid(const std::string &path) const {
if (NXopengrouppath(m_fileID, path.c_str()) == NX_OK) {
NXclosegroup(m_fileID);
return true;
void NXClass::open() {
// if (NX_ERROR == NXopengroup(m_fileID,name().c_str(),NX_class().c_str()))
if (NX_ERROR == NXopengrouppath(m_fileID, name().c_str())) {
throw std::runtime_error("Cannot open group " + name() + " of class " +
m_open = true;
readAllInfo();
}
Roman Tolchenov
committed
/** It is fast, but the parent of this class must be open at
* the time of calling. openNXClass uses open() (the slow one). To open calss
* using openLocal() do:
Roman Tolchenov
committed
* NXTheClass class(parent,name);
* class.openLocal();
* // work with class
* class.close();
Janik Zikovsky
committed
* @param nxclass :: The NX class name. If empty NX_class() will be used
Roman Tolchenov
committed
* @return true if OK
*/
bool NXClass::openLocal(const std::string &nxclass) {
std::string className = nxclass.empty() ? NX_class() : nxclass;
if (NX_ERROR == NXopengroup(m_fileID, name().c_str(), className.c_str())) {
// It would be nice if this worked
// if (NX_ERROR == NXopengrouppath(m_fileID,m_path.c_str()))
// throw std::runtime_error("Cannot open group "+m_path+" of class
// "+NX_class());
//}
return false;
}
return true;
void NXClass::close() {
if (NX_ERROR == NXclosegroup(m_fileID)) {
throw std::runtime_error("Cannot close group " + name() + " of class " +
NX_class());
}
m_open = false;
void NXClass::reset() { NXinitgroupdir(m_fileID); }
void NXClass::clear() {
m_groups.reset(new std::vector<NXClassInfo>);
m_datasets.reset(new std::vector<NXInfo>);
std::string NXClass::getString(const std::string &name) const {
NXChar buff = openNXChar(name);
try {
buff.load();
return std::string(buff(), buff.dim0());
} catch (std::runtime_error &) {
// deals with reading uninitialized/empty data
return std::string();
}
double NXClass::getDouble(const std::string &name) const {
NXDouble number = openNXDouble(name);
number.load();
return *number();
float NXClass::getFloat(const std::string &name) const {
NXFloat number = openNXFloat(name);
number.load();
return *number();
int NXClass::getInt(const std::string &name) const {
NXInt number = openNXInt(name);
number.load();
return *number();
Steve Williams
committed
/** Returns whether an individual group (or group) is present
Janik Zikovsky
committed
* @param query :: the class name to search for
Steve Williams
committed
* @return true if the name is found and false otherwise
*/
bool NXClass::containsGroup(const std::string &query) const {
Steve Williams
committed
std::vector<NXClassInfo>::const_iterator end = m_groups->end();
for (std::vector<NXClassInfo>::const_iterator i = m_groups->begin(); i != end;
++i) {
if (i->nxname == query) {
Steve Williams
committed
return true;
}
}
return false;
}
* Returns NXInfo for a dataset
* @param name :: The name of the dataset
* @return NXInfo::stat is set to NX_ERROR if the dataset does not exist
*/
NXInfo NXClass::getDataSetInfo(const std::string &name) const {
NXInfo info;
for (std::vector<NXInfo>::const_iterator it = datasets().begin();
it != datasets().end(); ++it) {
if (it->nxname == name)
return *it;
}
info.stat = NX_ERROR;
return info;
/**
* Returns whether an individual dataset is present.
*/
bool NXClass::containsDataSet(const std::string &query) const {
return getDataSetInfo(query).stat != NX_ERROR;
//---------------------------------------------------------
// NXNote methods
//---------------------------------------------------------
std::string NXNote::author() {
if (!m_author_ok) {
NXChar aut = openNXChar("author");
aut.load();
m_author = std::string(aut(), aut.dim0());
m_author_ok = true;
}
return m_author;
}
std::vector<std::string> &NXNote::data() {
if (!m_data_ok) {
int rank;
int dims[4];
int type;
NXopendata(m_fileID, "data");
NXgetinfo(m_fileID, &rank, dims, &type);
int n = dims[0];
char *buffer = new char[n];
NXstatus stat = NXgetdata(m_fileID, buffer);
NXclosedata(m_fileID);
m_data.clear();
if (stat == NX_ERROR) {
return m_data;
}
std::istringstream istr(std::string(buffer, n));
delete[] buffer;
std::string line;
while (getline(istr, line)) {
m_data.push_back(line);
m_data_ok = true;
}
return m_data;
std::string NXNote::description() {
if (!m_description_ok) {
NXChar str = openNXChar("description");
str.load();
m_description = std::string(str(), str.dim0());
m_description_ok = true;
}
return m_description;
}
std::vector<char> &NXBinary::binary() {
if (!m_data_ok) {
int rank;
int dims[4];
int type;
NXopendata(m_fileID, "data");
NXgetinfo(m_fileID, &rank, dims, &type);
int n = dims[0];
m_binary.resize(n);
NXstatus stat = NXgetdata(m_fileID, &m_binary[0]);
(void)stat; // Avoid unused variable compiler warning
NXclosedata(m_fileID);
}
return m_binary;
}
//---------------------------------------------------------
// NXRoot methods
//---------------------------------------------------------
/** Constructor. On creation opens the Nexus file for reading only.
Janik Zikovsky
committed
* @param fname :: The file name to open
NXRoot::NXRoot(const std::string &fname) : m_filename(fname) {
// Open NeXus file
NXstatus stat = NXopen(m_filename.c_str(), NXACC_READ, &m_fileID);
if (stat == NX_ERROR) {
std::cout << "NXRoot: Error loading " << m_filename;
throw Kernel::Exception::FileError("Unable to open File:", m_filename);
}
readAllInfo();
}
/** Constructor.
* Creates a new Nexus file. The first root entry will be also created.
Janik Zikovsky
committed
* @param fname :: The file name to create
* @param entry :: The name of the first entry in the new file
NXRoot::NXRoot(const std::string &fname, const std::string &entry)
: m_filename(fname) {
(void)entry;
// Open NeXus file
NXstatus stat = NXopen(m_filename.c_str(), NXACC_CREATE5, &m_fileID);
if (stat == NX_ERROR) {
throw Kernel::Exception::FileError("Unable to open File:", m_filename);
}
NXRoot::~NXRoot() { NXclose(&m_fileID); }
bool NXRoot::isStandard() const { return true; }
/**
* Open the first NXentry in the file.
*/
NXEntry NXRoot::openFirstEntry() {
if (groups().empty()) {
throw std::runtime_error("NeXus file has no entries");
}
for (std::vector<NXClassInfo>::const_iterator grp = groups().begin();
grp != groups().end(); ++grp) {
if (grp->nxclass == "NXentry") {
return openEntry(grp->nxname);
}
}
throw std::runtime_error("NeXus file has no entries");
}
//---------------------------------------------------------
// NXDataSet methods
//---------------------------------------------------------
/** Constructor.
* @param parent :: The parent Nexus class. In terms of HDF it is the group
* containing the dataset.
Janik Zikovsky
committed
* @param name :: The name of the dataset relative to its parent
NXDataSet::NXDataSet(const NXClass &parent, const std::string &name)
: NXObject(parent.m_fileID, &parent, name) {
size_t i = name.find_last_of('/');
if (i == std::string::npos)
m_info.nxname = name;
else if (name.empty() || i == name.size() - 1)
throw std::runtime_error("Improper dataset name " + name);
m_info.nxname = name.substr(i + 1);
// Opens the data set. Does not read in any data. Call load(...) to load the
// data
void NXDataSet::open() {
if (i == std::string::npos || i == 0)
return; // we are in the root group, assume it is open
std::string group_path = m_path.substr(0, i);
if (NX_ERROR == NXopenpath(m_fileID, group_path.c_str())) {
throw std::runtime_error("Cannot open dataset " + m_path);
if (NXopendata(m_fileID, name().c_str()) != NX_OK) {
throw std::runtime_error("Error opening data in group \"" + name() + "\"");
}
if (NXgetinfo(m_fileID, &m_info.rank, m_info.dims, &m_info.type) != NX_OK) {
throw std::runtime_error("Error retrieving information for " + name() +
" group");
Gigg, Martyn Anthony
committed
NXclosedata(m_fileID);
void NXDataSet::openLocal() {
if (NXopendata(m_fileID, name().c_str()) != NX_OK) {
throw std::runtime_error("Error opening data in group \"" + name() + "\"");
}
if (NXgetinfo(m_fileID, &m_info.rank, m_info.dims, &m_info.type) != NX_OK) {
throw std::runtime_error("Error retrieving information for " + name() +
" group");
NXclosedata(m_fileID);
Gigg, Martyn Anthony
committed
/**
* The size of the first dimension of data
* @returns An integer indicating the size of the dimension.
* @throws out_of_range error if requested on an object of rank 0
*/
int NXDataSet::dim0() const {
if (m_info.rank == 0) {
throw std::out_of_range(
"NXDataSet::dim0() - Requested dimension greater than rank.");
Gigg, Martyn Anthony
committed
}
return m_info.dims[0];
}
/**
* The size of the second dimension of data
* @returns An integer indicating the size of the dimension
* @throws out_of_range error if requested on an object of rank < 2
*/
int NXDataSet::dim1() const {
if (m_info.rank < 2) {
throw std::out_of_range(
"NXDataSet::dim1() - Requested dimension greater than rank.");
Gigg, Martyn Anthony
committed
}
return m_info.dims[1];
}
/**
* The size of the third dimension of data
* @returns An integer indicating the size of the dimension
* @throws out_of_range error if requested on an object of rank < 3
*/
int NXDataSet::dim2() const {
if (m_info.rank < 3) {
throw std::out_of_range(
"NXDataSet::dim2() - Requested dimension greater than rank.");
Gigg, Martyn Anthony
committed
}
return m_info.dims[2];
}
/**
* The size of the fourth dimension of data
* @returns An integer indicating the size of the dimension
* @throws out_of_range error if requested on an object of rank < 4
*/
int NXDataSet::dim3() const {
if (m_info.rank < 4) {
throw std::out_of_range(
"NXDataSet::dim3() - Requested dimension greater than rank.");
Gigg, Martyn Anthony
committed
}
return m_info.dims[3];
}
Janik Zikovsky
committed
* @param data :: The pointer to the buffer accepting the data from the file.
* @throw runtime_error if the operation fails.
*/
void NXDataSet::getData(void *data) {
NXopendata(m_fileID, name().c_str());
if (NXgetdata(m_fileID, data) != NX_OK)
throw std::runtime_error("Cannot read data from NeXus file");
NXclosedata(m_fileID);
}
/** Wrapper to the NXgetslab.
Janik Zikovsky
committed
* @param data :: The pointer to the buffer accepting the data from the file.
* @param start :: The array of starting indeces to read in from the file. The
* size of the array must be equal to
* @param size :: The array of numbers of data elements to read along each
* dimenstion.
* The number of dimensions (the size of the array) must be equal to
* the rank of the data.
* @throw runtime_error if the operation fails.
*/
void NXDataSet::getSlab(void *data, int start[], int size[]) {
NXopendata(m_fileID, name().c_str());
if (NXgetslab(m_fileID, data, start, size) != NX_OK)
throw std::runtime_error("Cannot read data slab from NeXus file");
NXclosedata(m_fileID);
}
//---------------------------------------------------------
// NXData methods
//---------------------------------------------------------
NXData::NXData(const NXClass &parent, const std::string &name)
: NXMainClass(parent, name) {}
//---------------------------------------------------------
// NXLog methods
//---------------------------------------------------------
Gigg, Martyn Anthony
committed
/** Creates a property wrapper around the log entry
* @returns A valid property pointer or NULL
*/
Kernel::Property *NXLog::createProperty() {
Gigg, Martyn Anthony
committed
NXInfo vinfo = getDataSetInfo("time");
Gigg, Martyn Anthony
committed
return createSingleValueProperty();
Gigg, Martyn Anthony
committed
return createTimeSeries();
}
}
/** Creates a single value property of the log
* @returns A pointer to a newly created property wrapped around the log entry
*/
Kernel::Property *NXLog::createSingleValueProperty() {
Gigg, Martyn Anthony
committed
const std::string valAttr("value");
NXInfo vinfo = getDataSetInfo(valAttr);
Kernel::Property *prop;
int nxType = vinfo.type;
Gigg, Martyn Anthony
committed
prop = new Kernel::PropertyWithValue<double>(name(), getDouble(valAttr));
Gigg, Martyn Anthony
committed
prop = new Kernel::PropertyWithValue<int>(name(), getInt(valAttr));
} else if (nxType == NX_CHAR) {
prop =
new Kernel::PropertyWithValue<std::string>(name(), getString(valAttr));
} else if (nxType == NX_UINT8) {
NXDataSetTyped<unsigned char> value(*this, valAttr);
Gigg, Martyn Anthony
committed
value.load();
bool state = (value[0] == 0) ? false : true;
Gigg, Martyn Anthony
committed
prop = new Kernel::PropertyWithValue<bool>(name(), state);
Gigg, Martyn Anthony
committed
prop = NULL;
}
return prop;
}
* Create a TimeSeries property form the records of the NXLog group. Times are
* in dataset "time"
* and the values are in dataset "value"
* @param start_time :: If the "time" dataset does not have the "start"
* attribute sets the
Janik Zikovsky
committed
* @param new_name :: If not empty it is used as the TimeSeries property name
Kernel::Property *NXLog::createTimeSeries(const std::string &start_time,
const std::string &new_name) {
const std::string &logName = new_name.empty() ? name() : new_name;
NXInfo vinfo = getDataSetInfo("time");
if (vinfo.type == NX_FLOAT64) {
NXDouble times(*this, "time");
times.openLocal();
times.load();
std::string units = times.attributes("units");
if (units == "minutes") {
std::transform(times(), times() + times.dim0(), times(),
std::bind2nd(std::multiplies<double>(), 60));
} else if (!units.empty() && units.substr(0, 6) != "second") {
return NULL;
}
return parseTimeSeries(logName, times, start_time);
} else if (vinfo.type == NX_FLOAT32) {
NXFloat times(*this, "time");
times.openLocal();
times.load();
std::string units = times.attributes("units");
if (units == "minutes") {
std::transform(times(), times() + times.dim0(), times(),
std::bind2nd(std::multiplies<float>(), 60));
} else if (!units.empty() && units.substr(0, 6) != "second") {