Program Listing for File OutputManagerH5.hpp
↰ Return to documentation for file (src/io/OutputManagerH5.hpp)
#ifndef OUTPUTMANAGERH5_HPP
#define OUTPUTMANAGERH5_HPP
#include "../utils/ThreadVec.hpp"
#include "OutputManager.hpp"
#include "SimControls.hpp"
#include "hdf5.h" // NOLINT(misc-include-cleaner)
#include <filesystem>
#include <optional>
#include <string>
namespace core
{
class Cluster;
class Galaxy;
} // namespace core
namespace io
{
class OutputManagerH5 : public OutputManager
{
public:
explicit OutputManagerH5(const SimControls& simControls, bool restart = false);
~OutputManagerH5() override;
OutputManagerH5(const OutputManagerH5&) = delete;
auto operator=(const OutputManagerH5&) -> OutputManagerH5& = delete;
OutputManagerH5(OutputManagerH5&&) = delete;
auto operator=(OutputManagerH5&&) -> OutputManagerH5& = delete;
void writeCluster(unsigned long trial, const core::Cluster& cluster) override;
void writeClusterSpec(unsigned long trial, double time,
core::Cluster& cluster) override;
void writeClusterPhot(unsigned long trial, double time,
core::Cluster& cluster) override;
void writeClusterYields(unsigned long trial, double time,
core::Cluster& cluster) override;
void writeGalaxy(unsigned long trial, double time,
core::Galaxy& galaxy) override;
void writeGalaxySpec(unsigned long trial, double time,
core::Galaxy& galaxy) override;
void writeGalaxyPhot(unsigned long trial, double time,
core::Galaxy& galaxy) override;
void writeGalaxyYields(unsigned long trial, double time,
core::Galaxy& galaxy) override;
void writeClusterFeedback(unsigned long trial, double time,
core::Cluster& cluster) override;
void writeGalaxyFeedback(unsigned long trial, double time,
core::Galaxy& galaxy) override;
void checkpoint(unsigned long trialsCompleted) override;
[[nodiscard]] auto restartTrialsDone() const -> unsigned long override
{ return restartTrialsDone_; }
[[nodiscard]] auto restartMaxTrial() const -> unsigned long override
{ return restartMaxTrial_; }
void notifyEarlyTermination(unsigned long trialsCompleted) override
{ earlyTerminationTrialsCompleted_ = trialsCompleted; }
private:
void restartSetup();
[[nodiscard]] auto findRestartUidForRank(unsigned long maxCheckpoint) const
-> std::optional<unsigned long>;
void openNewOutputFiles();
[[nodiscard]] auto checkpointModelName(unsigned long checkpointNum) const -> std::string;
void openOutputFile(const std::filesystem::path& path);
void closeOutputFile(unsigned long trialsCompleted);
void syncCheckpoints(unsigned long trialsCompleted);
static void consolidateFiles(const std::filesystem::path& path);
void openClustersGroup();
void openClusterSpectraGroup();
void openClusterPhotGroup();
void openClusterYieldsGroup();
void openGalaxyGroup();
void openGalaxySpectraGroup();
void openGalaxyPhotGroup();
void openGalaxyYieldsGroup();
void openClusterFeedbackGroup();
void openGalaxyFeedbackGroup();
// Number of times checkpoint() has rolled over to a new
// checkpoint; 0 until the first call. Only meaningful if
// SimControls::checkpointInterval() is non-zero -- see
// checkpointModelName()/openNewOutputFiles(). Not a ThreadVec
// like the handles below, despite (like them) being read/
// written from inside a "#pragma omp parallel" region: unlike
// those, this is one single, shared value describing the
// whole checkpoint set every thread's own file currently
// belongs to, not a separate value per thread -- and it is
// only ever mutated between parallel regions (see
// checkpoint()'s own comment for why), never concurrently, so
// it needs no locking of its own either.
unsigned long checkpointNumber_ = 0;
// The largest trial number ever actually passed to any write*
// method over this object's own whole lifetime (0 before the
// first one, or if this is a restart with nothing written yet
// this session -- see below) -- unlike checkpointNumber_/
// restartTrialsDone_ above, this genuinely is touched
// concurrently, from inside every write* method's own
// "#pragma omp critical(h5ThreadSafety)" block (see e.g.
// writeCluster()'s own comment), which is exactly why it is
// safe without any locking of its own: only one thread is ever
// inside that critical section at a time, regardless of which
// write* method it entered through.
//
// Deliberately never reset between checkpoints (unlike
// checkpointNumber_, which advances by one every rollover):
// if this object's constructor set restartMaxTrial_ (restart
// = true and a previous checkpoint was found), it also
// initializes this to that same value, so it already reflects
// every trial number the run being restarted ever wrote,
// before this session writes any of its own -- otherwise, if a
// restarted session were itself interrupted again having
// written nothing new yet, its own checkpoint's "max_trial"
// attribute (see closeOutputFile()'s own comment) would
// wrongly read back as 0 on a later restart, colliding new
// numbering with everything already written in earlier
// sessions. See restartSetup()'s own comment for how the
// value written here gets read back in.
unsigned long maxTrial_ = 0;
// Number of trials the run being restarted had already
// completed, as of the checkpoint restartSetup() found and
// resumed from -- 0 if this OutputManagerH5 was not
// constructed with restart = true, or if restartSetup() found
// no checkpoint to resume from. Set once, by restartSetup(),
// before openNewOutputFiles() is ever called, and never
// written again afterward, so (like checkpointNumber_ above)
// it needs no locking of its own despite being read from
// inside a "#pragma omp parallel" region by restartTrialsDone()'s
// own callers.
unsigned long restartTrialsDone_ = 0;
// The largest trial number restartSetup() found the run being
// restarted had ever actually written, as of the checkpoint it
// resumed from -- 0 under the same conditions restartTrialsDone_
// is 0 (see its own comment). Same locking rationale as
// restartTrialsDone_ (set once, before openNewOutputFiles()).
unsigned long restartMaxTrial_ = 0;
// Set by notifyEarlyTermination(), from outside any active
// parallel region, at most once, strictly before this object
// is ever destroyed (see its own comment) -- read only by the
// destructor itself, single-threaded, so (like
// checkpointNumber_/restartTrialsDone_ above) needs no locking
// of its own. std::nullopt (the default) means this run ended
// normally, having completed every trial -- the destructor's
// own long-standing assumption, unchanged from before this
// existed.
std::optional<unsigned long> earlyTerminationTrialsCompleted_;
// Each thread's own copy of every HDF5 handle below is
// private to it -- see this class's own header comment on why
// -- reset to -1 (the "not open" sentinel) by openOutputFile()
// itself, for the calling thread's own slot alone, just before
// that thread's own file is opened; see openNewOutputFiles()'s
// own comment for why this happens there rather than as a
// separate, whole-ThreadVec reset up front the way earlier,
// pre-checkpointing code did.
utils::ThreadVec<hid_t> file_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> clustersGroup_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> clusterSpectraGroup_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> clusterPhotGroup_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> clusterYieldsGroup_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> galaxyGroup_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> galaxySpectraGroup_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> galaxyPhotGroup_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> galaxyYieldsGroup_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> clusterFeedbackGroup_; // NOLINT(misc-include-cleaner)
utils::ThreadVec<hid_t> galaxyFeedbackGroup_; // NOLINT(misc-include-cleaner)
};
} // namespace io
#endif // OUTPUTMANAGERH5_HPP