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
70 string grp = tokens[0];
72 throw CanteraError(
"Storage::checkGroupRead",
73 "No group with id '{}' found at root.", grp);
77 h5::Group sub = m_file->getGroup(grp);
78 tokens.erase(tokens.begin());
79 for (
auto& grp : tokens) {
80 if (!hasGroup(path +
"/" + grp)) {
81 throw CanteraError(
"Storage::checkGroupRead",
82 "No group with id '{}' found at '{}'.", grp, path);
85 sub = sub.getGroup(grp);
90bool Storage::checkGroupWrite(
const string&
id,
bool permissive)
93 throw CanteraError(
"Storage::checkGroupWrite",
94 "Cannot write to file opened in read mode.");
97 throw CanteraError(
"Storage::checkGroupWrite",
98 "Cannot write to empty group id '' (root location).");
100 if (!m_file->exist(
id)) {
102 throw CanteraError(
"Storage::checkGroupWrite",
103 "Specified group with id '{}' does not exist.",
id);
105 m_file->createGroup(
id);
108 if (m_file->getObjectType(
id) != h5::ObjectType::Group) {
109 throw CanteraError(
"Storage::checkGroupWrite",
110 "Unable to write to existing object with id '{}'.",
id);
115bool Storage::checkGroup(
const string&
id,
bool permissive)
119 return checkGroupWrite(
id, permissive);
121 return checkGroupRead(
id);
122 }
catch (
const CanteraError& err) {
126 throw CanteraError(
"Storage::checkGroup", err.getMessage());
127 }
catch (
const std::exception& err) {
132 throw CanteraError(
"Storage::checkGroup",
133 "Encountered exception for group '{}':\n{}",
id, err.what());
137void Storage::deleteGroup(
const string&
id)
141 }
catch (
const std::exception& err) {
143 throw CanteraError(
"Storage::deleteGroup",
144 "Encountered exception while deleting group '{}':\n{}",
id, err.what());
148pair<size_t, set<string>> Storage::contents(
const string&
id)
const
152 }
catch (
const CanteraError& err) {
153 throw CanteraError(
"Storage::contents",
154 "Caught exception for group '{}':\n",
id, err.getMessage());
156 h5::Group sub = m_file->getGroup(
id);
159 size_t nElements = 0;
160 for (
auto& name : sub.listObjectNames()) {
161 if (sub.getObjectType(name) == h5::ObjectType::Dataset) {
162 h5::DataSpace space = sub.getDataSet(name).getSpace();
164 if (space.getNumberDimensions() < nDims) {
165 nDims = space.getNumberDimensions();
166 nElements = space.getElementCount();
170 if (nDims != 1 && nDims != npos) {
171 throw NotImplementedError(
"Storage::content",
172 "Encountered invalid data with {} dimensions.", nDims);
174 return std::make_pair(nElements, names);
177AnyMap readH5Attributes(
const h5::Group& sub,
bool recursive)
181 for (
auto& name : sub.listAttributeNames()) {
182 h5::Attribute attr = sub.getAttribute(name);
183 h5::DataType dtype = attr.getDataType();
184 h5::DataTypeClass dclass = dtype.getClass();
185 if (dclass == h5::DataTypeClass::Float) {
186 if (attr.getSpace().getElementCount() > 1) {
187 vector<double> values;
195 }
else if (dclass == h5::DataTypeClass::Integer) {
196 if (attr.getSpace().getElementCount() > 1) {
197 vector<long int> values;
205 }
else if (dclass == h5::DataTypeClass::String) {
206 if (attr.getSpace().getElementCount() > 1) {
207 vector<string> values;
215 }
else if (dclass == h5::DataTypeClass::Enum) {
217 if (attr.getSpace().getElementCount() > 1) {
218 vector<H5Boolean> values;
220 vector<bool> bValues;
221 for (
auto v : values) {
222 bValues.push_back(
bool(v));
228 out[name] = bool(value);
231 throw NotImplementedError(
"readH5Attributes",
232 "Unable to read attribute '{}' with type '{}'", name, dtype.string());
237 for (
auto& name : sub.listObjectNames()) {
238 if (sub.getObjectType(name) == h5::ObjectType::Group) {
239 out[name] = readH5Attributes(sub.getGroup(name), recursive);
247bool Storage::hasAttribute(
const string&
id,
const string& attr)
const
251 }
catch (
const CanteraError& err) {
252 throw CanteraError(
"Storage::hasAttribute",
253 "Caught exception for group '{}':\n",
id, err.getMessage());
255 h5::Group sub = m_file->getGroup(
id);
256 auto names = sub.listAttributeNames();
257 return std::find(names.begin(), names.end(), attr) != names.end();
260AnyMap Storage::readAttributes(
const string&
id,
bool recursive)
const
264 h5::Group sub = m_file->getGroup(
id);
265 return readH5Attributes(sub, recursive);
267 throw NotImplementedError(
"Storage::readAttribute",
269 }
catch (
const CanteraError& err) {
270 throw CanteraError(
"Storage::readAttribute",
271 "Caught exception for group '{}':\n",
id, err.getMessage());
275void writeH5Attributes(h5::Group sub,
const AnyMap& meta)
277 for (
auto& [name, item] : meta) {
278 if (sub.hasAttribute(name)) {
279 throw NotImplementedError(
"writeH5Attributes",
280 "Unable to overwrite existing Attribute '{}'", name);
282 if (item.is<
long int>()) {
283 int value = item.asInt();
284 h5::Attribute attr = sub.createAttribute<
long int>(
285 name, h5::DataSpace::From(value));
287 }
else if (item.is<
double>()) {
288 double value = item.asDouble();
289 h5::Attribute attr = sub.createAttribute<
double>(
290 name, h5::DataSpace::From(value));
292 }
else if (item.is<
string>()) {
293 string value = item.asString();
294 h5::Attribute attr = sub.createAttribute<
string>(
295 name, h5::DataSpace::From(value));
297 }
else if (item.is<
bool>()) {
298 bool bValue = item.asBool();
299 H5Boolean value = bValue ?
300 H5Boolean::HighFiveTrue : H5Boolean::HighFiveFalse;
301 h5::Attribute attr = sub.createAttribute<H5Boolean>(
302 name, h5::DataSpace::From(value));
304 }
else if (item.is<vector<long int>>()) {
305 auto values = item.as<vector<long int>>();
306 h5::Attribute attr = sub.createAttribute<
long int>(
307 name, h5::DataSpace::From(values));
309 }
else if (item.is<vector<double>>()) {
310 auto values = item.as<vector<double>>();
311 h5::Attribute attr = sub.createAttribute<
double>(
312 name, h5::DataSpace::From(values));
314 }
else if (item.is<vector<string>>()) {
315 auto values = item.as<vector<string>>();
316 h5::Attribute attr = sub.createAttribute<
string>(
317 name, h5::DataSpace::From(values));
319 }
else if (item.is<vector<bool>>()) {
320 auto bValue = item.as<vector<bool>>();
321 vector<H5Boolean> values;
322 for (
auto b : bValue) {
324 H5Boolean::HighFiveTrue : H5Boolean::HighFiveFalse);
326 h5::Attribute attr = sub.createAttribute<H5Boolean>(
327 name, h5::DataSpace::From(values));
329 }
else if (item.is<AnyMap>()) {
331 auto value = item.as<AnyMap>();
332 auto grp = sub.createGroup(name);
333 writeH5Attributes(grp, value);
335 throw NotImplementedError(
"writeH5Attributes",
336 "Unable to write attribute '{}' with type '{}'",
337 name, item.type_str());
342void Storage::writeAttributes(
const string&
id,
const AnyMap& meta)
345 checkGroupWrite(
id,
false);
346 h5::Group sub = m_file->getGroup(
id);
347 writeH5Attributes(sub, meta);
349 throw NotImplementedError(
"Storage::writeAttribute",
351 }
catch (
const CanteraError& err) {
353 throw CanteraError(
"Storage::writeAttributes",
"{}", err.getMessage());
354 }
catch (
const std::exception& err) {
356 throw CanteraError(
"Storage::writeAttributes",
357 "Encountered exception for group '{}':\n{}",
id, err.what());
361AnyValue Storage::readData(
const string&
id,
362 const string& name,
size_t rows,
size_t cols)
const
366 }
catch (
const CanteraError& err) {
367 throw CanteraError(
"Storage::readData",
368 "Caught exception for group '{}':\n",
id, err.getMessage());
370 h5::Group sub = m_file->getGroup(
id);
371 if (!sub.exist(name)) {
372 throw CanteraError(
"Storage::readData",
373 "DataSet '{}' not found in group '{}'.", name,
id);
375 h5::DataSet dataset = sub.getDataSet(name);
376 h5::DataSpace space = dataset.getSpace();
377 size_t ndim = space.getNumberDimensions();
378 if (cols == 0 && ndim != 1) {
379 throw CanteraError(
"Storage::readData",
380 "Shape of DataSet '{}' is inconsistent; expected one dimensions but "
381 "received {}.", name, ndim);
382 }
else if (cols != 0 && cols != npos && ndim != 2) {
383 throw CanteraError(
"Storage::readData",
384 "Shape of DataSet '{}' is inconsistent; expected two dimensions but "
385 "received {}.", name, ndim);
387 if (ndim == 0 || ndim > 2) {
388 throw NotImplementedError(
"Storage::readData",
389 "Cannot process DataSet '{}' as data has {} dimensions.", name, ndim);
391 const auto& shape = space.getDimensions();
392 if (shape[0] != rows) {
393 throw CanteraError(
"Storage::readData",
394 "Shape of DataSet '{}' is inconsistent; expected {} rows "
395 "but received {}.", name, rows, shape[0]);
397 if (cols != 0 && cols != npos && shape[1] != cols) {
398 throw CanteraError(
"Storage::readData",
399 "Shape of DataSet '{}' is inconsistent; expected {} columns "
400 "but received {}.", name, cols, shape[1]);
403 const auto datatype = dataset.getDataType().getClass();
404 if (datatype == h5::DataTypeClass::Float) {
411 vector<vector<double>> data;
415 }
catch (
const std::exception& err) {
416 throw NotImplementedError(
"Storage::readData",
417 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
418 name,
id, err.what());
420 }
else if (datatype == h5::DataTypeClass::Integer) {
423 vector<long int> data;
427 vector<vector<long int>> data;
431 }
catch (
const std::exception& err) {
432 throw NotImplementedError(
"Storage::readData",
433 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
434 name,
id, err.what());
436 }
else if (datatype == h5::DataTypeClass::String) {
443 vector<vector<string>> data;
447 }
catch (
const std::exception& err) {
448 throw NotImplementedError(
"Storage::readData",
449 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
450 name,
id, err.what());
453 throw NotImplementedError(
"Storage::readData",
454 "DataSet '{}' is not readable.", name);
459void Storage::writeData(
const string&
id,
const string& name,
const AnyValue& data)
462 checkGroupWrite(
id,
false);
463 }
catch (
const CanteraError& err) {
465 throw CanteraError(
"Storage::writeData",
"{}", err.getMessage());
466 }
catch (
const std::exception& err) {
468 throw CanteraError(
"Storage::writeData",
469 "Encountered exception for group '{}':\n{}",
id, err.what());
471 h5::Group sub = m_file->getGroup(
id);
472 if (sub.exist(name)) {
473 throw NotImplementedError(
"Storage::writeData",
474 "Unable to overwrite existing DataSet '{}' in group '{}'.", name,
id);
476 size_t size = data.vectorSize();
477 auto [rows, cols] = data.matrixShape();
478 if (size == npos && rows == npos) {
479 throw CanteraError(
"Storage::writeData",
480 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
481 "'{}'\nis neither a vector nor a matrix.", name,
id, data.type_str());
483 vector<size_t> dims{data.vectorSize()};
484 if (data.isVector<
long int>()) {
485 h5::DataSet dataset = sub.createDataSet<
long int>(name, h5::DataSpace(dims));
486 dataset.write(data.asVector<
long int>());
489 if (data.isVector<
double>()) {
490 h5::DataSet dataset = sub.createDataSet<
double>(name, h5::DataSpace(dims));
491 dataset.write(data.asVector<
double>());
494 if (data.isVector<
string>()) {
495 h5::DataSet dataset = sub.createDataSet<
string>(name, h5::DataSpace(dims));
496 dataset.write(data.asVector<
string>());
501 dims.push_back(rows);
502 dims.push_back(cols);
504 throw NotImplementedError(
"Storage::writeData",
505 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
506 "'{}'\nis not supported.", name,
id, data.type_str());
508 if (m_compressionLevel) {
511 h5::DataSpace space(dims, dims);
512 h5::DataSetCreateProps props;
513 props.add(h5::Chunking(vector<hsize_t>{dims[0], dims[1]}));
514 props.add(h5::Deflate(m_compressionLevel));
515 if (data.isVector<vector<long int>>()) {
516 h5::DataSet dataset = sub.createDataSet<
long int>(name, space, props);
517 dataset.write(data.asVector<vector<long int>>());
518 }
else if (data.isVector<vector<double>>()) {
519 h5::DataSet dataset = sub.createDataSet<
double>(name, space, props);
520 dataset.write(data.asVector<vector<double>>());
521 }
else if (data.isVector<vector<string>>()) {
522 h5::DataSet dataset = sub.createDataSet<
string>(name, space, props);
523 dataset.write(data.asVector<vector<string>>());
525 throw NotImplementedError(
"Storage::writeData",
526 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
527 "'{}'\nis not supported.", name,
id, data.type_str());
530 h5::DataSpace space(dims);
531 if (data.isVector<vector<long int>>()) {
532 h5::DataSet dataset = sub.createDataSet<
long int>(name, space);
533 dataset.write(data.asVector<vector<long int>>());
534 }
else if (data.isVector<vector<double>>()) {
535 h5::DataSet dataset = sub.createDataSet<
double>(name, space);
536 dataset.write(data.asVector<vector<double>>());
537 }
else if (data.isVector<vector<string>>()) {
538 h5::DataSet dataset = sub.createDataSet<
string>(name, space);
539 dataset.write(data.asVector<vector<string>>());
541 throw NotImplementedError(
"Storage::writeData",
542 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
543 "'{}'\nis not supported.", name,
id, data.type_str());
550Storage::Storage(
string fname,
bool write)
552 throw CanteraError(
"Storage::Storage",
553 "Saving to HDF requires HighFive installation.");
560void Storage::setCompressionLevel(
int level)
563 "Saving to HDF requires HighFive installation.");
566bool Storage::hasGroup(
const string&
id)
const
569 "Saving to HDF requires HighFive installation.");
572bool Storage::checkGroup(
const string&
id,
bool permissive)
575 "Saving to HDF requires HighFive installation.");
578void Storage::deleteGroup(
const string&
id)
581 "Saving to HDF requires HighFive installation.");
584pair<size_t, set<string>> Storage::contents(
const string&
id)
const
587 "Saving to HDF requires HighFive installation.");
590bool Storage::hasAttribute(
const string&
id,
const string& attr)
const
593 "Saving to HDF requires HighFive installation.");
596AnyMap Storage::readAttributes(
const string&
id,
bool recursive)
const
599 "Saving to HDF requires HighFive installation.");
602void Storage::writeAttributes(
const string&
id,
const AnyMap& meta)
605 "Saving to HDF requires HighFive installation.");
609 const string& name,
size_t rows,
size_t cols)
const
612 "Saving to HDF requires HighFive installation.");
615void Storage::writeData(
const string&
id,
616 const string& name,
const AnyValue& data)
619 "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,...
vector< string > tokenizePath(const string &in_val)
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"