14#if CT_USE_SYSTEM_HIGHFIVE
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>
22 #include "cantera/ext/HighFive/H5Attribute.hpp"
23 #include "cantera/ext/HighFive/H5DataSet.hpp"
24 #include "cantera/ext/HighFive/H5DataSpace.hpp"
25 #include "cantera/ext/HighFive/H5DataType.hpp"
26 #include "cantera/ext/HighFive/H5File.hpp"
27 #include "cantera/ext/HighFive/H5Group.hpp"
30namespace h5 = HighFive;
32#ifdef CT_USE_HIGHFIVE_BOOLEAN
34typedef h5::details::Boolean H5Boolean;
43h5::EnumType<H5Boolean> create_enum_boolean() {
44 return {{
"FALSE", H5Boolean::HighFiveFalse}, {
"TRUE", H5Boolean::HighFiveTrue}};
47HIGHFIVE_REGISTER_TYPE(H5Boolean, ::create_enum_boolean)
57Storage::Storage(
string fname,
bool write) : m_write(write)
60 m_file = make_unique<h5::File>(fname, h5::File::OpenOrCreate);
62 m_file = make_unique<h5::File>(fname, h5::File::ReadOnly);
71void Storage::setCompressionLevel(
int level)
73 if (level < 0 || level > 9) {
74 throw CanteraError(
"Storage::setCompressionLevel",
75 "Invalid compression level '{}' (needs to be 0..9).", level);
77 m_compressionLevel = level;
80bool Storage::hasGroup(
const string&
id)
const
82 if (!m_file->exist(
id)) {
85 if (m_file->getObjectType(
id) != h5::ObjectType::Group) {
91bool Storage::checkGroupRead(
const string&
id)
const
93 vector<string> tokens;
95 string grp = tokens[0];
97 throw CanteraError(
"Storage::checkGroupRead",
98 "No group with id '{}' found at root.", grp);
102 h5::Group sub = m_file->getGroup(grp);
103 tokens.erase(tokens.begin());
104 for (
auto& grp : tokens) {
105 if (!hasGroup(path +
"/" + grp)) {
106 throw CanteraError(
"Storage::checkGroupRead",
107 "No group with id '{}' found at '{}'.", grp, path);
110 sub = sub.getGroup(grp);
115bool Storage::checkGroupWrite(
const string&
id,
bool permissive)
118 throw CanteraError(
"Storage::checkGroupWrite",
119 "Cannot write to file opened in read mode.");
122 throw CanteraError(
"Storage::checkGroupWrite",
123 "Cannot write to empty group id '' (root location).");
125 if (!m_file->exist(
id)) {
127 throw CanteraError(
"Storage::checkGroupWrite",
128 "Specified group with id '{}' does not exist.",
id);
130 m_file->createGroup(
id);
133 if (m_file->getObjectType(
id) != h5::ObjectType::Group) {
134 throw CanteraError(
"Storage::checkGroupWrite",
135 "Unable to write to existing object with id '{}'.",
id);
140bool Storage::checkGroup(
const string&
id,
bool permissive)
144 return checkGroupWrite(
id, permissive);
146 return checkGroupRead(
id);
147 }
catch (
const CanteraError& err) {
151 throw CanteraError(
"Storage::checkGroup", err.getMessage());
152 }
catch (
const std::exception& err) {
157 throw CanteraError(
"Storage::checkGroup",
158 "Encountered exception for group '{}':\n{}",
id, err.what());
162void Storage::deleteGroup(
const string&
id)
166 }
catch (
const std::exception& err) {
168 throw CanteraError(
"Storage::deleteGroup",
169 "Encountered exception while deleting group '{}':\n{}",
id, err.what());
173pair<size_t, set<string>> Storage::contents(
const string&
id)
const
177 }
catch (
const CanteraError& err) {
178 throw CanteraError(
"Storage::contents",
179 "Caught exception for group '{}':\n",
id, err.getMessage());
181 h5::Group sub = m_file->getGroup(
id);
184 size_t nElements = 0;
185 for (
auto& name : sub.listObjectNames()) {
186 if (sub.getObjectType(name) == h5::ObjectType::Dataset) {
187 h5::DataSpace space = sub.getDataSet(name).getSpace();
189 if (space.getNumberDimensions() < nDims) {
190 nDims = space.getNumberDimensions();
191 nElements = space.getElementCount();
195 if (nDims != 1 && nDims != npos) {
196 throw NotImplementedError(
"Storage::content",
197 "Encountered invalid data with {} dimensions.", nDims);
199 return std::make_pair(nElements, names);
202AnyMap readH5Attributes(
const h5::Group& sub,
bool recursive)
206 for (
auto& name : sub.listAttributeNames()) {
207 h5::Attribute attr = sub.getAttribute(name);
208 h5::DataType dtype = attr.getDataType();
209 h5::DataTypeClass dclass = dtype.getClass();
210 if (dclass == h5::DataTypeClass::Float) {
211 if (attr.getSpace().getElementCount() > 1) {
212 vector<double> values;
220 }
else if (dclass == h5::DataTypeClass::Integer) {
221 if (attr.getSpace().getElementCount() > 1) {
222 vector<long int> values;
230 }
else if (dclass == h5::DataTypeClass::String) {
231 if (attr.getSpace().getElementCount() > 1) {
232 vector<string> values;
240 }
else if (dclass == h5::DataTypeClass::Enum) {
242 if (attr.getSpace().getElementCount() > 1) {
243 vector<H5Boolean> values;
245 vector<bool> bValues;
246 for (
auto v : values) {
247 bValues.push_back(
bool(v));
253 out[name] = bool(value);
256 throw NotImplementedError(
"readH5Attributes",
257 "Unable to read attribute '{}' with type '{}'", name, dtype.string());
262 for (
auto& name : sub.listObjectNames()) {
263 if (sub.getObjectType(name) == h5::ObjectType::Group) {
264 out[name] = readH5Attributes(sub.getGroup(name), recursive);
272bool Storage::hasAttribute(
const string&
id,
const string& attr)
const
276 }
catch (
const CanteraError& err) {
277 throw CanteraError(
"Storage::hasAttribute",
278 "Caught exception for group '{}':\n",
id, err.getMessage());
280 h5::Group sub = m_file->getGroup(
id);
281 auto names = sub.listAttributeNames();
282 return std::find(names.begin(), names.end(), attr) != names.end();
285AnyMap Storage::readAttributes(
const string&
id,
bool recursive)
const
289 h5::Group sub = m_file->getGroup(
id);
290 return readH5Attributes(sub, recursive);
292 throw NotImplementedError(
"Storage::readAttribute",
294 }
catch (
const CanteraError& err) {
295 throw CanteraError(
"Storage::readAttribute",
296 "Caught exception for group '{}':\n",
id, err.getMessage());
300void writeH5Attributes(h5::Group sub,
const AnyMap& meta)
302 for (
auto& [name, item] : meta) {
303 if (sub.hasAttribute(name)) {
304 throw NotImplementedError(
"writeH5Attributes",
305 "Unable to overwrite existing Attribute '{}'", name);
307 if (item.is<
long int>()) {
308 int value = item.asInt();
309 h5::Attribute attr = sub.createAttribute<
long int>(
310 name, h5::DataSpace::From(value));
312 }
else if (item.is<
double>()) {
313 double value = item.asDouble();
314 h5::Attribute attr = sub.createAttribute<
double>(
315 name, h5::DataSpace::From(value));
317 }
else if (item.is<
string>()) {
318 string value = item.asString();
319 h5::Attribute attr = sub.createAttribute<
string>(
320 name, h5::DataSpace::From(value));
322 }
else if (item.is<
bool>()) {
323 bool bValue = item.asBool();
324 H5Boolean value = bValue ?
325 H5Boolean::HighFiveTrue : H5Boolean::HighFiveFalse;
326 h5::Attribute attr = sub.createAttribute<H5Boolean>(
327 name, h5::DataSpace::From(value));
329 }
else if (item.is<vector<long int>>()) {
330 auto values = item.as<vector<long int>>();
331 h5::Attribute attr = sub.createAttribute<
long int>(
332 name, h5::DataSpace::From(values));
334 }
else if (item.is<vector<double>>()) {
335 auto values = item.as<vector<double>>();
336 h5::Attribute attr = sub.createAttribute<
double>(
337 name, h5::DataSpace::From(values));
339 }
else if (item.is<vector<string>>()) {
340 auto values = item.as<vector<string>>();
341 h5::Attribute attr = sub.createAttribute<
string>(
342 name, h5::DataSpace::From(values));
344 }
else if (item.is<vector<bool>>()) {
345 auto bValue = item.as<vector<bool>>();
346 vector<H5Boolean> values;
347 for (
auto b : bValue) {
349 H5Boolean::HighFiveTrue : H5Boolean::HighFiveFalse);
351 h5::Attribute attr = sub.createAttribute<H5Boolean>(
352 name, h5::DataSpace::From(values));
354 }
else if (item.is<AnyMap>()) {
356 auto value = item.as<AnyMap>();
357 auto grp = sub.createGroup(name);
358 writeH5Attributes(grp, value);
360 throw NotImplementedError(
"writeH5Attributes",
361 "Unable to write attribute '{}' with type '{}'",
362 name, item.type_str());
367void Storage::writeAttributes(
const string&
id,
const AnyMap& meta)
370 checkGroupWrite(
id,
false);
371 h5::Group sub = m_file->getGroup(
id);
372 writeH5Attributes(sub, meta);
374 throw NotImplementedError(
"Storage::writeAttribute",
376 }
catch (
const CanteraError& err) {
378 throw CanteraError(
"Storage::writeAttributes",
"{}", err.getMessage());
379 }
catch (
const std::exception& err) {
381 throw CanteraError(
"Storage::writeAttributes",
382 "Encountered exception for group '{}':\n{}",
id, err.what());
386AnyValue Storage::readData(
const string&
id,
387 const string& name,
size_t rows,
size_t cols)
const
391 }
catch (
const CanteraError& err) {
392 throw CanteraError(
"Storage::readData",
393 "Caught exception for group '{}':\n",
id, err.getMessage());
395 h5::Group sub = m_file->getGroup(
id);
396 if (!sub.exist(name)) {
397 throw CanteraError(
"Storage::readData",
398 "DataSet '{}' not found in group '{}'.", name,
id);
400 h5::DataSet dataset = sub.getDataSet(name);
401 h5::DataSpace space = dataset.getSpace();
402 size_t ndim = space.getNumberDimensions();
403 if (cols == 0 && ndim != 1) {
404 throw CanteraError(
"Storage::readData",
405 "Shape of DataSet '{}' is inconsistent; expected one dimensions but "
406 "received {}.", name, ndim);
407 }
else if (cols != 0 && cols != npos && ndim != 2) {
408 throw CanteraError(
"Storage::readData",
409 "Shape of DataSet '{}' is inconsistent; expected two dimensions but "
410 "received {}.", name, ndim);
412 if (ndim == 0 || ndim > 2) {
413 throw NotImplementedError(
"Storage::readData",
414 "Cannot process DataSet '{}' as data has {} dimensions.", name, ndim);
416 const auto& shape = space.getDimensions();
417 if (shape[0] != rows) {
418 throw CanteraError(
"Storage::readData",
419 "Shape of DataSet '{}' is inconsistent; expected {} rows "
420 "but received {}.", name, rows, shape[0]);
422 if (cols != 0 && cols != npos && shape[1] != cols) {
423 throw CanteraError(
"Storage::readData",
424 "Shape of DataSet '{}' is inconsistent; expected {} columns "
425 "but received {}.", name, cols, shape[1]);
428 const auto datatype = dataset.getDataType().getClass();
429 if (datatype == h5::DataTypeClass::Float) {
436 vector<vector<double>> data;
440 }
catch (
const std::exception& err) {
441 throw NotImplementedError(
"Storage::readData",
442 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
443 name,
id, err.what());
445 }
else if (datatype == h5::DataTypeClass::Integer) {
448 vector<long int> data;
452 vector<vector<long int>> data;
456 }
catch (
const std::exception& err) {
457 throw NotImplementedError(
"Storage::readData",
458 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
459 name,
id, err.what());
461 }
else if (datatype == h5::DataTypeClass::String) {
468 vector<vector<string>> data;
472 }
catch (
const std::exception& err) {
473 throw NotImplementedError(
"Storage::readData",
474 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
475 name,
id, err.what());
478 throw NotImplementedError(
"Storage::readData",
479 "DataSet '{}' is not readable.", name);
484void Storage::writeData(
const string&
id,
const string& name,
const AnyValue& data)
487 checkGroupWrite(
id,
false);
488 }
catch (
const CanteraError& err) {
490 throw CanteraError(
"Storage::writeData",
"{}", err.getMessage());
491 }
catch (
const std::exception& err) {
493 throw CanteraError(
"Storage::writeData",
494 "Encountered exception for group '{}':\n{}",
id, err.what());
496 h5::Group sub = m_file->getGroup(
id);
497 if (sub.exist(name)) {
498 throw NotImplementedError(
"Storage::writeData",
499 "Unable to overwrite existing DataSet '{}' in group '{}'.", name,
id);
501 size_t size = data.vectorSize();
502 auto [rows, cols] = data.matrixShape();
503 if (size == npos && rows == npos) {
504 throw CanteraError(
"Storage::writeData",
505 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
506 "'{}'\nis neither a vector nor a matrix.", name,
id, data.type_str());
508 vector<size_t> dims{data.vectorSize()};
509 if (data.isVector<
long int>()) {
510 h5::DataSet dataset = sub.createDataSet<
long int>(name, h5::DataSpace(dims));
511 dataset.write(data.asVector<
long int>());
514 if (data.isVector<
double>()) {
515 h5::DataSet dataset = sub.createDataSet<
double>(name, h5::DataSpace(dims));
516 dataset.write(data.asVector<
double>());
519 if (data.isVector<
string>()) {
520 h5::DataSet dataset = sub.createDataSet<
string>(name, h5::DataSpace(dims));
521 dataset.write(data.asVector<
string>());
526 dims.push_back(rows);
527 dims.push_back(cols);
529 throw NotImplementedError(
"Storage::writeData",
530 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
531 "'{}'\nis not supported.", name,
id, data.type_str());
533 if (m_compressionLevel) {
536 h5::DataSpace space(dims, dims);
537 h5::DataSetCreateProps props;
538 props.add(h5::Chunking(vector<hsize_t>{dims[0], dims[1]}));
539 props.add(h5::Deflate(m_compressionLevel));
540 if (data.isVector<vector<long int>>()) {
541 h5::DataSet dataset = sub.createDataSet<
long int>(name, space, props);
542 dataset.write(data.asVector<vector<long int>>());
543 }
else if (data.isVector<vector<double>>()) {
544 h5::DataSet dataset = sub.createDataSet<
double>(name, space, props);
545 dataset.write(data.asVector<vector<double>>());
546 }
else if (data.isVector<vector<string>>()) {
547 h5::DataSet dataset = sub.createDataSet<
string>(name, space, props);
548 dataset.write(data.asVector<vector<string>>());
550 throw NotImplementedError(
"Storage::writeData",
551 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
552 "'{}'\nis not supported.", name,
id, data.type_str());
555 h5::DataSpace space(dims);
556 if (data.isVector<vector<long int>>()) {
557 h5::DataSet dataset = sub.createDataSet<
long int>(name, space);
558 dataset.write(data.asVector<vector<long int>>());
559 }
else if (data.isVector<vector<double>>()) {
560 h5::DataSet dataset = sub.createDataSet<
double>(name, space);
561 dataset.write(data.asVector<vector<double>>());
562 }
else if (data.isVector<vector<string>>()) {
563 h5::DataSet dataset = sub.createDataSet<
string>(name, space);
564 dataset.write(data.asVector<vector<string>>());
566 throw NotImplementedError(
"Storage::writeData",
567 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
568 "'{}'\nis not supported.", name,
id, data.type_str());
575Storage::Storage(
string fname,
bool write)
577 throw CanteraError(
"Storage::Storage",
578 "Saving to HDF requires HighFive installation.");
585void Storage::setCompressionLevel(
int level)
588 "Saving to HDF requires HighFive installation.");
591bool Storage::hasGroup(
const string&
id)
const
594 "Saving to HDF requires HighFive installation.");
597bool Storage::checkGroup(
const string&
id,
bool permissive)
600 "Saving to HDF requires HighFive installation.");
603void Storage::deleteGroup(
const string&
id)
606 "Saving to HDF requires HighFive installation.");
609pair<size_t, set<string>> Storage::contents(
const string&
id)
const
612 "Saving to HDF requires HighFive installation.");
615bool Storage::hasAttribute(
const string&
id,
const string& attr)
const
618 "Saving to HDF requires HighFive installation.");
621AnyMap Storage::readAttributes(
const string&
id,
bool recursive)
const
624 "Saving to HDF requires HighFive installation.");
627void Storage::writeAttributes(
const string&
id,
const AnyMap& meta)
630 "Saving to HDF requires HighFive installation.");
634 const string& name,
size_t rows,
size_t cols)
const
637 "Saving to HDF requires HighFive installation.");
640void Storage::writeData(
const string&
id,
641 const string& name,
const AnyValue& data)
644 "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.
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"