Skip to content

Commit

Permalink
[FileIO] Add 'overwrite' option to save routines
Browse files Browse the repository at this point in the history
  • Loading branch information
ischoegl committed Mar 21, 2023
1 parent 243fac5 commit fe807d5
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 40 deletions.
23 changes: 15 additions & 8 deletions include/cantera/base/SolutionArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,37 +182,44 @@ class SolutionArray
* @param fname Name of HDF container file
* @param id Identifier of root location within the container file
* @param desc Description
* @param overwrite Force overwrite if id exists; optional (default=false)
*/
static void writeHeader(const string& fname, const string& id, const string& desc);
static void writeHeader(const string& fname, const string& id, const string& desc,
bool overwrite=false);

/*!
* Write header data to AnyMap.
*
* @param root Root node of AnyMap structure
* @param id Identifier of root location within the container file
* @param desc Description
* @param overwrite Force overwrite if id exists; optional (default=false)
*/
static void writeHeader(AnyMap& root, const string& id, const string& desc);
static void writeHeader(AnyMap& root, const string& id, const string& desc,
bool overwrite=false);

/*!
* Write SolutionArray data to container file.
*
* @param fname Name of HDF container file
* @param id Identifier of root location within the container file
* @param sub Name identifier for the subgroup holding actual data
* @param overwrite Force overwrite if sub exists; optional (default=false)
* @param compression Compression level; optional (default=0; HDF only)
*/
void writeEntry(const string& fname, const string& id,
const string& sub, int compression=0);
void writeEntry(const string& fname, const string& id, const string& sub,
bool overwrite=false, int compression=0);

/*!
* Write SolutionArray data to AnyMap.
*
* @param root Root node of AnyMap structure
* @param id Identifier of root location within the container file
* @param sub Name identifier for the subgroup holding actual data
* @param overwrite Force overwrite if sub exists; optional (default=false)
*/
void writeEntry(AnyMap& root, const string& id, const string& sub);
void writeEntry(AnyMap& root, const string& id, const string& sub,
bool overwrite=false);

/*!
* Save current SolutionArray and header to a container file.
Expand All @@ -221,11 +228,11 @@ class SolutionArray
* @param id Identifier of root location within the container file
* @param sub Name identifier for the subgroup holding actual data
* @param desc Custom comment describing the dataset to be stored
* @param overwrite Force overwrite if sub exists; optional (default=false)
* @param compression Compression level; optional (default=0; HDF only)
*/
string save(const string& fname, const string& id,
const string& sub,
const string& desc, int compression=0);
string save(const string& fname, const string& id, const string& sub,
const string& desc, bool overwrite=false, int compression=0);

/*!
* Read header data from container file.
Expand Down
11 changes: 9 additions & 2 deletions include/cantera/base/Storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,18 @@ class Storage
bool hasGroup(const string& id) const;

//! Check whether path location exists.
//! If the file has write access, create location; otherwise exceptions are thrown.
//! If the location does not exist, an exception is thrown unless the *permissive*
//! flag is set; in this case, the method attempts to create a new location if the
//! file is accessed in write mode.
//! @param id storage location within file
//! @param permissive if true, do not raise exceptions
//! @param permissive if true, do not raise exceptions (default=false)
//! @returns boolean indicating whether id is pre-existing
bool checkGroup(const string& id, bool permissive=false);

//! Delete group
//! @param id storage location within file
void deleteGroup(const string& id);

//! Retrieve contents of file from a specified location
//! @param id storage location within file
//! @returns pair containing size and list of entry names of stored data set
Expand Down
9 changes: 7 additions & 2 deletions include/cantera/oneD/Sim1D.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,20 +134,25 @@ class Sim1D : public OneDim
* @param id Identifier of solution within the container file
* @param desc Description of the solution
* @param loglevel Level of diagnostic output
* @param overwrite Force overwrite if name exists; optional (default=`False`)
* @param compression Compression level (optional; HDF only)
*/
void save(const std::string& fname, const std::string& id,
const std::string& desc, int loglevel=1, int compression=0);
const std::string& desc, int loglevel=1,
bool overwrite=false, int compression=0);

/**
* Save the residual of the current solution to a container file.
* @param fname Name of output container file
* @param id Identifier of solution within the container file
* @param desc Description of the solution
* @param loglevel Level of diagnostic output
* @param overwrite Force overwrite if name exists; optional (default=`False`)
* @param compression Compression level (optional; HDF only)
*/
void saveResidual(const std::string& fname, const std::string& id,
const std::string& desc, int loglevel=1);
const std::string& desc, int loglevel=1,
bool overwrite=false, int compression=0);

/**
* Initialize the solution with a previously-saved solution.
Expand Down
57 changes: 39 additions & 18 deletions src/base/SolutionArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ SolutionArray::SolutionArray(const SolutionArray& other,
, m_active(selected)
{
for (auto loc : m_active) {
if (loc < 0 || loc >= m_dataSize) {
if (loc < 0 || loc >= (int)m_dataSize) {
IndexError("SolutionArray::SolutionArray", "indices", loc, m_dataSize);
}
}
Expand Down Expand Up @@ -123,7 +123,7 @@ void SolutionArray::reset()
size_t nState = m_sol->thermo()->stateSize();
vector<double> state(nState);
m_sol->thermo()->saveState(state); // thermo contains current state
for (int k = 0; k < m_size; ++k) {
for (size_t k = 0; k < m_size; ++k) {
std::copy(state.begin(), state.end(), m_data->data() + m_active[k] * m_stride);
}
for (auto& [key, extra] : *m_extra) {
Expand Down Expand Up @@ -415,7 +415,7 @@ void SolutionArray::setLoc(size_t loc, bool restore)
"Both current and buffered indices are invalid.");
}
return;
} else if (m_active[loc] == m_loc) {
} else if (m_active[loc] == (int)m_loc) {
return;
} else if (loc >= m_size) {
throw IndexError("SolutionArray::setLoc", "indices", loc, m_size - 1);
Expand Down Expand Up @@ -584,22 +584,33 @@ AnyMap& openField(AnyMap& root, const string& id)
}

void SolutionArray::writeHeader(const string& fname, const string& id,
const string& desc)
const string& desc, bool overwrite)
{
Storage file(fname, true);
file.checkGroup(id, true);
if (file.checkGroup(id, true)) {
if (!overwrite) {
throw CanteraError("SolutionArray::writeHeader",
"Group id '{}' exists; use 'overwrite' argument to overwrite.", id);
}
file.deleteGroup(id);
file.checkGroup(id, true);
}
file.writeAttributes(id, preamble(desc));
}

void SolutionArray::writeHeader(AnyMap& root, const string& id,
const string& desc)
const string& desc, bool overwrite)
{
AnyMap& data = openField(root, id);
if (!data.empty() && !overwrite) {
throw CanteraError("SolutionArray::writeHeader",
"Field id '{}' exists; use 'overwrite' argument to overwrite.", id);
}
data.update(preamble(desc));
}

void SolutionArray::writeEntry(const string& fname, const string& id,
const string& sub, int compression)
void SolutionArray::writeEntry(const string& fname, const string& id, const string& sub,
bool overwrite, int compression)
{
if (id == "") {
throw CanteraError("SolutionArray::writeEntry",
Expand All @@ -619,7 +630,14 @@ void SolutionArray::writeEntry(const string& fname, const string& id,
} else {
path += "/data";
}
file.checkGroup(path, true);
if (file.checkGroup(path, true)) {
if (!overwrite) {
throw CanteraError("SolutionArray::writeEntry",
"Group id '{}' exists; use 'overwrite' argument to overwrite.", id);
}
file.deleteGroup(path);
file.checkGroup(path, true);
}
file.writeAttributes(path, m_meta);
AnyMap more;
if (apiNdim() == 1) {
Expand Down Expand Up @@ -665,8 +683,8 @@ void SolutionArray::writeEntry(const string& fname, const string& id,
}
}

void SolutionArray::writeEntry(AnyMap& root,
const string& id, const string& sub)
void SolutionArray::writeEntry(AnyMap& root, const string& id, const string& sub,
bool overwrite)
{
if (id == "") {
throw CanteraError("SolutionArray::writeEntry",
Expand All @@ -684,6 +702,10 @@ void SolutionArray::writeEntry(AnyMap& root,
}
AnyMap& data = openField(root, path);
bool preexisting = !data.empty();
if (preexisting && !overwrite) {
throw CanteraError("SolutionArray::writeEntry",
"Field id '{}' exists; use 'overwrite' argument to overwrite.", id);
}
if (apiNdim() == 1) {
data["size"] = int(m_dataSize);
} else {
Expand Down Expand Up @@ -759,9 +781,8 @@ void SolutionArray::append(const vector<double>& state, const AnyMap& extra)
}
}

string SolutionArray::save(const string& fname, const string& id,
const string& sub, const string& desc,
int compression)
string SolutionArray::save(const string& fname, const string& id, const string& sub,
const string& desc, bool overwrite, int compression)
{
if (m_size < m_dataSize) {
throw NotImplementedError("SolutionArray::save",
Expand All @@ -770,8 +791,8 @@ string SolutionArray::save(const string& fname, const string& id,
size_t dot = fname.find_last_of(".");
string extension = (dot != npos) ? toLowerCopy(fname.substr(dot + 1)) : "";
if (extension == "h5" || extension == "hdf" || extension == "hdf5") {
writeEntry(fname, id, sub, compression);
writeHeader(fname, id, desc);
writeHeader(fname, id, desc, overwrite);
writeEntry(fname, id, sub, true, compression);
return id;
}
if (extension == "yaml" || extension == "yml") {
Expand All @@ -780,8 +801,8 @@ string SolutionArray::save(const string& fname, const string& id,
if (std::ifstream(fname).good()) {
data = AnyMap::fromYamlFile(fname);
}
writeEntry(data, id, sub);
writeHeader(data, id, desc);
writeHeader(data, id, desc, overwrite);
writeEntry(data, id, sub, true);

// Write the output file and remove the now-outdated cached file
std::ofstream out(fname);
Expand Down
19 changes: 18 additions & 1 deletion src/base/Storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ bool Storage::checkGroupWrite(const string& id, bool permissive)
"Specified group with id '{}' does not exist.", id);
}
m_file->createGroup(id);
return true;
return false;
}
if (m_file->getObjectType(id) != h5::ObjectType::Group) {
throw CanteraError("Storage::checkGroupWrite",
Expand Down Expand Up @@ -153,6 +153,17 @@ bool Storage::checkGroup(const string& id, bool permissive)
}
}

void Storage::deleteGroup(const string& id)
{
try {
m_file->unlink(id);
} catch (const std::exception& err) {
// convert HighFive exception
throw CanteraError("Storage::deleteGroup",
"Encountered exception while deleting group '{}':\n{}", id, err.what());
}
}

std::pair<size_t, set<string>> Storage::contents(const string& id) const
{
try {
Expand Down Expand Up @@ -581,6 +592,12 @@ bool Storage::checkGroup(const string& id, bool permissive)
"Saving to HDF requires HighFive installation.");
}

void Storage::deleteGroup(const string& id)
{
throw CanteraError("Storage::deleteGroup",
"Saving to HDF requires HighFive installation.");
}

std::pair<size_t, set<string>> Storage::contents(const string& id) const
{
throw CanteraError("Storage::contents",
Expand Down
22 changes: 13 additions & 9 deletions src/oneD/Sim1D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,16 @@ void Sim1D::setProfile(size_t dom, size_t comp,
}

void Sim1D::save(const std::string& fname, const std::string& id,
const std::string& desc, int loglevel, int compression)
const std::string& desc, int loglevel, bool overwrite, int compression)
{
size_t dot = fname.find_last_of(".");
string extension = (dot != npos) ? toLowerCopy(fname.substr(dot+1)) : "";
if (extension == "h5" || extension == "hdf" || extension == "hdf5") {
SolutionArray::writeHeader(fname, id, desc, overwrite);
for (auto dom : m_dom) {
auto arr = dom->asArray(m_x.data() + dom->loc());
arr->writeEntry(fname, id, dom->id(), compression);
arr->writeEntry(fname, id, dom->id(), overwrite, compression);
}
SolutionArray::writeHeader(fname, id, desc);
if (loglevel > 0) {
writelog("Solution saved to file '{}' as group '{}'.\n", fname, id);
}
Expand All @@ -136,11 +136,11 @@ void Sim1D::save(const std::string& fname, const std::string& id,
if (std::ifstream(fname).good()) {
data = AnyMap::fromYamlFile(fname);
}
SolutionArray::writeHeader(data, id, desc);
SolutionArray::writeHeader(data, id, desc, overwrite);

for (auto dom : m_dom) {
auto arr = dom->asArray(m_x.data() + dom->loc());
arr->writeEntry(data, id, dom->id());
arr->writeEntry(data, id, dom->id(), overwrite);
}

// Write the output file and remove the now-outdated cached file
Expand All @@ -152,22 +152,24 @@ void Sim1D::save(const std::string& fname, const std::string& id,
}
return;
}
throw CanteraError("Sim1D::save",
"Unsupported file format '{}'", extension);
throw CanteraError("Sim1D::save", "Unsupported file format '{}'.", extension);
}

void Sim1D::saveResidual(const std::string& fname, const std::string& id,
const std::string& desc, int loglevel)
const std::string& desc,
int loglevel, bool overwrite, int compression)
{
vector_fp res(m_x.size(), -999);
OneDim::eval(npos, &m_x[0], &res[0], 0.0);
// Temporarily put the residual into m_x, since this is the vector that the save()
// function reads.
std::swap(res, m_x);
save(fname, id, desc, loglevel);
save(fname, id, desc, loglevel, overwrite, compression);
std::swap(res, m_x);
}

namespace { // restrict scope of helper function to local translation unit

//! convert data format used by Python h5py export (Cantera < 3.0)
AnyMap legacyH5(shared_ptr<SolutionArray> arr, const AnyMap& header={})
{
Expand Down Expand Up @@ -251,6 +253,8 @@ AnyMap legacyH5(shared_ptr<SolutionArray> arr, const AnyMap& header={})
return out;
}

} // end unnamed namespace

AnyMap Sim1D::restore(const std::string& fname, const std::string& id,
int loglevel)
{
Expand Down

0 comments on commit fe807d5

Please sign in to comment.