Skip to content
Snippets Groups Projects
particles_output_hdf5.hpp 14.99 KiB
/******************************************************************************
*                                                                             *
*  Copyright 2019 Max Planck Institute for Dynamics and Self-Organization     *
*                                                                             *
*  This file is part of TurTLE.                                               *
*                                                                             *
*  TurTLE is free software: you can redistribute it and/or modify             *
*  it under the terms of the GNU General Public License as published          *
*  by the Free Software Foundation, either version 3 of the License,          *
*  or (at your option) any later version.                                     *
*                                                                             *
*  TurTLE is distributed in the hope that it will be useful,                  *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of             *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
*  GNU General Public License for more details.                               *
*                                                                             *
*  You should have received a copy of the GNU General Public License          *
*  along with TurTLE.  If not, see <http://www.gnu.org/licenses/>             *
*                                                                             *
* Contact: Cristian.Lalescu@ds.mpg.de                                         *
*                                                                             *
******************************************************************************/



#ifndef PARTICLES_OUTPUT_HDF5_HPP
#define PARTICLES_OUTPUT_HDF5_HPP

#include <memory>
#include <vector>
#include <hdf5.h>
#include <sys/stat.h>

#include "abstract_particles_output.hpp"
#include "scope_timer.hpp"

template <class partsize_t,
          class real_number,
          int size_particle_positions>
class particles_output_hdf5 : public abstract_particles_output<partsize_t,
                                                               real_number,
                                                               size_particle_positions>{
    using Parent = abstract_particles_output<partsize_t,
                                             real_number,
                                             size_particle_positions>;

    std::string particle_species_name;

    hid_t file_id;
    const partsize_t total_nb_particles;
    std::vector<hsize_t> particle_file_layout;   // to hold the shape of initial condition array

    hid_t dset_id_state;
    hid_t dset_id_rhs;

    bool use_collective_io;

public:
    particles_output_hdf5(MPI_Comm in_mpi_com,
                          const std::string ps_name,
                          const partsize_t inTotalNbParticles,
                          const int in_nb_rhs,
                          const bool in_use_collective_io = false)
            : abstract_particles_output<partsize_t,
                                        real_number,
                                        size_particle_positions>(
                                                in_mpi_com,
                                                inTotalNbParticles,
                                                in_nb_rhs),
              particle_species_name(ps_name),
              file_id(0),
              total_nb_particles(inTotalNbParticles),
              dset_id_state(0),
              dset_id_rhs(0),
              use_collective_io(in_use_collective_io){}

    int open_file(std::string filename){
        if(Parent::isInvolved()){
            TIMEZONE("particles_output_hdf5::open_file");

            this->require_checkpoint_groups(filename);

            hid_t plist_id_par = H5Pcreate(H5P_FILE_ACCESS);
            assert(plist_id_par >= 0);
            int retTest = H5Pset_fapl_mpio(
                    plist_id_par,
                    Parent::getComWriter(),
                    MPI_INFO_NULL);
            variable_used_only_in_assert(retTest);
            assert(retTest >= 0);

            // Parallel HDF5 write
            file_id = H5Fopen(
                    filename.c_str(),
                    H5F_ACC_RDWR | H5F_ACC_DEBUG,
                    plist_id_par);
            // file_id = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC | H5F_ACC_DEBUG/*H5F_ACC_EXCL*/, H5P_DEFAULT/*H5F_ACC_RDWR*/, plist_id_par);
            assert(file_id >= 0);
            H5Pclose(plist_id_par);

            dset_id_state = H5Gopen(
                    file_id,
                    (this->particle_species_name + std::string("/state")).c_str(),
                    H5P_DEFAULT);
            assert(dset_id_state >= 0);
            dset_id_rhs = H5Gopen(
                    file_id,
                    (this->particle_species_name + std::string("/rhs")).c_str(),
                    H5P_DEFAULT);
            assert(dset_id_rhs >= 0);
        }
        return EXIT_SUCCESS;
    }

    ~particles_output_hdf5(){}

    void update_particle_species_name(
            const std::string new_name)
    {
        this->particle_species_name.assign(new_name);
    }

    int close_file(void){
        if(Parent::isInvolved()){
            TIMEZONE("particles_output_hdf5::close_file");

            int rethdf = H5Gclose(dset_id_state);
            variable_used_only_in_assert(rethdf);
            assert(rethdf >= 0);

            rethdf = H5Gclose(dset_id_rhs);
            assert(rethdf >= 0);

            rethdf = H5Fclose(file_id);
            assert(rethdf >= 0);
        }
        return EXIT_SUCCESS;
    }

    void require_checkpoint_groups(std::string filename){
        if(Parent::isInvolved()){
            if (Parent::getMyRank() == 0)
            {
                bool file_exists = false;
                {
                    struct stat file_buffer;
                    file_exists = (stat(filename.c_str(), &file_buffer) == 0);
                }
                hid_t file_id;
                if (file_exists)
                    file_id = H5Fopen(
                        filename.c_str(),
                        H5F_ACC_RDWR | H5F_ACC_DEBUG,
                        H5P_DEFAULT);
                else
                    file_id = H5Fcreate(
                        filename.c_str(),
                        H5F_ACC_EXCL | H5F_ACC_DEBUG,
                        H5P_DEFAULT,
                        H5P_DEFAULT);
                assert(file_id >= 0);
                bool group_exists = H5Lexists(
                        file_id,
                        this->particle_species_name.c_str(),
                        H5P_DEFAULT);
                if (!group_exists)
                {
                    hid_t gg = H5Gcreate(
                        file_id,
                        this->particle_species_name.c_str(),
                        H5P_DEFAULT,
                        H5P_DEFAULT,
                        H5P_DEFAULT);
                    assert(gg >= 0);
                    H5Gclose(gg);
                }
                hid_t gg = H5Gopen(
                        file_id,
                        this->particle_species_name.c_str(),
                        H5P_DEFAULT);
                assert(gg >= 0);
                group_exists = H5Lexists(
                        gg,
                        "state",
                        H5P_DEFAULT);
                if (!group_exists)
                {
                    hid_t ggg = H5Gcreate(
                        gg,
                        "state",
                        H5P_DEFAULT,
                        H5P_DEFAULT,
                        H5P_DEFAULT);
                    assert(ggg >= 0);
                    H5Gclose(ggg);
                }
                group_exists = H5Lexists(
                        gg,
                        "rhs",
                        H5P_DEFAULT);
                if (!group_exists)
                {
                    hid_t ggg = H5Gcreate(
                        gg,
                        "rhs",
                        H5P_DEFAULT,
                        H5P_DEFAULT,
                        H5P_DEFAULT);
                    assert(ggg >= 0);
                    H5Gclose(ggg);
                }
                H5Gclose(gg);
                H5Fclose(file_id);
            }
            MPI_Barrier(Parent::getComWriter());
        }
    }

    void write(
            const int idx_time_step,
            const real_number* particles_positions,
            const std::unique_ptr<real_number[]>* particles_rhs,
            const partsize_t nb_particles,
            const partsize_t particles_idx_offset,
            const int size_particle_rhs) final{
        assert(Parent::isInvolved());

        TIMEZONE("particles_output_hdf5::write");

        assert(particles_idx_offset < Parent::getTotalNbParticles() || (particles_idx_offset == Parent::getTotalNbParticles() && nb_particles == 0));
        assert(particles_idx_offset+nb_particles <= Parent::getTotalNbParticles());

        static_assert(std::is_same<real_number, double>::value ||
                      std::is_same<real_number, float>::value,
                      "real_number must be double or float");
        const hid_t type_id = (sizeof(real_number) == 8 ? H5T_NATIVE_DOUBLE : H5T_NATIVE_FLOAT);

        hid_t plist_id = H5Pcreate(H5P_DATASET_XFER);
        assert(plist_id >= 0);
        {
            int rethdf = H5Pset_dxpl_mpio(plist_id, use_collective_io ? H5FD_MPIO_COLLECTIVE : H5FD_MPIO_INDEPENDENT);
            variable_used_only_in_assert(rethdf);
            assert(rethdf >= 0);
        }

        {
            std::vector<hsize_t> datacount = std::vector<hsize_t>(this->particle_file_layout);
            datacount.push_back(size_particle_positions);
            hid_t dataspace = H5Screate_simple(datacount.size(), &datacount.front(), NULL);
            assert(dataspace >= 0);

            hid_t dataset_id = H5Dcreate( dset_id_state,
                                          std::to_string(idx_time_step).c_str(),
                                          type_id,
                                          dataspace,
                                          H5P_DEFAULT,
                                          H5P_DEFAULT,
                                          H5P_DEFAULT);
            assert(dataset_id >= 0);

            assert(nb_particles >= 0);
            assert(particles_idx_offset >= 0);
            const hsize_t count[2] = {hsize_t(nb_particles), size_particle_positions};
            const hsize_t offset[2] = {hsize_t(particles_idx_offset), 0};
            hid_t memspace = H5Screate_simple(2, count, NULL);
            assert(memspace >= 0);

            assert(total_nb_particles >= 0);
            assert(size_particle_positions >= 0);
            const hsize_t file_count[2] = {hsize_t(total_nb_particles), size_particle_positions};
            hid_t filespace = H5Screate_simple(2, file_count, NULL);
            assert(filespace >= 0);

            int rethdf = H5Sselect_hyperslab(
                    filespace,
                    H5S_SELECT_SET,
                    offset,
                    NULL,
                    count,
                    NULL);
            variable_used_only_in_assert(rethdf);
            assert(rethdf >= 0);

            herr_t	status = H5Dwrite(
                    dataset_id,
                    type_id,
                    memspace,
                    filespace,
                    plist_id,
                    particles_positions);
            variable_used_only_in_assert(status);
            assert(status >= 0);
            rethdf = H5Sclose(memspace);
            assert(rethdf >= 0);
            rethdf = H5Dclose(dataset_id);
            assert(rethdf >= 0);
            rethdf = H5Sclose(filespace);
            assert(rethdf >= 0);
        }
        {
            assert(size_particle_rhs >= 0);
            std::vector<hsize_t> datacount = std::vector<hsize_t>(this->particle_file_layout);
            datacount.insert(datacount.begin(), hsize_t(Parent::getNbRhs()));
            datacount.push_back(size_particle_rhs);
            hid_t dataspace = H5Screate_simple(datacount.size(), &datacount.front(), NULL);
            assert(dataspace >= 0);

            hid_t dataset_id = H5Dcreate( dset_id_rhs,
                                          std::to_string(idx_time_step).c_str(),
                                          type_id,
                                          dataspace,
                                          H5P_DEFAULT,
                                          H5P_DEFAULT,
                                          H5P_DEFAULT);
            assert(dataset_id >= 0);

            assert(particles_idx_offset >= 0);
            for(int idx_rhs = 0 ; idx_rhs < Parent::getNbRhs() ; ++idx_rhs){
                const hsize_t count[3] = {
                    1,
                    hsize_t(nb_particles),
                    hsize_t(size_particle_rhs)};
                const hsize_t offset[3] = {
                    hsize_t(idx_rhs),
                    hsize_t(particles_idx_offset),
                    0};
                hid_t memspace = H5Screate_simple(3, count, NULL);
                assert(memspace >= 0);

                assert(total_nb_particles >= 0);
                assert(size_particle_positions >= 0);
                const hsize_t file_count[3] = {hsize_t(Parent::getNbRhs()), hsize_t(total_nb_particles), size_particle_positions};
                hid_t filespace = H5Screate_simple(3, file_count, NULL);
                assert(filespace >= 0);

                int rethdf = H5Sselect_hyperslab(
                        filespace,
                        H5S_SELECT_SET,
                        offset,
                        NULL,
                        count,
                        NULL);
                variable_used_only_in_assert(rethdf);
                assert(rethdf >= 0);

                herr_t	status = H5Dwrite(
                        dataset_id,
                        type_id,
                        memspace,
                        filespace,
                        plist_id,
                        particles_rhs[idx_rhs].get());
                variable_used_only_in_assert(status);
                assert(status >= 0);
                rethdf = H5Sclose(filespace);
                assert(rethdf >= 0);
                rethdf = H5Sclose(memspace);
                assert(rethdf >= 0);
            }
            int rethdf = H5Dclose(dataset_id);
            variable_used_only_in_assert(rethdf);
            assert(rethdf >= 0);
        }

        {
            int rethdf = H5Pclose(plist_id);
            variable_used_only_in_assert(rethdf);
            assert(rethdf >= 0);
        }
    }

    int setParticleFileLayout(std::vector<hsize_t> input_layout){
        this->particle_file_layout.resize(input_layout.size());
        for (unsigned int i=0; i<this->particle_file_layout.size(); i++)
            this->particle_file_layout[i] = input_layout[i];
        return EXIT_SUCCESS;
    }

    std::vector<hsize_t> getParticleFileLayout(void){
        return std::vector<hsize_t>(this->particle_file_layout);
    }
};

#endif//PARTICLES_OUTPUT_HDF5_HPP