15#include <highfive/H5Attribute.hpp>
16#include <highfive/H5DataSet.hpp>
17#include <highfive/H5DataSpace.hpp>
18#include <highfive/H5DataType.hpp>
19#include <highfive/H5File.hpp>
20#include <highfive/H5Group.hpp>
22namespace h5 = HighFive;
23typedef h5::details::Boolean H5Boolean;
32Storage::Storage(
string fname,
bool write) : m_write(write)
35 m_file = make_unique<h5::File>(fname, h5::File::OpenOrCreate);
38 m_file = make_unique<h5::File>(fullName, h5::File::ReadOnly);
47void Storage::setCompressionLevel(
int level)
49 if (level < 0 || level > 9) {
50 throw CanteraError(
"Storage::setCompressionLevel",
51 "Invalid compression level '{}' (needs to be 0..9).", level);
53 m_compressionLevel = level;
56bool Storage::hasGroup(
const string&
id)
const
58 if (!m_file->exist(
id)) {
61 if (m_file->getObjectType(
id) != h5::ObjectType::Group) {
67bool Storage::checkGroupRead(
const string&
id)
const
69 vector<string> tokens;
71 string grp = tokens[0];
73 throw CanteraError(
"Storage::checkGroupRead",
74 "No group with id '{}' found at root.", grp);
78 h5::Group sub = m_file->getGroup(grp);
79 tokens.erase(tokens.begin());
80 for (
auto& grp : tokens) {
81 if (!hasGroup(path +
"/" + grp)) {
82 throw CanteraError(
"Storage::checkGroupRead",
83 "No group with id '{}' found at '{}'.", grp, path);
86 sub = sub.getGroup(grp);
91bool Storage::checkGroupWrite(
const string&
id,
bool permissive)
94 throw CanteraError(
"Storage::checkGroupWrite",
95 "Cannot write to file opened in read mode.");
98 throw CanteraError(
"Storage::checkGroupWrite",
99 "Cannot write to empty group id '' (root location).");
101 if (!m_file->exist(
id)) {
103 throw CanteraError(
"Storage::checkGroupWrite",
104 "Specified group with id '{}' does not exist.",
id);
106 m_file->createGroup(
id);
109 if (m_file->getObjectType(
id) != h5::ObjectType::Group) {
110 throw CanteraError(
"Storage::checkGroupWrite",
111 "Unable to write to existing object with id '{}'.",
id);
116bool Storage::checkGroup(
const string&
id,
bool permissive)
120 return checkGroupWrite(
id, permissive);
122 return checkGroupRead(
id);
123 }
catch (
const CanteraError& err) {
127 throw CanteraError(
"Storage::checkGroup", err.getMessage());
128 }
catch (
const std::exception& err) {
133 throw CanteraError(
"Storage::checkGroup",
134 "Encountered exception for group '{}':\n{}",
id, err.what());
138void Storage::deleteGroup(
const string&
id)
142 }
catch (
const std::exception& err) {
144 throw CanteraError(
"Storage::deleteGroup",
145 "Encountered exception while deleting group '{}':\n{}",
id, err.what());
149pair<size_t, set<string>> Storage::contents(
const string&
id)
const
153 }
catch (
const CanteraError& err) {
154 throw CanteraError(
"Storage::contents",
155 "Caught exception for group '{}':\n",
id, err.getMessage());
157 h5::Group sub = m_file->getGroup(
id);
160 size_t nElements = 0;
161 for (
auto& name : sub.listObjectNames()) {
162 if (sub.getObjectType(name) == h5::ObjectType::Dataset) {
163 h5::DataSpace space = sub.getDataSet(name).getSpace();
165 if (space.getNumberDimensions() < nDims) {
166 nDims = space.getNumberDimensions();
167 nElements = space.getElementCount();
171 if (nDims != 1 && nDims != npos) {
172 throw NotImplementedError(
"Storage::content",
173 "Encountered invalid data with {} dimensions.", nDims);
175 return std::make_pair(nElements, names);
178AnyMap readH5Attributes(
const h5::Group& sub,
bool recursive)
182 for (
auto& name : sub.listAttributeNames()) {
183 h5::Attribute attr = sub.getAttribute(name);
184 h5::DataType dtype = attr.getDataType();
185 h5::DataTypeClass dclass = dtype.getClass();
186 if (dclass == h5::DataTypeClass::Float) {
187 if (attr.getSpace().getElementCount() > 1) {
188 vector<double> values;
196 }
else if (dclass == h5::DataTypeClass::Integer) {
197 if (attr.getSpace().getElementCount() > 1) {
198 vector<long int> values;
206 }
else if (dclass == h5::DataTypeClass::String) {
207 if (attr.getSpace().getElementCount() > 1) {
208 vector<string> values;
216 }
else if (dclass == h5::DataTypeClass::Enum) {
218 if (attr.getSpace().getElementCount() > 1) {
219 vector<H5Boolean> values;
221 vector<bool> bValues;
222 for (
auto v : values) {
223 bValues.push_back(
bool(v));
229 out[name] = bool(value);
232 throw NotImplementedError(
"readH5Attributes",
233 "Unable to read attribute '{}' with type '{}'", name, dtype.string());
238 for (
auto& name : sub.listObjectNames()) {
239 if (sub.getObjectType(name) == h5::ObjectType::Group) {
240 out[name] = readH5Attributes(sub.getGroup(name), recursive);
248bool Storage::hasAttribute(
const string&
id,
const string& attr)
const
252 }
catch (
const CanteraError& err) {
253 throw CanteraError(
"Storage::hasAttribute",
254 "Caught exception for group '{}':\n",
id, err.getMessage());
256 h5::Group sub = m_file->getGroup(
id);
257 auto names = sub.listAttributeNames();
258 return std::find(names.begin(), names.end(), attr) != names.end();
261AnyMap Storage::readAttributes(
const string&
id,
bool recursive)
const
265 h5::Group sub = m_file->getGroup(
id);
266 return readH5Attributes(sub, recursive);
268 throw NotImplementedError(
"Storage::readAttribute",
270 }
catch (
const CanteraError& err) {
271 throw CanteraError(
"Storage::readAttribute",
272 "Caught exception for group '{}':\n",
id, err.getMessage());
276void writeH5Attributes(h5::Group sub,
const AnyMap& meta)
278 for (
auto& [name, item] : meta) {
279 if (sub.hasAttribute(name)) {
280 throw NotImplementedError(
"writeH5Attributes",
281 "Unable to overwrite existing Attribute '{}'", name);
283 if (item.is<
long int>()) {
284 int value = item.asInt();
285 h5::Attribute attr = sub.createAttribute<
long int>(
286 name, h5::DataSpace::From(value));
288 }
else if (item.is<
double>()) {
289 double value = item.asDouble();
290 h5::Attribute attr = sub.createAttribute<
double>(
291 name, h5::DataSpace::From(value));
293 }
else if (item.is<
string>()) {
294 string value = item.asString();
295 h5::Attribute attr = sub.createAttribute<
string>(
296 name, h5::DataSpace::From(value));
298 }
else if (item.is<
bool>()) {
299 bool bValue = item.asBool();
300 H5Boolean value = bValue ?
301 H5Boolean::HighFiveTrue : H5Boolean::HighFiveFalse;
302 h5::Attribute attr = sub.createAttribute<H5Boolean>(
303 name, h5::DataSpace::From(value));
305 }
else if (item.is<vector<long int>>()) {
306 auto values = item.as<vector<long int>>();
307 h5::Attribute attr = sub.createAttribute<
long int>(
308 name, h5::DataSpace::From(values));
310 }
else if (item.is<vector<double>>()) {
311 auto values = item.as<vector<double>>();
312 h5::Attribute attr = sub.createAttribute<
double>(
313 name, h5::DataSpace::From(values));
315 }
else if (item.is<vector<string>>()) {
316 auto values = item.as<vector<string>>();
317 h5::Attribute attr = sub.createAttribute<
string>(
318 name, h5::DataSpace::From(values));
320 }
else if (item.is<vector<bool>>()) {
321 auto bValue = item.as<vector<bool>>();
322 vector<H5Boolean> values;
323 for (
auto b : bValue) {
325 H5Boolean::HighFiveTrue : H5Boolean::HighFiveFalse);
327 h5::Attribute attr = sub.createAttribute<H5Boolean>(
328 name, h5::DataSpace::From(values));
330 }
else if (item.is<AnyMap>()) {
332 auto value = item.as<AnyMap>();
333 auto grp = sub.createGroup(name);
334 writeH5Attributes(grp, value);
336 throw NotImplementedError(
"writeH5Attributes",
337 "Unable to write attribute '{}' with type '{}'",
338 name, item.type_str());
343void Storage::writeAttributes(
const string&
id,
const AnyMap& meta)
346 checkGroupWrite(
id,
false);
347 h5::Group sub = m_file->getGroup(
id);
348 writeH5Attributes(sub, meta);
350 throw NotImplementedError(
"Storage::writeAttribute",
352 }
catch (
const CanteraError& err) {
354 throw CanteraError(
"Storage::writeAttributes",
"{}", err.getMessage());
355 }
catch (
const std::exception& err) {
357 throw CanteraError(
"Storage::writeAttributes",
358 "Encountered exception for group '{}':\n{}",
id, err.what());
362AnyValue Storage::readData(
const string&
id,
363 const string& name,
size_t rows,
size_t cols)
const
367 }
catch (
const CanteraError& err) {
368 throw CanteraError(
"Storage::readData",
369 "Caught exception for group '{}':\n",
id, err.getMessage());
371 h5::Group sub = m_file->getGroup(
id);
372 if (!sub.exist(name)) {
373 throw CanteraError(
"Storage::readData",
374 "DataSet '{}' not found in group '{}'.", name,
id);
376 h5::DataSet dataset = sub.getDataSet(name);
377 h5::DataSpace space = dataset.getSpace();
378 size_t ndim = space.getNumberDimensions();
379 if (cols == 0 && ndim != 1) {
380 throw CanteraError(
"Storage::readData",
381 "Shape of DataSet '{}' is inconsistent; expected one dimensions but "
382 "received {}.", name, ndim);
383 }
else if (cols != 0 && cols != npos && ndim != 2) {
384 throw CanteraError(
"Storage::readData",
385 "Shape of DataSet '{}' is inconsistent; expected two dimensions but "
386 "received {}.", name, ndim);
388 if (ndim == 0 || ndim > 2) {
389 throw NotImplementedError(
"Storage::readData",
390 "Cannot process DataSet '{}' as data has {} dimensions.", name, ndim);
392 const auto& shape = space.getDimensions();
393 if (shape[0] != rows) {
394 throw CanteraError(
"Storage::readData",
395 "Shape of DataSet '{}' is inconsistent; expected {} rows "
396 "but received {}.", name, rows, shape[0]);
398 if (cols != 0 && cols != npos && shape[1] != cols) {
399 throw CanteraError(
"Storage::readData",
400 "Shape of DataSet '{}' is inconsistent; expected {} columns "
401 "but received {}.", name, cols, shape[1]);
404 const auto datatype = dataset.getDataType().getClass();
405 if (datatype == h5::DataTypeClass::Float) {
412 vector<vector<double>> data;
416 }
catch (
const std::exception& err) {
417 throw NotImplementedError(
"Storage::readData",
418 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
419 name,
id, err.what());
421 }
else if (datatype == h5::DataTypeClass::Integer) {
424 vector<long int> data;
428 vector<vector<long int>> data;
432 }
catch (
const std::exception& err) {
433 throw NotImplementedError(
"Storage::readData",
434 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
435 name,
id, err.what());
437 }
else if (datatype == h5::DataTypeClass::String) {
444 vector<vector<string>> data;
448 }
catch (
const std::exception& err) {
449 throw NotImplementedError(
"Storage::readData",
450 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
451 name,
id, err.what());
454 throw NotImplementedError(
"Storage::readData",
455 "DataSet '{}' is not readable.", name);
460void Storage::writeData(
const string&
id,
const string& name,
const AnyValue& data)
463 checkGroupWrite(
id,
false);
464 }
catch (
const CanteraError& err) {
466 throw CanteraError(
"Storage::writeData",
"{}", err.getMessage());
467 }
catch (
const std::exception& err) {
469 throw CanteraError(
"Storage::writeData",
470 "Encountered exception for group '{}':\n{}",
id, err.what());
472 h5::Group sub = m_file->getGroup(
id);
473 if (sub.exist(name)) {
474 throw NotImplementedError(
"Storage::writeData",
475 "Unable to overwrite existing DataSet '{}' in group '{}'.", name,
id);
477 size_t size = data.vectorSize();
478 auto [rows, cols] = data.matrixShape();
479 if (size == npos && rows == npos) {
480 throw CanteraError(
"Storage::writeData",
481 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
482 "'{}'\nis neither a vector nor a matrix.", name,
id, data.type_str());
484 vector<size_t> dims{data.vectorSize()};
485 if (data.isVector<
long int>()) {
486 h5::DataSet dataset = sub.createDataSet<
long int>(name, h5::DataSpace(dims));
487 dataset.write(data.asVector<
long int>());
490 if (data.isVector<
double>()) {
491 h5::DataSet dataset = sub.createDataSet<
double>(name, h5::DataSpace(dims));
492 dataset.write(data.asVector<
double>());
495 if (data.isVector<
string>()) {
496 h5::DataSet dataset = sub.createDataSet<
string>(name, h5::DataSpace(dims));
497 dataset.write(data.asVector<
string>());
502 dims.push_back(rows);
503 dims.push_back(cols);
505 throw NotImplementedError(
"Storage::writeData",
506 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
507 "'{}'\nis not supported.", name,
id, data.type_str());
509 if (m_compressionLevel) {
512 h5::DataSpace space(dims, dims);
513 h5::DataSetCreateProps props;
514 props.add(h5::Chunking(vector<hsize_t>{dims[0], dims[1]}));
515 props.add(h5::Deflate(m_compressionLevel));
516 if (data.isVector<vector<long int>>()) {
517 h5::DataSet dataset = sub.createDataSet<
long int>(name, space, props);
518 dataset.write(data.asVector<vector<long int>>());
519 }
else if (data.isVector<vector<double>>()) {
520 h5::DataSet dataset = sub.createDataSet<
double>(name, space, props);
521 dataset.write(data.asVector<vector<double>>());
522 }
else if (data.isVector<vector<string>>()) {
523 h5::DataSet dataset = sub.createDataSet<
string>(name, space, props);
524 dataset.write(data.asVector<vector<string>>());
526 throw NotImplementedError(
"Storage::writeData",
527 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
528 "'{}'\nis not supported.", name,
id, data.type_str());
531 h5::DataSpace space(dims);
532 if (data.isVector<vector<long int>>()) {
533 h5::DataSet dataset = sub.createDataSet<
long int>(name, space);
534 dataset.write(data.asVector<vector<long int>>());
535 }
else if (data.isVector<vector<double>>()) {
536 h5::DataSet dataset = sub.createDataSet<
double>(name, space);
537 dataset.write(data.asVector<vector<double>>());
538 }
else if (data.isVector<vector<string>>()) {
539 h5::DataSet dataset = sub.createDataSet<
string>(name, space);
540 dataset.write(data.asVector<vector<string>>());
542 throw NotImplementedError(
"Storage::writeData",
543 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
544 "'{}'\nis not supported.", name,
id, data.type_str());
551Storage::Storage(
string fname,
bool write)
553 throw CanteraError(
"Storage::Storage",
554 "Saving to HDF requires HighFive installation.");
561void Storage::setCompressionLevel(
int level)
564 "Saving to HDF requires HighFive installation.");
567bool Storage::hasGroup(
const string&
id)
const
570 "Saving to HDF requires HighFive installation.");
573bool Storage::checkGroup(
const string&
id,
bool permissive)
576 "Saving to HDF requires HighFive installation.");
579void Storage::deleteGroup(
const string&
id)
582 "Saving to HDF requires HighFive installation.");
585pair<size_t, set<string>> Storage::contents(
const string&
id)
const
588 "Saving to HDF requires HighFive installation.");
591bool Storage::hasAttribute(
const string&
id,
const string& attr)
const
594 "Saving to HDF requires HighFive installation.");
597AnyMap Storage::readAttributes(
const string&
id,
bool recursive)
const
600 "Saving to HDF requires HighFive installation.");
603void Storage::writeAttributes(
const string&
id,
const AnyMap& meta)
606 "Saving to HDF requires HighFive installation.");
610 const string& name,
size_t rows,
size_t cols)
const
613 "Saving to HDF requires HighFive installation.");
616void Storage::writeData(
const string&
id,
617 const string& name,
const AnyValue& data)
620 "Saving to HDF requires HighFive installation.");
A map of string keys to values whose type can vary at runtime.
A wrapper for a variable whose type is determined at runtime.
Base class for exceptions thrown by Cantera classes.
virtual string getMessage() const
Method overridden by derived classes to format the error message.
An error indicating that an unimplemented function has been called.
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
void tokenizePath(const string &in_val, vector< string > &v)
This function separates a string up into tokens according to the location of path separators.
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"