15#if CT_USE_SYSTEM_HIGHFIVE
16 #include <highfive/H5Attribute.hpp>
17 #include <highfive/H5DataSet.hpp>
18 #include <highfive/H5DataSpace.hpp>
19 #include <highfive/H5DataType.hpp>
20 #include <highfive/H5File.hpp>
21 #include <highfive/H5Group.hpp>
23 #include "cantera/ext/HighFive/H5Attribute.hpp"
24 #include "cantera/ext/HighFive/H5DataSet.hpp"
25 #include "cantera/ext/HighFive/H5DataSpace.hpp"
26 #include "cantera/ext/HighFive/H5DataType.hpp"
27 #include "cantera/ext/HighFive/H5File.hpp"
28 #include "cantera/ext/HighFive/H5Group.hpp"
31namespace h5 = HighFive;
33#ifdef CT_USE_HIGHFIVE_BOOLEAN
35typedef h5::details::Boolean H5Boolean;
44h5::EnumType<H5Boolean> create_enum_boolean() {
45 return {{
"FALSE", H5Boolean::HighFiveFalse}, {
"TRUE", H5Boolean::HighFiveTrue}};
48HIGHFIVE_REGISTER_TYPE(H5Boolean, ::create_enum_boolean)
58Storage::Storage(
string fname,
bool write) : m_write(write)
61 m_file = make_unique<h5::File>(fname, h5::File::OpenOrCreate);
64 m_file = make_unique<h5::File>(fullName, h5::File::ReadOnly);
73void Storage::setCompressionLevel(
int level)
75 if (level < 0 || level > 9) {
76 throw CanteraError(
"Storage::setCompressionLevel",
77 "Invalid compression level '{}' (needs to be 0..9).", level);
79 m_compressionLevel = level;
82bool Storage::hasGroup(
const string&
id)
const
84 if (!m_file->exist(
id)) {
87 if (m_file->getObjectType(
id) != h5::ObjectType::Group) {
93bool Storage::checkGroupRead(
const string&
id)
const
95 vector<string> tokens;
97 string grp = tokens[0];
99 throw CanteraError(
"Storage::checkGroupRead",
100 "No group with id '{}' found at root.", grp);
104 h5::Group sub = m_file->getGroup(grp);
105 tokens.erase(tokens.begin());
106 for (
auto& grp : tokens) {
107 if (!hasGroup(path +
"/" + grp)) {
108 throw CanteraError(
"Storage::checkGroupRead",
109 "No group with id '{}' found at '{}'.", grp, path);
112 sub = sub.getGroup(grp);
117bool Storage::checkGroupWrite(
const string&
id,
bool permissive)
120 throw CanteraError(
"Storage::checkGroupWrite",
121 "Cannot write to file opened in read mode.");
124 throw CanteraError(
"Storage::checkGroupWrite",
125 "Cannot write to empty group id '' (root location).");
127 if (!m_file->exist(
id)) {
129 throw CanteraError(
"Storage::checkGroupWrite",
130 "Specified group with id '{}' does not exist.",
id);
132 m_file->createGroup(
id);
135 if (m_file->getObjectType(
id) != h5::ObjectType::Group) {
136 throw CanteraError(
"Storage::checkGroupWrite",
137 "Unable to write to existing object with id '{}'.",
id);
142bool Storage::checkGroup(
const string&
id,
bool permissive)
146 return checkGroupWrite(
id, permissive);
148 return checkGroupRead(
id);
149 }
catch (
const CanteraError& err) {
153 throw CanteraError(
"Storage::checkGroup", err.getMessage());
154 }
catch (
const std::exception& err) {
159 throw CanteraError(
"Storage::checkGroup",
160 "Encountered exception for group '{}':\n{}",
id, err.what());
164void Storage::deleteGroup(
const string&
id)
168 }
catch (
const std::exception& err) {
170 throw CanteraError(
"Storage::deleteGroup",
171 "Encountered exception while deleting group '{}':\n{}",
id, err.what());
175pair<size_t, set<string>> Storage::contents(
const string&
id)
const
179 }
catch (
const CanteraError& err) {
180 throw CanteraError(
"Storage::contents",
181 "Caught exception for group '{}':\n",
id, err.getMessage());
183 h5::Group sub = m_file->getGroup(
id);
186 size_t nElements = 0;
187 for (
auto& name : sub.listObjectNames()) {
188 if (sub.getObjectType(name) == h5::ObjectType::Dataset) {
189 h5::DataSpace space = sub.getDataSet(name).getSpace();
191 if (space.getNumberDimensions() < nDims) {
192 nDims = space.getNumberDimensions();
193 nElements = space.getElementCount();
197 if (nDims != 1 && nDims != npos) {
198 throw NotImplementedError(
"Storage::content",
199 "Encountered invalid data with {} dimensions.", nDims);
201 return std::make_pair(nElements, names);
204AnyMap readH5Attributes(
const h5::Group& sub,
bool recursive)
208 for (
auto& name : sub.listAttributeNames()) {
209 h5::Attribute attr = sub.getAttribute(name);
210 h5::DataType dtype = attr.getDataType();
211 h5::DataTypeClass dclass = dtype.getClass();
212 if (dclass == h5::DataTypeClass::Float) {
213 if (attr.getSpace().getElementCount() > 1) {
214 vector<double> values;
222 }
else if (dclass == h5::DataTypeClass::Integer) {
223 if (attr.getSpace().getElementCount() > 1) {
224 vector<long int> values;
232 }
else if (dclass == h5::DataTypeClass::String) {
233 if (attr.getSpace().getElementCount() > 1) {
234 vector<string> values;
242 }
else if (dclass == h5::DataTypeClass::Enum) {
244 if (attr.getSpace().getElementCount() > 1) {
245 vector<H5Boolean> values;
247 vector<bool> bValues;
248 for (
auto v : values) {
249 bValues.push_back(
bool(v));
255 out[name] = bool(value);
258 throw NotImplementedError(
"readH5Attributes",
259 "Unable to read attribute '{}' with type '{}'", name, dtype.string());
264 for (
auto& name : sub.listObjectNames()) {
265 if (sub.getObjectType(name) == h5::ObjectType::Group) {
266 out[name] = readH5Attributes(sub.getGroup(name), recursive);
274bool Storage::hasAttribute(
const string&
id,
const string& attr)
const
278 }
catch (
const CanteraError& err) {
279 throw CanteraError(
"Storage::hasAttribute",
280 "Caught exception for group '{}':\n",
id, err.getMessage());
282 h5::Group sub = m_file->getGroup(
id);
283 auto names = sub.listAttributeNames();
284 return std::find(names.begin(), names.end(), attr) != names.end();
287AnyMap Storage::readAttributes(
const string&
id,
bool recursive)
const
291 h5::Group sub = m_file->getGroup(
id);
292 return readH5Attributes(sub, recursive);
294 throw NotImplementedError(
"Storage::readAttribute",
296 }
catch (
const CanteraError& err) {
297 throw CanteraError(
"Storage::readAttribute",
298 "Caught exception for group '{}':\n",
id, err.getMessage());
302void writeH5Attributes(h5::Group sub,
const AnyMap& meta)
304 for (
auto& [name, item] : meta) {
305 if (sub.hasAttribute(name)) {
306 throw NotImplementedError(
"writeH5Attributes",
307 "Unable to overwrite existing Attribute '{}'", name);
309 if (item.is<
long int>()) {
310 int value = item.asInt();
311 h5::Attribute attr = sub.createAttribute<
long int>(
312 name, h5::DataSpace::From(value));
314 }
else if (item.is<
double>()) {
315 double value = item.asDouble();
316 h5::Attribute attr = sub.createAttribute<
double>(
317 name, h5::DataSpace::From(value));
319 }
else if (item.is<
string>()) {
320 string value = item.asString();
321 h5::Attribute attr = sub.createAttribute<
string>(
322 name, h5::DataSpace::From(value));
324 }
else if (item.is<
bool>()) {
325 bool bValue = item.asBool();
326 H5Boolean value = bValue ?
327 H5Boolean::HighFiveTrue : H5Boolean::HighFiveFalse;
328 h5::Attribute attr = sub.createAttribute<H5Boolean>(
329 name, h5::DataSpace::From(value));
331 }
else if (item.is<vector<long int>>()) {
332 auto values = item.as<vector<long int>>();
333 h5::Attribute attr = sub.createAttribute<
long int>(
334 name, h5::DataSpace::From(values));
336 }
else if (item.is<vector<double>>()) {
337 auto values = item.as<vector<double>>();
338 h5::Attribute attr = sub.createAttribute<
double>(
339 name, h5::DataSpace::From(values));
341 }
else if (item.is<vector<string>>()) {
342 auto values = item.as<vector<string>>();
343 h5::Attribute attr = sub.createAttribute<
string>(
344 name, h5::DataSpace::From(values));
346 }
else if (item.is<vector<bool>>()) {
347 auto bValue = item.as<vector<bool>>();
348 vector<H5Boolean> values;
349 for (
auto b : bValue) {
351 H5Boolean::HighFiveTrue : H5Boolean::HighFiveFalse);
353 h5::Attribute attr = sub.createAttribute<H5Boolean>(
354 name, h5::DataSpace::From(values));
356 }
else if (item.is<AnyMap>()) {
358 auto value = item.as<AnyMap>();
359 auto grp = sub.createGroup(name);
360 writeH5Attributes(grp, value);
362 throw NotImplementedError(
"writeH5Attributes",
363 "Unable to write attribute '{}' with type '{}'",
364 name, item.type_str());
369void Storage::writeAttributes(
const string&
id,
const AnyMap& meta)
372 checkGroupWrite(
id,
false);
373 h5::Group sub = m_file->getGroup(
id);
374 writeH5Attributes(sub, meta);
376 throw NotImplementedError(
"Storage::writeAttribute",
378 }
catch (
const CanteraError& err) {
380 throw CanteraError(
"Storage::writeAttributes",
"{}", err.getMessage());
381 }
catch (
const std::exception& err) {
383 throw CanteraError(
"Storage::writeAttributes",
384 "Encountered exception for group '{}':\n{}",
id, err.what());
388AnyValue Storage::readData(
const string&
id,
389 const string& name,
size_t rows,
size_t cols)
const
393 }
catch (
const CanteraError& err) {
394 throw CanteraError(
"Storage::readData",
395 "Caught exception for group '{}':\n",
id, err.getMessage());
397 h5::Group sub = m_file->getGroup(
id);
398 if (!sub.exist(name)) {
399 throw CanteraError(
"Storage::readData",
400 "DataSet '{}' not found in group '{}'.", name,
id);
402 h5::DataSet dataset = sub.getDataSet(name);
403 h5::DataSpace space = dataset.getSpace();
404 size_t ndim = space.getNumberDimensions();
405 if (cols == 0 && ndim != 1) {
406 throw CanteraError(
"Storage::readData",
407 "Shape of DataSet '{}' is inconsistent; expected one dimensions but "
408 "received {}.", name, ndim);
409 }
else if (cols != 0 && cols != npos && ndim != 2) {
410 throw CanteraError(
"Storage::readData",
411 "Shape of DataSet '{}' is inconsistent; expected two dimensions but "
412 "received {}.", name, ndim);
414 if (ndim == 0 || ndim > 2) {
415 throw NotImplementedError(
"Storage::readData",
416 "Cannot process DataSet '{}' as data has {} dimensions.", name, ndim);
418 const auto& shape = space.getDimensions();
419 if (shape[0] != rows) {
420 throw CanteraError(
"Storage::readData",
421 "Shape of DataSet '{}' is inconsistent; expected {} rows "
422 "but received {}.", name, rows, shape[0]);
424 if (cols != 0 && cols != npos && shape[1] != cols) {
425 throw CanteraError(
"Storage::readData",
426 "Shape of DataSet '{}' is inconsistent; expected {} columns "
427 "but received {}.", name, cols, shape[1]);
430 const auto datatype = dataset.getDataType().getClass();
431 if (datatype == h5::DataTypeClass::Float) {
438 vector<vector<double>> data;
442 }
catch (
const std::exception& err) {
443 throw NotImplementedError(
"Storage::readData",
444 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
445 name,
id, err.what());
447 }
else if (datatype == h5::DataTypeClass::Integer) {
450 vector<long int> data;
454 vector<vector<long int>> data;
458 }
catch (
const std::exception& err) {
459 throw NotImplementedError(
"Storage::readData",
460 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
461 name,
id, err.what());
463 }
else if (datatype == h5::DataTypeClass::String) {
470 vector<vector<string>> data;
474 }
catch (
const std::exception& err) {
475 throw NotImplementedError(
"Storage::readData",
476 "Encountered HighFive exception for DataSet '{}' in group '{}':\n{}",
477 name,
id, err.what());
480 throw NotImplementedError(
"Storage::readData",
481 "DataSet '{}' is not readable.", name);
486void Storage::writeData(
const string&
id,
const string& name,
const AnyValue& data)
489 checkGroupWrite(
id,
false);
490 }
catch (
const CanteraError& err) {
492 throw CanteraError(
"Storage::writeData",
"{}", err.getMessage());
493 }
catch (
const std::exception& err) {
495 throw CanteraError(
"Storage::writeData",
496 "Encountered exception for group '{}':\n{}",
id, err.what());
498 h5::Group sub = m_file->getGroup(
id);
499 if (sub.exist(name)) {
500 throw NotImplementedError(
"Storage::writeData",
501 "Unable to overwrite existing DataSet '{}' in group '{}'.", name,
id);
503 size_t size = data.vectorSize();
504 auto [rows, cols] = data.matrixShape();
505 if (size == npos && rows == npos) {
506 throw CanteraError(
"Storage::writeData",
507 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
508 "'{}'\nis neither a vector nor a matrix.", name,
id, data.type_str());
510 vector<size_t> dims{data.vectorSize()};
511 if (data.isVector<
long int>()) {
512 h5::DataSet dataset = sub.createDataSet<
long int>(name, h5::DataSpace(dims));
513 dataset.write(data.asVector<
long int>());
516 if (data.isVector<
double>()) {
517 h5::DataSet dataset = sub.createDataSet<
double>(name, h5::DataSpace(dims));
518 dataset.write(data.asVector<
double>());
521 if (data.isVector<
string>()) {
522 h5::DataSet dataset = sub.createDataSet<
string>(name, h5::DataSpace(dims));
523 dataset.write(data.asVector<
string>());
528 dims.push_back(rows);
529 dims.push_back(cols);
531 throw NotImplementedError(
"Storage::writeData",
532 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
533 "'{}'\nis not supported.", name,
id, data.type_str());
535 if (m_compressionLevel) {
538 h5::DataSpace space(dims, dims);
539 h5::DataSetCreateProps props;
540 props.add(h5::Chunking(vector<hsize_t>{dims[0], dims[1]}));
541 props.add(h5::Deflate(m_compressionLevel));
542 if (data.isVector<vector<long int>>()) {
543 h5::DataSet dataset = sub.createDataSet<
long int>(name, space, props);
544 dataset.write(data.asVector<vector<long int>>());
545 }
else if (data.isVector<vector<double>>()) {
546 h5::DataSet dataset = sub.createDataSet<
double>(name, space, props);
547 dataset.write(data.asVector<vector<double>>());
548 }
else if (data.isVector<vector<string>>()) {
549 h5::DataSet dataset = sub.createDataSet<
string>(name, space, props);
550 dataset.write(data.asVector<vector<string>>());
552 throw NotImplementedError(
"Storage::writeData",
553 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
554 "'{}'\nis not supported.", name,
id, data.type_str());
557 h5::DataSpace space(dims);
558 if (data.isVector<vector<long int>>()) {
559 h5::DataSet dataset = sub.createDataSet<
long int>(name, space);
560 dataset.write(data.asVector<vector<long int>>());
561 }
else if (data.isVector<vector<double>>()) {
562 h5::DataSet dataset = sub.createDataSet<
double>(name, space);
563 dataset.write(data.asVector<vector<double>>());
564 }
else if (data.isVector<vector<string>>()) {
565 h5::DataSet dataset = sub.createDataSet<
string>(name, space);
566 dataset.write(data.asVector<vector<string>>());
568 throw NotImplementedError(
"Storage::writeData",
569 "Cannot write DataSet '{}' in group '{}' as input data with type\n"
570 "'{}'\nis not supported.", name,
id, data.type_str());
577Storage::Storage(
string fname,
bool write)
579 throw CanteraError(
"Storage::Storage",
580 "Saving to HDF requires HighFive installation.");
587void Storage::setCompressionLevel(
int level)
590 "Saving to HDF requires HighFive installation.");
593bool Storage::hasGroup(
const string&
id)
const
596 "Saving to HDF requires HighFive installation.");
599bool Storage::checkGroup(
const string&
id,
bool permissive)
602 "Saving to HDF requires HighFive installation.");
605void Storage::deleteGroup(
const string&
id)
608 "Saving to HDF requires HighFive installation.");
611pair<size_t, set<string>> Storage::contents(
const string&
id)
const
614 "Saving to HDF requires HighFive installation.");
617bool Storage::hasAttribute(
const string&
id,
const string& attr)
const
620 "Saving to HDF requires HighFive installation.");
623AnyMap Storage::readAttributes(
const string&
id,
bool recursive)
const
626 "Saving to HDF requires HighFive installation.");
629void Storage::writeAttributes(
const string&
id,
const AnyMap& meta)
632 "Saving to HDF requires HighFive installation.");
636 const string& name,
size_t rows,
size_t cols)
const
639 "Saving to HDF requires HighFive installation.");
642void Storage::writeData(
const string&
id,
643 const string& name,
const AnyValue& data)
646 "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"