Abstract base class for writing simulation results to various formats.
Abstract base class for writing simulation results to various formatsResultsWriter provides a unified interface for outputting simulation data to different file formats (binary, VTK, HDF5, etc.). It handles parallel I/O via MPI, allowing each rank to write its local subdomain data to a collective output file.
using namespace pfc;
auto world = world::create({256, 256, 256}, {1.0, 1.0, 1.0});
auto writer = std::make_unique<BinaryWriter>(\
"results_%04d.bin\");
// Configure writer
auto global_size = world::get_size(world);
auto local_size = world::get_size(local_world);
auto local_offset = compute_offset(local_world); // Relative to global origin
writer->set_domain(global_size, local_size, local_offset);
// Write field at step 0
writer->write(0, field); // Creates results_0000.bin
#ifndef PFC_RESULTS_WRITER_HPP
#define PFC_RESULTS_WRITER_HPP
#include "types.hpp"
#include <array>
#include <iostream>
#include <mpi.h>
#include <vector>
namespace pfc {
class ResultsWriter {
public:
ResultsWriter(const std::string &filename) { m_filename = filename; }
virtual ~ResultsWriter() = default;
virtual void set_domain(const std::array<int, 3> &arr_global,
const std::array<int, 3> &arr_local,
const std::array<int, 3> &arr_offset) = 0;
virtual MPI_Status write(int increment, const RealField &data) = 0;
virtual MPI_Status write(int increment, const ComplexField &data) = 0;
template <typename T> MPI_Status write(const std::vector<T> &data) {
return write(0, data);
}
protected:
std::string m_filename;
};
class BinaryWriter : public ResultsWriter {
using ResultsWriter::ResultsWriter;
private:
MPI_Datatype m_filetype;
static MPI_Datatype get_type(RealField) { return MPI_DOUBLE; }
static MPI_Datatype get_type(ComplexField) { return MPI_DOUBLE_COMPLEX; }
public:
void set_domain(const std::array<int, 3> &arr_global,
const std::array<int, 3> &arr_local,
const std::array<int, 3> &arr_offset) {
MPI_Type_create_subarray(3, arr_global.data(), arr_local.data(),
arr_offset.data(), MPI_ORDER_FORTRAN, MPI_DOUBLE,
&m_filetype);
MPI_Type_commit(&m_filetype);
};
MPI_Status write(int increment, const RealField &data) {
return write_(increment, data);
}
MPI_Status write(int increment, const ComplexField &data) {
return write_(increment, data);
}
template <typename T>
MPI_Status write_(int increment, const std::vector<T> &data) {
MPI_File fh;
std::string filename2 = utils::format_with_number(m_filename, increment);
MPI_File_open(MPI_COMM_WORLD, filename2.c_str(),
MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);
MPI_Offset filesize = 0;
MPI_Status status;
const unsigned int disp = 0;
MPI_Datatype type = get_type(data);
MPI_File_set_size(fh, filesize);
MPI_File_set_view(fh, disp, type, m_filetype, "native", MPI_INFO_NULL);
MPI_File_write_all(fh, data.data(), data.size(), type, &status);
MPI_File_close(&fh);
return status;
}
};
}
#endif
General utility functions.