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